1 | #include <machine/rtems-bsd-kernel-space.h> |
---|
2 | |
---|
3 | /*- |
---|
4 | * SPDX-License-Identifier: BSD-2-Clause-FreeBSD |
---|
5 | * |
---|
6 | * Copyright (c) 2012 Damjan Marion <dmarion@Freebsd.org> |
---|
7 | * Copyright (c) 2016 Rubicon Communications, LLC (Netgate) |
---|
8 | * All rights reserved. |
---|
9 | * |
---|
10 | * Redistribution and use in source and binary forms, with or without |
---|
11 | * modification, are permitted provided that the following conditions |
---|
12 | * are met: |
---|
13 | * 1. Redistributions of source code must retain the above copyright |
---|
14 | * notice, this list of conditions and the following disclaimer. |
---|
15 | * 2. Redistributions in binary form must reproduce the above copyright |
---|
16 | * notice, this list of conditions and the following disclaimer in the |
---|
17 | * documentation and/or other materials provided with the distribution. |
---|
18 | * |
---|
19 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND |
---|
20 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
---|
21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
---|
22 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE |
---|
23 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
---|
24 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
---|
25 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
---|
26 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
---|
27 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
---|
28 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
---|
29 | * SUCH DAMAGE. |
---|
30 | */ |
---|
31 | |
---|
32 | /* |
---|
33 | * TI Common Platform Ethernet Switch (CPSW) Driver |
---|
34 | * Found in TI8148 "DaVinci" and AM335x "Sitara" SoCs. |
---|
35 | * |
---|
36 | * This controller is documented in the AM335x Technical Reference |
---|
37 | * Manual, in the TMS320DM814x DaVinci Digital Video Processors TRM |
---|
38 | * and in the TMS320C6452 3 Port Switch Ethernet Subsystem TRM. |
---|
39 | * |
---|
40 | * It is basically a single Ethernet port (port 0) wired internally to |
---|
41 | * a 3-port store-and-forward switch connected to two independent |
---|
42 | * "sliver" controllers (port 1 and port 2). You can operate the |
---|
43 | * controller in a variety of different ways by suitably configuring |
---|
44 | * the slivers and the Address Lookup Engine (ALE) that routes packets |
---|
45 | * between the ports. |
---|
46 | * |
---|
47 | * This code was developed and tested on a BeagleBone with |
---|
48 | * an AM335x SoC. |
---|
49 | */ |
---|
50 | |
---|
51 | #include <sys/cdefs.h> |
---|
52 | __FBSDID("$FreeBSD$"); |
---|
53 | |
---|
54 | #include <rtems/bsd/local/opt_cpsw.h> |
---|
55 | |
---|
56 | #include <sys/param.h> |
---|
57 | #include <sys/bus.h> |
---|
58 | #include <sys/kernel.h> |
---|
59 | #include <sys/lock.h> |
---|
60 | #include <sys/mbuf.h> |
---|
61 | #include <sys/module.h> |
---|
62 | #include <sys/mutex.h> |
---|
63 | #include <sys/rman.h> |
---|
64 | #include <sys/socket.h> |
---|
65 | #include <sys/sockio.h> |
---|
66 | #include <sys/sysctl.h> |
---|
67 | |
---|
68 | #include <machine/bus.h> |
---|
69 | #include <machine/resource.h> |
---|
70 | #include <machine/stdarg.h> |
---|
71 | |
---|
72 | #include <net/ethernet.h> |
---|
73 | #include <net/bpf.h> |
---|
74 | #include <net/if.h> |
---|
75 | #include <net/if_dl.h> |
---|
76 | #include <net/if_media.h> |
---|
77 | #include <net/if_types.h> |
---|
78 | |
---|
79 | #include <arm/ti/ti_scm.h> |
---|
80 | #include <arm/ti/am335x/am335x_scm.h> |
---|
81 | |
---|
82 | #include <dev/mii/mii.h> |
---|
83 | #include <dev/mii/miivar.h> |
---|
84 | |
---|
85 | #include <dev/ofw/ofw_bus.h> |
---|
86 | #include <dev/ofw/ofw_bus_subr.h> |
---|
87 | |
---|
88 | #ifdef CPSW_ETHERSWITCH |
---|
89 | #include <dev/etherswitch/etherswitch.h> |
---|
90 | #include <rtems/bsd/local/etherswitch_if.h> |
---|
91 | #endif |
---|
92 | |
---|
93 | #include "if_cpswreg.h" |
---|
94 | #include "if_cpswvar.h" |
---|
95 | |
---|
96 | #include <rtems/bsd/local/miibus_if.h> |
---|
97 | |
---|
98 | /* Device probe/attach/detach. */ |
---|
99 | static int cpsw_probe(device_t); |
---|
100 | static int cpsw_attach(device_t); |
---|
101 | static int cpsw_detach(device_t); |
---|
102 | static int cpswp_probe(device_t); |
---|
103 | static int cpswp_attach(device_t); |
---|
104 | static int cpswp_detach(device_t); |
---|
105 | |
---|
106 | static phandle_t cpsw_get_node(device_t, device_t); |
---|
107 | |
---|
108 | /* Device Init/shutdown. */ |
---|
109 | static int cpsw_shutdown(device_t); |
---|
110 | static void cpswp_init(void *); |
---|
111 | static void cpswp_init_locked(void *); |
---|
112 | static void cpswp_stop_locked(struct cpswp_softc *); |
---|
113 | |
---|
114 | /* Device Suspend/Resume. */ |
---|
115 | static int cpsw_suspend(device_t); |
---|
116 | static int cpsw_resume(device_t); |
---|
117 | |
---|
118 | /* Ioctl. */ |
---|
119 | static int cpswp_ioctl(struct ifnet *, u_long command, caddr_t data); |
---|
120 | |
---|
121 | static int cpswp_miibus_readreg(device_t, int phy, int reg); |
---|
122 | static int cpswp_miibus_writereg(device_t, int phy, int reg, int value); |
---|
123 | static void cpswp_miibus_statchg(device_t); |
---|
124 | |
---|
125 | /* Send/Receive packets. */ |
---|
126 | static void cpsw_intr_rx(void *arg); |
---|
127 | static struct mbuf *cpsw_rx_dequeue(struct cpsw_softc *); |
---|
128 | static void cpsw_rx_enqueue(struct cpsw_softc *); |
---|
129 | static void cpswp_start(struct ifnet *); |
---|
130 | static void cpsw_intr_tx(void *); |
---|
131 | static void cpswp_tx_enqueue(struct cpswp_softc *); |
---|
132 | static int cpsw_tx_dequeue(struct cpsw_softc *); |
---|
133 | |
---|
134 | /* Misc interrupts and watchdog. */ |
---|
135 | static void cpsw_intr_rx_thresh(void *); |
---|
136 | static void cpsw_intr_misc(void *); |
---|
137 | static void cpswp_tick(void *); |
---|
138 | static void cpswp_ifmedia_sts(struct ifnet *, struct ifmediareq *); |
---|
139 | static int cpswp_ifmedia_upd(struct ifnet *); |
---|
140 | static void cpsw_tx_watchdog(void *); |
---|
141 | |
---|
142 | /* ALE support */ |
---|
143 | static void cpsw_ale_read_entry(struct cpsw_softc *, uint16_t, uint32_t *); |
---|
144 | static void cpsw_ale_write_entry(struct cpsw_softc *, uint16_t, uint32_t *); |
---|
145 | static int cpsw_ale_mc_entry_set(struct cpsw_softc *, uint8_t, int, uint8_t *); |
---|
146 | static void cpsw_ale_dump_table(struct cpsw_softc *); |
---|
147 | static int cpsw_ale_update_vlan_table(struct cpsw_softc *, int, int, int, int, |
---|
148 | int); |
---|
149 | static int cpswp_ale_update_addresses(struct cpswp_softc *, int); |
---|
150 | |
---|
151 | /* Statistics and sysctls. */ |
---|
152 | static void cpsw_add_sysctls(struct cpsw_softc *); |
---|
153 | static void cpsw_stats_collect(struct cpsw_softc *); |
---|
154 | static int cpsw_stats_sysctl(SYSCTL_HANDLER_ARGS); |
---|
155 | |
---|
156 | #ifdef CPSW_ETHERSWITCH |
---|
157 | static etherswitch_info_t *cpsw_getinfo(device_t); |
---|
158 | static int cpsw_getport(device_t, etherswitch_port_t *); |
---|
159 | static int cpsw_setport(device_t, etherswitch_port_t *); |
---|
160 | static int cpsw_getconf(device_t, etherswitch_conf_t *); |
---|
161 | static int cpsw_getvgroup(device_t, etherswitch_vlangroup_t *); |
---|
162 | static int cpsw_setvgroup(device_t, etherswitch_vlangroup_t *); |
---|
163 | static int cpsw_readreg(device_t, int); |
---|
164 | static int cpsw_writereg(device_t, int, int); |
---|
165 | static int cpsw_readphy(device_t, int, int); |
---|
166 | static int cpsw_writephy(device_t, int, int, int); |
---|
167 | #endif |
---|
168 | |
---|
169 | /* |
---|
170 | * Arbitrary limit on number of segments in an mbuf to be transmitted. |
---|
171 | * Packets with more segments than this will be defragmented before |
---|
172 | * they are queued. |
---|
173 | */ |
---|
174 | #define CPSW_TXFRAGS 16 |
---|
175 | |
---|
176 | /* Shared resources. */ |
---|
177 | static device_method_t cpsw_methods[] = { |
---|
178 | /* Device interface */ |
---|
179 | DEVMETHOD(device_probe, cpsw_probe), |
---|
180 | DEVMETHOD(device_attach, cpsw_attach), |
---|
181 | DEVMETHOD(device_detach, cpsw_detach), |
---|
182 | DEVMETHOD(device_shutdown, cpsw_shutdown), |
---|
183 | DEVMETHOD(device_suspend, cpsw_suspend), |
---|
184 | DEVMETHOD(device_resume, cpsw_resume), |
---|
185 | /* Bus interface */ |
---|
186 | DEVMETHOD(bus_add_child, device_add_child_ordered), |
---|
187 | /* OFW methods */ |
---|
188 | DEVMETHOD(ofw_bus_get_node, cpsw_get_node), |
---|
189 | #ifdef CPSW_ETHERSWITCH |
---|
190 | /* etherswitch interface */ |
---|
191 | DEVMETHOD(etherswitch_getinfo, cpsw_getinfo), |
---|
192 | DEVMETHOD(etherswitch_readreg, cpsw_readreg), |
---|
193 | DEVMETHOD(etherswitch_writereg, cpsw_writereg), |
---|
194 | DEVMETHOD(etherswitch_readphyreg, cpsw_readphy), |
---|
195 | DEVMETHOD(etherswitch_writephyreg, cpsw_writephy), |
---|
196 | DEVMETHOD(etherswitch_getport, cpsw_getport), |
---|
197 | DEVMETHOD(etherswitch_setport, cpsw_setport), |
---|
198 | DEVMETHOD(etherswitch_getvgroup, cpsw_getvgroup), |
---|
199 | DEVMETHOD(etherswitch_setvgroup, cpsw_setvgroup), |
---|
200 | DEVMETHOD(etherswitch_getconf, cpsw_getconf), |
---|
201 | #endif |
---|
202 | DEVMETHOD_END |
---|
203 | }; |
---|
204 | |
---|
205 | static driver_t cpsw_driver = { |
---|
206 | "cpswss", |
---|
207 | cpsw_methods, |
---|
208 | sizeof(struct cpsw_softc), |
---|
209 | }; |
---|
210 | |
---|
211 | static devclass_t cpsw_devclass; |
---|
212 | |
---|
213 | DRIVER_MODULE(cpswss, simplebus, cpsw_driver, cpsw_devclass, 0, 0); |
---|
214 | |
---|
215 | /* Port/Slave resources. */ |
---|
216 | static device_method_t cpswp_methods[] = { |
---|
217 | /* Device interface */ |
---|
218 | DEVMETHOD(device_probe, cpswp_probe), |
---|
219 | DEVMETHOD(device_attach, cpswp_attach), |
---|
220 | DEVMETHOD(device_detach, cpswp_detach), |
---|
221 | /* MII interface */ |
---|
222 | DEVMETHOD(miibus_readreg, cpswp_miibus_readreg), |
---|
223 | DEVMETHOD(miibus_writereg, cpswp_miibus_writereg), |
---|
224 | DEVMETHOD(miibus_statchg, cpswp_miibus_statchg), |
---|
225 | DEVMETHOD_END |
---|
226 | }; |
---|
227 | |
---|
228 | static driver_t cpswp_driver = { |
---|
229 | "cpsw", |
---|
230 | cpswp_methods, |
---|
231 | sizeof(struct cpswp_softc), |
---|
232 | }; |
---|
233 | |
---|
234 | static devclass_t cpswp_devclass; |
---|
235 | |
---|
236 | #ifdef CPSW_ETHERSWITCH |
---|
237 | DRIVER_MODULE(etherswitch, cpswss, etherswitch_driver, etherswitch_devclass, 0, 0); |
---|
238 | MODULE_DEPEND(cpswss, etherswitch, 1, 1, 1); |
---|
239 | #endif |
---|
240 | |
---|
241 | DRIVER_MODULE(cpsw, cpswss, cpswp_driver, cpswp_devclass, 0, 0); |
---|
242 | DRIVER_MODULE(miibus, cpsw, miibus_driver, miibus_devclass, 0, 0); |
---|
243 | MODULE_DEPEND(cpsw, ether, 1, 1, 1); |
---|
244 | MODULE_DEPEND(cpsw, miibus, 1, 1, 1); |
---|
245 | |
---|
246 | #ifdef CPSW_ETHERSWITCH |
---|
247 | static struct cpsw_vlangroups cpsw_vgroups[CPSW_VLANS]; |
---|
248 | #endif |
---|
249 | |
---|
250 | static uint32_t slave_mdio_addr[] = { 0x4a100200, 0x4a100300 }; |
---|
251 | |
---|
252 | static struct resource_spec irq_res_spec[] = { |
---|
253 | { SYS_RES_IRQ, 0, RF_ACTIVE | RF_SHAREABLE }, |
---|
254 | { SYS_RES_IRQ, 1, RF_ACTIVE | RF_SHAREABLE }, |
---|
255 | { SYS_RES_IRQ, 2, RF_ACTIVE | RF_SHAREABLE }, |
---|
256 | { SYS_RES_IRQ, 3, RF_ACTIVE | RF_SHAREABLE }, |
---|
257 | { -1, 0 } |
---|
258 | }; |
---|
259 | |
---|
260 | static struct { |
---|
261 | void (*cb)(void *); |
---|
262 | } cpsw_intr_cb[] = { |
---|
263 | { cpsw_intr_rx_thresh }, |
---|
264 | { cpsw_intr_rx }, |
---|
265 | { cpsw_intr_tx }, |
---|
266 | { cpsw_intr_misc }, |
---|
267 | }; |
---|
268 | |
---|
269 | /* Number of entries here must match size of stats |
---|
270 | * array in struct cpswp_softc. */ |
---|
271 | static struct cpsw_stat { |
---|
272 | int reg; |
---|
273 | char *oid; |
---|
274 | } cpsw_stat_sysctls[CPSW_SYSCTL_COUNT] = { |
---|
275 | {0x00, "GoodRxFrames"}, |
---|
276 | {0x04, "BroadcastRxFrames"}, |
---|
277 | {0x08, "MulticastRxFrames"}, |
---|
278 | {0x0C, "PauseRxFrames"}, |
---|
279 | {0x10, "RxCrcErrors"}, |
---|
280 | {0x14, "RxAlignErrors"}, |
---|
281 | {0x18, "OversizeRxFrames"}, |
---|
282 | {0x1c, "RxJabbers"}, |
---|
283 | {0x20, "ShortRxFrames"}, |
---|
284 | {0x24, "RxFragments"}, |
---|
285 | {0x30, "RxOctets"}, |
---|
286 | {0x34, "GoodTxFrames"}, |
---|
287 | {0x38, "BroadcastTxFrames"}, |
---|
288 | {0x3c, "MulticastTxFrames"}, |
---|
289 | {0x40, "PauseTxFrames"}, |
---|
290 | {0x44, "DeferredTxFrames"}, |
---|
291 | {0x48, "CollisionsTxFrames"}, |
---|
292 | {0x4c, "SingleCollisionTxFrames"}, |
---|
293 | {0x50, "MultipleCollisionTxFrames"}, |
---|
294 | {0x54, "ExcessiveCollisions"}, |
---|
295 | {0x58, "LateCollisions"}, |
---|
296 | {0x5c, "TxUnderrun"}, |
---|
297 | {0x60, "CarrierSenseErrors"}, |
---|
298 | {0x64, "TxOctets"}, |
---|
299 | {0x68, "RxTx64OctetFrames"}, |
---|
300 | {0x6c, "RxTx65to127OctetFrames"}, |
---|
301 | {0x70, "RxTx128to255OctetFrames"}, |
---|
302 | {0x74, "RxTx256to511OctetFrames"}, |
---|
303 | {0x78, "RxTx512to1024OctetFrames"}, |
---|
304 | {0x7c, "RxTx1024upOctetFrames"}, |
---|
305 | {0x80, "NetOctets"}, |
---|
306 | {0x84, "RxStartOfFrameOverruns"}, |
---|
307 | {0x88, "RxMiddleOfFrameOverruns"}, |
---|
308 | {0x8c, "RxDmaOverruns"} |
---|
309 | }; |
---|
310 | |
---|
311 | /* |
---|
312 | * Basic debug support. |
---|
313 | */ |
---|
314 | |
---|
315 | static void |
---|
316 | cpsw_debugf_head(const char *funcname) |
---|
317 | { |
---|
318 | int t = (int)(time_second % (24 * 60 * 60)); |
---|
319 | |
---|
320 | printf("%02d:%02d:%02d %s ", t / (60 * 60), (t / 60) % 60, t % 60, funcname); |
---|
321 | } |
---|
322 | |
---|
323 | static void |
---|
324 | cpsw_debugf(const char *fmt, ...) |
---|
325 | { |
---|
326 | va_list ap; |
---|
327 | |
---|
328 | va_start(ap, fmt); |
---|
329 | vprintf(fmt, ap); |
---|
330 | va_end(ap); |
---|
331 | printf("\n"); |
---|
332 | |
---|
333 | } |
---|
334 | |
---|
335 | #define CPSW_DEBUGF(_sc, a) do { \ |
---|
336 | if ((_sc)->debug) { \ |
---|
337 | cpsw_debugf_head(__func__); \ |
---|
338 | cpsw_debugf a; \ |
---|
339 | } \ |
---|
340 | } while (0) |
---|
341 | |
---|
342 | /* |
---|
343 | * Locking macros |
---|
344 | */ |
---|
345 | #define CPSW_TX_LOCK(sc) do { \ |
---|
346 | mtx_assert(&(sc)->rx.lock, MA_NOTOWNED); \ |
---|
347 | mtx_lock(&(sc)->tx.lock); \ |
---|
348 | } while (0) |
---|
349 | |
---|
350 | #define CPSW_TX_UNLOCK(sc) mtx_unlock(&(sc)->tx.lock) |
---|
351 | #define CPSW_TX_LOCK_ASSERT(sc) mtx_assert(&(sc)->tx.lock, MA_OWNED) |
---|
352 | |
---|
353 | #define CPSW_RX_LOCK(sc) do { \ |
---|
354 | mtx_assert(&(sc)->tx.lock, MA_NOTOWNED); \ |
---|
355 | mtx_lock(&(sc)->rx.lock); \ |
---|
356 | } while (0) |
---|
357 | |
---|
358 | #define CPSW_RX_UNLOCK(sc) mtx_unlock(&(sc)->rx.lock) |
---|
359 | #define CPSW_RX_LOCK_ASSERT(sc) mtx_assert(&(sc)->rx.lock, MA_OWNED) |
---|
360 | |
---|
361 | #define CPSW_PORT_LOCK(_sc) do { \ |
---|
362 | mtx_assert(&(_sc)->lock, MA_NOTOWNED); \ |
---|
363 | mtx_lock(&(_sc)->lock); \ |
---|
364 | } while (0) |
---|
365 | |
---|
366 | #define CPSW_PORT_UNLOCK(_sc) mtx_unlock(&(_sc)->lock) |
---|
367 | #define CPSW_PORT_LOCK_ASSERT(_sc) mtx_assert(&(_sc)->lock, MA_OWNED) |
---|
368 | |
---|
369 | /* |
---|
370 | * Read/Write macros |
---|
371 | */ |
---|
372 | #define cpsw_read_4(_sc, _reg) bus_read_4((_sc)->mem_res, (_reg)) |
---|
373 | #define cpsw_write_4(_sc, _reg, _val) \ |
---|
374 | bus_write_4((_sc)->mem_res, (_reg), (_val)) |
---|
375 | |
---|
376 | #define cpsw_cpdma_bd_offset(i) (CPSW_CPPI_RAM_OFFSET + ((i)*16)) |
---|
377 | |
---|
378 | #define cpsw_cpdma_bd_paddr(sc, slot) \ |
---|
379 | BUS_SPACE_PHYSADDR(sc->mem_res, slot->bd_offset) |
---|
380 | #define cpsw_cpdma_read_bd(sc, slot, val) \ |
---|
381 | bus_read_region_4(sc->mem_res, slot->bd_offset, (uint32_t *) val, 4) |
---|
382 | #define cpsw_cpdma_write_bd(sc, slot, val) \ |
---|
383 | bus_write_region_4(sc->mem_res, slot->bd_offset, (uint32_t *) val, 4) |
---|
384 | #define cpsw_cpdma_write_bd_next(sc, slot, next_slot) \ |
---|
385 | cpsw_write_4(sc, slot->bd_offset, cpsw_cpdma_bd_paddr(sc, next_slot)) |
---|
386 | #define cpsw_cpdma_write_bd_flags(sc, slot, val) \ |
---|
387 | bus_write_2(sc->mem_res, slot->bd_offset + 14, val) |
---|
388 | #define cpsw_cpdma_read_bd_flags(sc, slot) \ |
---|
389 | bus_read_2(sc->mem_res, slot->bd_offset + 14) |
---|
390 | #define cpsw_write_hdp_slot(sc, queue, slot) \ |
---|
391 | cpsw_write_4(sc, (queue)->hdp_offset, cpsw_cpdma_bd_paddr(sc, slot)) |
---|
392 | #define CP_OFFSET (CPSW_CPDMA_TX_CP(0) - CPSW_CPDMA_TX_HDP(0)) |
---|
393 | #define cpsw_read_cp(sc, queue) \ |
---|
394 | cpsw_read_4(sc, (queue)->hdp_offset + CP_OFFSET) |
---|
395 | #define cpsw_write_cp(sc, queue, val) \ |
---|
396 | cpsw_write_4(sc, (queue)->hdp_offset + CP_OFFSET, (val)) |
---|
397 | #define cpsw_write_cp_slot(sc, queue, slot) \ |
---|
398 | cpsw_write_cp(sc, queue, cpsw_cpdma_bd_paddr(sc, slot)) |
---|
399 | |
---|
400 | #if 0 |
---|
401 | /* XXX temporary function versions for debugging. */ |
---|
402 | static void |
---|
403 | cpsw_write_hdp_slotX(struct cpsw_softc *sc, struct cpsw_queue *queue, struct cpsw_slot *slot) |
---|
404 | { |
---|
405 | uint32_t reg = queue->hdp_offset; |
---|
406 | uint32_t v = cpsw_cpdma_bd_paddr(sc, slot); |
---|
407 | CPSW_DEBUGF(("HDP <=== 0x%08x (was 0x%08x)", v, cpsw_read_4(sc, reg))); |
---|
408 | cpsw_write_4(sc, reg, v); |
---|
409 | } |
---|
410 | |
---|
411 | static void |
---|
412 | cpsw_write_cp_slotX(struct cpsw_softc *sc, struct cpsw_queue *queue, struct cpsw_slot *slot) |
---|
413 | { |
---|
414 | uint32_t v = cpsw_cpdma_bd_paddr(sc, slot); |
---|
415 | CPSW_DEBUGF(("CP <=== 0x%08x (expecting 0x%08x)", v, cpsw_read_cp(sc, queue))); |
---|
416 | cpsw_write_cp(sc, queue, v); |
---|
417 | } |
---|
418 | #endif |
---|
419 | |
---|
420 | /* |
---|
421 | * Expanded dump routines for verbose debugging. |
---|
422 | */ |
---|
423 | static void |
---|
424 | cpsw_dump_slot(struct cpsw_softc *sc, struct cpsw_slot *slot) |
---|
425 | { |
---|
426 | static const char *flags[] = {"SOP", "EOP", "Owner", "EOQ", |
---|
427 | "TDownCmplt", "PassCRC", "Long", "Short", "MacCtl", "Overrun", |
---|
428 | "PktErr1", "PortEn/PktErr0", "RxVlanEncap", "Port2", "Port1", |
---|
429 | "Port0"}; |
---|
430 | struct cpsw_cpdma_bd bd; |
---|
431 | const char *sep; |
---|
432 | int i; |
---|
433 | |
---|
434 | cpsw_cpdma_read_bd(sc, slot, &bd); |
---|
435 | printf("BD Addr : 0x%08x Next : 0x%08x\n", |
---|
436 | cpsw_cpdma_bd_paddr(sc, slot), bd.next); |
---|
437 | printf(" BufPtr: 0x%08x BufLen: 0x%08x\n", bd.bufptr, bd.buflen); |
---|
438 | printf(" BufOff: 0x%08x PktLen: 0x%08x\n", bd.bufoff, bd.pktlen); |
---|
439 | printf(" Flags: "); |
---|
440 | sep = ""; |
---|
441 | for (i = 0; i < 16; ++i) { |
---|
442 | if (bd.flags & (1 << (15 - i))) { |
---|
443 | printf("%s%s", sep, flags[i]); |
---|
444 | sep = ","; |
---|
445 | } |
---|
446 | } |
---|
447 | printf("\n"); |
---|
448 | if (slot->mbuf) { |
---|
449 | printf(" Ether: %14D\n", |
---|
450 | (char *)(slot->mbuf->m_data), " "); |
---|
451 | printf(" Packet: %16D\n", |
---|
452 | (char *)(slot->mbuf->m_data) + 14, " "); |
---|
453 | } |
---|
454 | } |
---|
455 | |
---|
456 | #define CPSW_DUMP_SLOT(cs, slot) do { \ |
---|
457 | IF_DEBUG(sc) { \ |
---|
458 | cpsw_dump_slot(sc, slot); \ |
---|
459 | } \ |
---|
460 | } while (0) |
---|
461 | |
---|
462 | static void |
---|
463 | cpsw_dump_queue(struct cpsw_softc *sc, struct cpsw_slots *q) |
---|
464 | { |
---|
465 | struct cpsw_slot *slot; |
---|
466 | int i = 0; |
---|
467 | int others = 0; |
---|
468 | |
---|
469 | STAILQ_FOREACH(slot, q, next) { |
---|
470 | if (i > CPSW_TXFRAGS) |
---|
471 | ++others; |
---|
472 | else |
---|
473 | cpsw_dump_slot(sc, slot); |
---|
474 | ++i; |
---|
475 | } |
---|
476 | if (others) |
---|
477 | printf(" ... and %d more.\n", others); |
---|
478 | printf("\n"); |
---|
479 | } |
---|
480 | |
---|
481 | #define CPSW_DUMP_QUEUE(sc, q) do { \ |
---|
482 | IF_DEBUG(sc) { \ |
---|
483 | cpsw_dump_queue(sc, q); \ |
---|
484 | } \ |
---|
485 | } while (0) |
---|
486 | |
---|
487 | static void |
---|
488 | cpsw_init_slots(struct cpsw_softc *sc) |
---|
489 | { |
---|
490 | struct cpsw_slot *slot; |
---|
491 | int i; |
---|
492 | |
---|
493 | STAILQ_INIT(&sc->avail); |
---|
494 | |
---|
495 | /* Put the slot descriptors onto the global avail list. */ |
---|
496 | for (i = 0; i < nitems(sc->_slots); i++) { |
---|
497 | slot = &sc->_slots[i]; |
---|
498 | slot->bd_offset = cpsw_cpdma_bd_offset(i); |
---|
499 | STAILQ_INSERT_TAIL(&sc->avail, slot, next); |
---|
500 | } |
---|
501 | } |
---|
502 | |
---|
503 | static int |
---|
504 | cpsw_add_slots(struct cpsw_softc *sc, struct cpsw_queue *queue, int requested) |
---|
505 | { |
---|
506 | const int max_slots = nitems(sc->_slots); |
---|
507 | struct cpsw_slot *slot; |
---|
508 | int i; |
---|
509 | |
---|
510 | if (requested < 0) |
---|
511 | requested = max_slots; |
---|
512 | |
---|
513 | for (i = 0; i < requested; ++i) { |
---|
514 | slot = STAILQ_FIRST(&sc->avail); |
---|
515 | if (slot == NULL) |
---|
516 | return (0); |
---|
517 | if (bus_dmamap_create(sc->mbuf_dtag, 0, &slot->dmamap)) { |
---|
518 | device_printf(sc->dev, "failed to create dmamap\n"); |
---|
519 | return (ENOMEM); |
---|
520 | } |
---|
521 | STAILQ_REMOVE_HEAD(&sc->avail, next); |
---|
522 | STAILQ_INSERT_TAIL(&queue->avail, slot, next); |
---|
523 | ++queue->avail_queue_len; |
---|
524 | ++queue->queue_slots; |
---|
525 | } |
---|
526 | return (0); |
---|
527 | } |
---|
528 | |
---|
529 | static void |
---|
530 | cpsw_free_slot(struct cpsw_softc *sc, struct cpsw_slot *slot) |
---|
531 | { |
---|
532 | int error; |
---|
533 | |
---|
534 | if (slot->dmamap) { |
---|
535 | if (slot->mbuf) |
---|
536 | bus_dmamap_unload(sc->mbuf_dtag, slot->dmamap); |
---|
537 | error = bus_dmamap_destroy(sc->mbuf_dtag, slot->dmamap); |
---|
538 | KASSERT(error == 0, ("Mapping still active")); |
---|
539 | slot->dmamap = NULL; |
---|
540 | } |
---|
541 | if (slot->mbuf) { |
---|
542 | m_freem(slot->mbuf); |
---|
543 | slot->mbuf = NULL; |
---|
544 | } |
---|
545 | } |
---|
546 | |
---|
547 | static void |
---|
548 | cpsw_reset(struct cpsw_softc *sc) |
---|
549 | { |
---|
550 | int i; |
---|
551 | |
---|
552 | callout_stop(&sc->watchdog.callout); |
---|
553 | |
---|
554 | /* Reset RMII/RGMII wrapper. */ |
---|
555 | cpsw_write_4(sc, CPSW_WR_SOFT_RESET, 1); |
---|
556 | while (cpsw_read_4(sc, CPSW_WR_SOFT_RESET) & 1) |
---|
557 | ; |
---|
558 | |
---|
559 | /* Disable TX and RX interrupts for all cores. */ |
---|
560 | for (i = 0; i < 3; ++i) { |
---|
561 | cpsw_write_4(sc, CPSW_WR_C_RX_THRESH_EN(i), 0x00); |
---|
562 | cpsw_write_4(sc, CPSW_WR_C_TX_EN(i), 0x00); |
---|
563 | cpsw_write_4(sc, CPSW_WR_C_RX_EN(i), 0x00); |
---|
564 | cpsw_write_4(sc, CPSW_WR_C_MISC_EN(i), 0x00); |
---|
565 | } |
---|
566 | |
---|
567 | /* Reset CPSW subsystem. */ |
---|
568 | cpsw_write_4(sc, CPSW_SS_SOFT_RESET, 1); |
---|
569 | while (cpsw_read_4(sc, CPSW_SS_SOFT_RESET) & 1) |
---|
570 | ; |
---|
571 | |
---|
572 | /* Reset Sliver port 1 and 2 */ |
---|
573 | for (i = 0; i < 2; i++) { |
---|
574 | /* Reset */ |
---|
575 | cpsw_write_4(sc, CPSW_SL_SOFT_RESET(i), 1); |
---|
576 | while (cpsw_read_4(sc, CPSW_SL_SOFT_RESET(i)) & 1) |
---|
577 | ; |
---|
578 | } |
---|
579 | |
---|
580 | /* Reset DMA controller. */ |
---|
581 | cpsw_write_4(sc, CPSW_CPDMA_SOFT_RESET, 1); |
---|
582 | while (cpsw_read_4(sc, CPSW_CPDMA_SOFT_RESET) & 1) |
---|
583 | ; |
---|
584 | |
---|
585 | /* Disable TX & RX DMA */ |
---|
586 | cpsw_write_4(sc, CPSW_CPDMA_TX_CONTROL, 0); |
---|
587 | cpsw_write_4(sc, CPSW_CPDMA_RX_CONTROL, 0); |
---|
588 | |
---|
589 | /* Clear all queues. */ |
---|
590 | for (i = 0; i < 8; i++) { |
---|
591 | cpsw_write_4(sc, CPSW_CPDMA_TX_HDP(i), 0); |
---|
592 | cpsw_write_4(sc, CPSW_CPDMA_RX_HDP(i), 0); |
---|
593 | cpsw_write_4(sc, CPSW_CPDMA_TX_CP(i), 0); |
---|
594 | cpsw_write_4(sc, CPSW_CPDMA_RX_CP(i), 0); |
---|
595 | } |
---|
596 | |
---|
597 | /* Clear all interrupt Masks */ |
---|
598 | cpsw_write_4(sc, CPSW_CPDMA_RX_INTMASK_CLEAR, 0xFFFFFFFF); |
---|
599 | cpsw_write_4(sc, CPSW_CPDMA_TX_INTMASK_CLEAR, 0xFFFFFFFF); |
---|
600 | } |
---|
601 | |
---|
602 | static void |
---|
603 | cpsw_init(struct cpsw_softc *sc) |
---|
604 | { |
---|
605 | struct cpsw_slot *slot; |
---|
606 | uint32_t reg; |
---|
607 | |
---|
608 | /* Disable the interrupt pacing. */ |
---|
609 | reg = cpsw_read_4(sc, CPSW_WR_INT_CONTROL); |
---|
610 | reg &= ~(CPSW_WR_INT_PACE_EN | CPSW_WR_INT_PRESCALE_MASK); |
---|
611 | cpsw_write_4(sc, CPSW_WR_INT_CONTROL, reg); |
---|
612 | |
---|
613 | /* Clear ALE */ |
---|
614 | cpsw_write_4(sc, CPSW_ALE_CONTROL, CPSW_ALE_CTL_CLEAR_TBL); |
---|
615 | |
---|
616 | /* Enable ALE */ |
---|
617 | reg = CPSW_ALE_CTL_ENABLE; |
---|
618 | if (sc->dualemac) |
---|
619 | reg |= CPSW_ALE_CTL_VLAN_AWARE; |
---|
620 | cpsw_write_4(sc, CPSW_ALE_CONTROL, reg); |
---|
621 | |
---|
622 | /* Set Host Port Mapping. */ |
---|
623 | cpsw_write_4(sc, CPSW_PORT_P0_CPDMA_TX_PRI_MAP, 0x76543210); |
---|
624 | cpsw_write_4(sc, CPSW_PORT_P0_CPDMA_RX_CH_MAP, 0); |
---|
625 | |
---|
626 | /* Initialize ALE: set host port to forwarding(3). */ |
---|
627 | cpsw_write_4(sc, CPSW_ALE_PORTCTL(0), |
---|
628 | ALE_PORTCTL_INGRESS | ALE_PORTCTL_FORWARD); |
---|
629 | |
---|
630 | cpsw_write_4(sc, CPSW_SS_PTYPE, 0); |
---|
631 | |
---|
632 | /* Enable statistics for ports 0, 1 and 2 */ |
---|
633 | cpsw_write_4(sc, CPSW_SS_STAT_PORT_EN, 7); |
---|
634 | |
---|
635 | /* Turn off flow control. */ |
---|
636 | cpsw_write_4(sc, CPSW_SS_FLOW_CONTROL, 0); |
---|
637 | |
---|
638 | /* Make IP hdr aligned with 4 */ |
---|
639 | cpsw_write_4(sc, CPSW_CPDMA_RX_BUFFER_OFFSET, 2); |
---|
640 | |
---|
641 | /* Initialize RX Buffer Descriptors */ |
---|
642 | cpsw_write_4(sc, CPSW_CPDMA_RX_PENDTHRESH(0), 0); |
---|
643 | cpsw_write_4(sc, CPSW_CPDMA_RX_FREEBUFFER(0), 0); |
---|
644 | |
---|
645 | /* Enable TX & RX DMA */ |
---|
646 | cpsw_write_4(sc, CPSW_CPDMA_TX_CONTROL, 1); |
---|
647 | cpsw_write_4(sc, CPSW_CPDMA_RX_CONTROL, 1); |
---|
648 | |
---|
649 | /* Enable Interrupts for core 0 */ |
---|
650 | cpsw_write_4(sc, CPSW_WR_C_RX_THRESH_EN(0), 0xFF); |
---|
651 | cpsw_write_4(sc, CPSW_WR_C_RX_EN(0), 0xFF); |
---|
652 | cpsw_write_4(sc, CPSW_WR_C_TX_EN(0), 0xFF); |
---|
653 | cpsw_write_4(sc, CPSW_WR_C_MISC_EN(0), 0x1F); |
---|
654 | |
---|
655 | /* Enable host Error Interrupt */ |
---|
656 | cpsw_write_4(sc, CPSW_CPDMA_DMA_INTMASK_SET, 3); |
---|
657 | |
---|
658 | /* Enable interrupts for RX and TX on Channel 0 */ |
---|
659 | cpsw_write_4(sc, CPSW_CPDMA_RX_INTMASK_SET, |
---|
660 | CPSW_CPDMA_RX_INT(0) | CPSW_CPDMA_RX_INT_THRESH(0)); |
---|
661 | cpsw_write_4(sc, CPSW_CPDMA_TX_INTMASK_SET, 1); |
---|
662 | |
---|
663 | /* Initialze MDIO - ENABLE, PREAMBLE=0, FAULTENB, CLKDIV=0xFF */ |
---|
664 | /* TODO Calculate MDCLK=CLK/(CLKDIV+1) */ |
---|
665 | cpsw_write_4(sc, MDIOCONTROL, MDIOCTL_ENABLE | MDIOCTL_FAULTENB | 0xff); |
---|
666 | |
---|
667 | /* Select MII in GMII_SEL, Internal Delay mode */ |
---|
668 | //ti_scm_reg_write_4(0x650, 0); |
---|
669 | |
---|
670 | /* Initialize active queues. */ |
---|
671 | slot = STAILQ_FIRST(&sc->tx.active); |
---|
672 | if (slot != NULL) |
---|
673 | cpsw_write_hdp_slot(sc, &sc->tx, slot); |
---|
674 | slot = STAILQ_FIRST(&sc->rx.active); |
---|
675 | if (slot != NULL) |
---|
676 | cpsw_write_hdp_slot(sc, &sc->rx, slot); |
---|
677 | cpsw_rx_enqueue(sc); |
---|
678 | cpsw_write_4(sc, CPSW_CPDMA_RX_FREEBUFFER(0), sc->rx.active_queue_len); |
---|
679 | cpsw_write_4(sc, CPSW_CPDMA_RX_PENDTHRESH(0), CPSW_TXFRAGS); |
---|
680 | |
---|
681 | /* Activate network interface. */ |
---|
682 | sc->rx.running = 1; |
---|
683 | sc->tx.running = 1; |
---|
684 | sc->watchdog.timer = 0; |
---|
685 | callout_init(&sc->watchdog.callout, 0); |
---|
686 | callout_reset(&sc->watchdog.callout, hz, cpsw_tx_watchdog, sc); |
---|
687 | } |
---|
688 | |
---|
689 | /* |
---|
690 | * |
---|
691 | * Device Probe, Attach, Detach. |
---|
692 | * |
---|
693 | */ |
---|
694 | |
---|
695 | static int |
---|
696 | cpsw_probe(device_t dev) |
---|
697 | { |
---|
698 | |
---|
699 | if (!ofw_bus_status_okay(dev)) |
---|
700 | return (ENXIO); |
---|
701 | |
---|
702 | if (!ofw_bus_is_compatible(dev, "ti,cpsw")) |
---|
703 | return (ENXIO); |
---|
704 | |
---|
705 | device_set_desc(dev, "3-port Switch Ethernet Subsystem"); |
---|
706 | return (BUS_PROBE_DEFAULT); |
---|
707 | } |
---|
708 | |
---|
709 | static int |
---|
710 | cpsw_intr_attach(struct cpsw_softc *sc) |
---|
711 | { |
---|
712 | int i; |
---|
713 | |
---|
714 | for (i = 0; i < CPSW_INTR_COUNT; i++) { |
---|
715 | if (bus_setup_intr(sc->dev, sc->irq_res[i], |
---|
716 | INTR_TYPE_NET | INTR_MPSAFE, NULL, |
---|
717 | cpsw_intr_cb[i].cb, sc, &sc->ih_cookie[i]) != 0) { |
---|
718 | return (-1); |
---|
719 | } |
---|
720 | } |
---|
721 | |
---|
722 | return (0); |
---|
723 | } |
---|
724 | |
---|
725 | static void |
---|
726 | cpsw_intr_detach(struct cpsw_softc *sc) |
---|
727 | { |
---|
728 | int i; |
---|
729 | |
---|
730 | for (i = 0; i < CPSW_INTR_COUNT; i++) { |
---|
731 | if (sc->ih_cookie[i]) { |
---|
732 | bus_teardown_intr(sc->dev, sc->irq_res[i], |
---|
733 | sc->ih_cookie[i]); |
---|
734 | } |
---|
735 | } |
---|
736 | } |
---|
737 | |
---|
738 | static int |
---|
739 | cpsw_get_fdt_data(struct cpsw_softc *sc, int port) |
---|
740 | { |
---|
741 | char *name; |
---|
742 | int len, phy, vlan; |
---|
743 | pcell_t phy_id[3], vlan_id; |
---|
744 | phandle_t child; |
---|
745 | unsigned long mdio_child_addr; |
---|
746 | |
---|
747 | /* Find any slave with phy_id */ |
---|
748 | phy = -1; |
---|
749 | vlan = -1; |
---|
750 | for (child = OF_child(sc->node); child != 0; child = OF_peer(child)) { |
---|
751 | if (OF_getprop_alloc(child, "name", (void **)&name) < 0) |
---|
752 | continue; |
---|
753 | if (sscanf(name, "slave@%lx", &mdio_child_addr) != 1) { |
---|
754 | OF_prop_free(name); |
---|
755 | continue; |
---|
756 | } |
---|
757 | OF_prop_free(name); |
---|
758 | if (mdio_child_addr != slave_mdio_addr[port]) |
---|
759 | continue; |
---|
760 | |
---|
761 | len = OF_getproplen(child, "phy_id"); |
---|
762 | if (len / sizeof(pcell_t) == 2) { |
---|
763 | /* Get phy address from fdt */ |
---|
764 | if (OF_getencprop(child, "phy_id", phy_id, len) > 0) |
---|
765 | phy = phy_id[1]; |
---|
766 | } |
---|
767 | |
---|
768 | len = OF_getproplen(child, "dual_emac_res_vlan"); |
---|
769 | if (len / sizeof(pcell_t) == 1) { |
---|
770 | /* Get phy address from fdt */ |
---|
771 | if (OF_getencprop(child, "dual_emac_res_vlan", |
---|
772 | &vlan_id, len) > 0) { |
---|
773 | vlan = vlan_id; |
---|
774 | } |
---|
775 | } |
---|
776 | |
---|
777 | break; |
---|
778 | } |
---|
779 | if (phy == -1) |
---|
780 | return (ENXIO); |
---|
781 | sc->port[port].phy = phy; |
---|
782 | sc->port[port].vlan = vlan; |
---|
783 | |
---|
784 | return (0); |
---|
785 | } |
---|
786 | |
---|
787 | static int |
---|
788 | cpsw_attach(device_t dev) |
---|
789 | { |
---|
790 | int error, i; |
---|
791 | struct cpsw_softc *sc; |
---|
792 | uint32_t reg; |
---|
793 | |
---|
794 | sc = device_get_softc(dev); |
---|
795 | sc->dev = dev; |
---|
796 | sc->node = ofw_bus_get_node(dev); |
---|
797 | getbinuptime(&sc->attach_uptime); |
---|
798 | |
---|
799 | if (OF_getencprop(sc->node, "active_slave", &sc->active_slave, |
---|
800 | sizeof(sc->active_slave)) <= 0) { |
---|
801 | sc->active_slave = 0; |
---|
802 | } |
---|
803 | if (sc->active_slave > 1) |
---|
804 | sc->active_slave = 1; |
---|
805 | |
---|
806 | if (OF_hasprop(sc->node, "dual_emac")) |
---|
807 | sc->dualemac = 1; |
---|
808 | |
---|
809 | for (i = 0; i < CPSW_PORTS; i++) { |
---|
810 | if (!sc->dualemac && i != sc->active_slave) |
---|
811 | continue; |
---|
812 | if (cpsw_get_fdt_data(sc, i) != 0) { |
---|
813 | device_printf(dev, |
---|
814 | "failed to get PHY address from FDT\n"); |
---|
815 | return (ENXIO); |
---|
816 | } |
---|
817 | } |
---|
818 | |
---|
819 | /* Initialize mutexes */ |
---|
820 | mtx_init(&sc->tx.lock, device_get_nameunit(dev), |
---|
821 | "cpsw TX lock", MTX_DEF); |
---|
822 | mtx_init(&sc->rx.lock, device_get_nameunit(dev), |
---|
823 | "cpsw RX lock", MTX_DEF); |
---|
824 | |
---|
825 | /* Allocate IRQ resources */ |
---|
826 | error = bus_alloc_resources(dev, irq_res_spec, sc->irq_res); |
---|
827 | if (error) { |
---|
828 | device_printf(dev, "could not allocate IRQ resources\n"); |
---|
829 | cpsw_detach(dev); |
---|
830 | return (ENXIO); |
---|
831 | } |
---|
832 | |
---|
833 | sc->mem_rid = 0; |
---|
834 | sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, |
---|
835 | &sc->mem_rid, RF_ACTIVE); |
---|
836 | if (sc->mem_res == NULL) { |
---|
837 | device_printf(sc->dev, "failed to allocate memory resource\n"); |
---|
838 | cpsw_detach(dev); |
---|
839 | return (ENXIO); |
---|
840 | } |
---|
841 | |
---|
842 | reg = cpsw_read_4(sc, CPSW_SS_IDVER); |
---|
843 | device_printf(dev, "CPSW SS Version %d.%d (%d)\n", (reg >> 8 & 0x7), |
---|
844 | reg & 0xFF, (reg >> 11) & 0x1F); |
---|
845 | |
---|
846 | cpsw_add_sysctls(sc); |
---|
847 | |
---|
848 | /* Allocate a busdma tag and DMA safe memory for mbufs. */ |
---|
849 | error = bus_dma_tag_create( |
---|
850 | bus_get_dma_tag(sc->dev), /* parent */ |
---|
851 | 1, 0, /* alignment, boundary */ |
---|
852 | BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ |
---|
853 | BUS_SPACE_MAXADDR, /* highaddr */ |
---|
854 | NULL, NULL, /* filtfunc, filtfuncarg */ |
---|
855 | MCLBYTES, CPSW_TXFRAGS, /* maxsize, nsegments */ |
---|
856 | MCLBYTES, 0, /* maxsegsz, flags */ |
---|
857 | NULL, NULL, /* lockfunc, lockfuncarg */ |
---|
858 | &sc->mbuf_dtag); /* dmatag */ |
---|
859 | if (error) { |
---|
860 | device_printf(dev, "bus_dma_tag_create failed\n"); |
---|
861 | cpsw_detach(dev); |
---|
862 | return (error); |
---|
863 | } |
---|
864 | |
---|
865 | /* Allocate a NULL buffer for padding. */ |
---|
866 | sc->nullpad = malloc(ETHER_MIN_LEN, M_DEVBUF, M_WAITOK | M_ZERO); |
---|
867 | |
---|
868 | cpsw_init_slots(sc); |
---|
869 | |
---|
870 | /* Allocate slots to TX and RX queues. */ |
---|
871 | STAILQ_INIT(&sc->rx.avail); |
---|
872 | STAILQ_INIT(&sc->rx.active); |
---|
873 | STAILQ_INIT(&sc->tx.avail); |
---|
874 | STAILQ_INIT(&sc->tx.active); |
---|
875 | // For now: 128 slots to TX, rest to RX. |
---|
876 | // XXX TODO: start with 32/64 and grow dynamically based on demand. |
---|
877 | if (cpsw_add_slots(sc, &sc->tx, 128) || |
---|
878 | cpsw_add_slots(sc, &sc->rx, -1)) { |
---|
879 | device_printf(dev, "failed to allocate dmamaps\n"); |
---|
880 | cpsw_detach(dev); |
---|
881 | return (ENOMEM); |
---|
882 | } |
---|
883 | device_printf(dev, "Initial queue size TX=%d RX=%d\n", |
---|
884 | sc->tx.queue_slots, sc->rx.queue_slots); |
---|
885 | |
---|
886 | sc->tx.hdp_offset = CPSW_CPDMA_TX_HDP(0); |
---|
887 | sc->rx.hdp_offset = CPSW_CPDMA_RX_HDP(0); |
---|
888 | |
---|
889 | if (cpsw_intr_attach(sc) == -1) { |
---|
890 | device_printf(dev, "failed to setup interrupts\n"); |
---|
891 | cpsw_detach(dev); |
---|
892 | return (ENXIO); |
---|
893 | } |
---|
894 | |
---|
895 | #ifdef CPSW_ETHERSWITCH |
---|
896 | for (i = 0; i < CPSW_VLANS; i++) |
---|
897 | cpsw_vgroups[i].vid = -1; |
---|
898 | #endif |
---|
899 | |
---|
900 | /* Reset the controller. */ |
---|
901 | cpsw_reset(sc); |
---|
902 | cpsw_init(sc); |
---|
903 | |
---|
904 | for (i = 0; i < CPSW_PORTS; i++) { |
---|
905 | if (!sc->dualemac && i != sc->active_slave) |
---|
906 | continue; |
---|
907 | sc->port[i].dev = device_add_child(dev, "cpsw", i); |
---|
908 | if (sc->port[i].dev == NULL) { |
---|
909 | cpsw_detach(dev); |
---|
910 | return (ENXIO); |
---|
911 | } |
---|
912 | } |
---|
913 | bus_generic_probe(dev); |
---|
914 | bus_generic_attach(dev); |
---|
915 | |
---|
916 | return (0); |
---|
917 | } |
---|
918 | |
---|
919 | static int |
---|
920 | cpsw_detach(device_t dev) |
---|
921 | { |
---|
922 | struct cpsw_softc *sc; |
---|
923 | int error, i; |
---|
924 | |
---|
925 | bus_generic_detach(dev); |
---|
926 | sc = device_get_softc(dev); |
---|
927 | |
---|
928 | for (i = 0; i < CPSW_PORTS; i++) { |
---|
929 | if (sc->port[i].dev) |
---|
930 | device_delete_child(dev, sc->port[i].dev); |
---|
931 | } |
---|
932 | |
---|
933 | if (device_is_attached(dev)) { |
---|
934 | callout_stop(&sc->watchdog.callout); |
---|
935 | callout_drain(&sc->watchdog.callout); |
---|
936 | } |
---|
937 | |
---|
938 | /* Stop and release all interrupts */ |
---|
939 | cpsw_intr_detach(sc); |
---|
940 | |
---|
941 | /* Free dmamaps and mbufs */ |
---|
942 | for (i = 0; i < nitems(sc->_slots); ++i) |
---|
943 | cpsw_free_slot(sc, &sc->_slots[i]); |
---|
944 | |
---|
945 | /* Free null padding buffer. */ |
---|
946 | if (sc->nullpad) |
---|
947 | free(sc->nullpad, M_DEVBUF); |
---|
948 | |
---|
949 | /* Free DMA tag */ |
---|
950 | if (sc->mbuf_dtag) { |
---|
951 | error = bus_dma_tag_destroy(sc->mbuf_dtag); |
---|
952 | KASSERT(error == 0, ("Unable to destroy DMA tag")); |
---|
953 | } |
---|
954 | |
---|
955 | /* Free IO memory handler */ |
---|
956 | if (sc->mem_res != NULL) |
---|
957 | bus_release_resource(dev, SYS_RES_MEMORY, sc->mem_rid, sc->mem_res); |
---|
958 | bus_release_resources(dev, irq_res_spec, sc->irq_res); |
---|
959 | |
---|
960 | /* Destroy mutexes */ |
---|
961 | mtx_destroy(&sc->rx.lock); |
---|
962 | mtx_destroy(&sc->tx.lock); |
---|
963 | |
---|
964 | /* Detach the switch device, if present. */ |
---|
965 | error = bus_generic_detach(dev); |
---|
966 | if (error != 0) |
---|
967 | return (error); |
---|
968 | |
---|
969 | return (device_delete_children(dev)); |
---|
970 | } |
---|
971 | |
---|
972 | static phandle_t |
---|
973 | cpsw_get_node(device_t bus, device_t dev) |
---|
974 | { |
---|
975 | |
---|
976 | /* Share controller node with port device. */ |
---|
977 | return (ofw_bus_get_node(bus)); |
---|
978 | } |
---|
979 | |
---|
980 | static int |
---|
981 | cpswp_probe(device_t dev) |
---|
982 | { |
---|
983 | |
---|
984 | if (device_get_unit(dev) > 1) { |
---|
985 | device_printf(dev, "Only two ports are supported.\n"); |
---|
986 | return (ENXIO); |
---|
987 | } |
---|
988 | device_set_desc(dev, "Ethernet Switch Port"); |
---|
989 | |
---|
990 | return (BUS_PROBE_DEFAULT); |
---|
991 | } |
---|
992 | |
---|
993 | static int |
---|
994 | cpswp_attach(device_t dev) |
---|
995 | { |
---|
996 | int error; |
---|
997 | struct ifnet *ifp; |
---|
998 | struct cpswp_softc *sc; |
---|
999 | uint32_t reg; |
---|
1000 | uint8_t mac_addr[ETHER_ADDR_LEN]; |
---|
1001 | |
---|
1002 | sc = device_get_softc(dev); |
---|
1003 | sc->dev = dev; |
---|
1004 | sc->pdev = device_get_parent(dev); |
---|
1005 | sc->swsc = device_get_softc(sc->pdev); |
---|
1006 | sc->unit = device_get_unit(dev); |
---|
1007 | sc->phy = sc->swsc->port[sc->unit].phy; |
---|
1008 | sc->vlan = sc->swsc->port[sc->unit].vlan; |
---|
1009 | if (sc->swsc->dualemac && sc->vlan == -1) |
---|
1010 | sc->vlan = sc->unit + 1; |
---|
1011 | |
---|
1012 | if (sc->unit == 0) { |
---|
1013 | sc->physel = MDIOUSERPHYSEL0; |
---|
1014 | sc->phyaccess = MDIOUSERACCESS0; |
---|
1015 | } else { |
---|
1016 | sc->physel = MDIOUSERPHYSEL1; |
---|
1017 | sc->phyaccess = MDIOUSERACCESS1; |
---|
1018 | } |
---|
1019 | |
---|
1020 | mtx_init(&sc->lock, device_get_nameunit(dev), "cpsw port lock", |
---|
1021 | MTX_DEF); |
---|
1022 | |
---|
1023 | /* Allocate network interface */ |
---|
1024 | ifp = sc->ifp = if_alloc(IFT_ETHER); |
---|
1025 | if (ifp == NULL) { |
---|
1026 | cpswp_detach(dev); |
---|
1027 | return (ENXIO); |
---|
1028 | } |
---|
1029 | |
---|
1030 | if_initname(ifp, device_get_name(sc->dev), sc->unit); |
---|
1031 | ifp->if_softc = sc; |
---|
1032 | ifp->if_flags = IFF_SIMPLEX | IFF_MULTICAST | IFF_BROADCAST; |
---|
1033 | ifp->if_capabilities = IFCAP_VLAN_MTU | IFCAP_HWCSUM; //FIXME VLAN? |
---|
1034 | ifp->if_capenable = ifp->if_capabilities; |
---|
1035 | |
---|
1036 | ifp->if_init = cpswp_init; |
---|
1037 | ifp->if_start = cpswp_start; |
---|
1038 | ifp->if_ioctl = cpswp_ioctl; |
---|
1039 | |
---|
1040 | ifp->if_snd.ifq_drv_maxlen = sc->swsc->tx.queue_slots; |
---|
1041 | IFQ_SET_MAXLEN(&ifp->if_snd, ifp->if_snd.ifq_drv_maxlen); |
---|
1042 | IFQ_SET_READY(&ifp->if_snd); |
---|
1043 | |
---|
1044 | /* Get high part of MAC address from control module (mac_id[0|1]_hi) */ |
---|
1045 | ti_scm_reg_read_4(SCM_MAC_ID0_HI + sc->unit * 8, ®); |
---|
1046 | mac_addr[0] = reg & 0xFF; |
---|
1047 | mac_addr[1] = (reg >> 8) & 0xFF; |
---|
1048 | mac_addr[2] = (reg >> 16) & 0xFF; |
---|
1049 | mac_addr[3] = (reg >> 24) & 0xFF; |
---|
1050 | |
---|
1051 | /* Get low part of MAC address from control module (mac_id[0|1]_lo) */ |
---|
1052 | ti_scm_reg_read_4(SCM_MAC_ID0_LO + sc->unit * 8, ®); |
---|
1053 | mac_addr[4] = reg & 0xFF; |
---|
1054 | mac_addr[5] = (reg >> 8) & 0xFF; |
---|
1055 | |
---|
1056 | error = mii_attach(dev, &sc->miibus, ifp, cpswp_ifmedia_upd, |
---|
1057 | cpswp_ifmedia_sts, BMSR_DEFCAPMASK, sc->phy, MII_OFFSET_ANY, 0); |
---|
1058 | if (error) { |
---|
1059 | device_printf(dev, "attaching PHYs failed\n"); |
---|
1060 | cpswp_detach(dev); |
---|
1061 | return (error); |
---|
1062 | } |
---|
1063 | sc->mii = device_get_softc(sc->miibus); |
---|
1064 | |
---|
1065 | /* Select PHY and enable interrupts */ |
---|
1066 | cpsw_write_4(sc->swsc, sc->physel, |
---|
1067 | MDIO_PHYSEL_LINKINTENB | (sc->phy & 0x1F)); |
---|
1068 | |
---|
1069 | ether_ifattach(sc->ifp, mac_addr); |
---|
1070 | callout_init(&sc->mii_callout, 0); |
---|
1071 | |
---|
1072 | return (0); |
---|
1073 | } |
---|
1074 | |
---|
1075 | static int |
---|
1076 | cpswp_detach(device_t dev) |
---|
1077 | { |
---|
1078 | struct cpswp_softc *sc; |
---|
1079 | |
---|
1080 | sc = device_get_softc(dev); |
---|
1081 | CPSW_DEBUGF(sc->swsc, ("")); |
---|
1082 | if (device_is_attached(dev)) { |
---|
1083 | ether_ifdetach(sc->ifp); |
---|
1084 | CPSW_PORT_LOCK(sc); |
---|
1085 | cpswp_stop_locked(sc); |
---|
1086 | CPSW_PORT_UNLOCK(sc); |
---|
1087 | callout_drain(&sc->mii_callout); |
---|
1088 | } |
---|
1089 | |
---|
1090 | bus_generic_detach(dev); |
---|
1091 | |
---|
1092 | if_free(sc->ifp); |
---|
1093 | mtx_destroy(&sc->lock); |
---|
1094 | |
---|
1095 | return (0); |
---|
1096 | } |
---|
1097 | |
---|
1098 | /* |
---|
1099 | * |
---|
1100 | * Init/Shutdown. |
---|
1101 | * |
---|
1102 | */ |
---|
1103 | |
---|
1104 | static int |
---|
1105 | cpsw_ports_down(struct cpsw_softc *sc) |
---|
1106 | { |
---|
1107 | struct cpswp_softc *psc; |
---|
1108 | struct ifnet *ifp1, *ifp2; |
---|
1109 | |
---|
1110 | if (!sc->dualemac) |
---|
1111 | return (1); |
---|
1112 | psc = device_get_softc(sc->port[0].dev); |
---|
1113 | ifp1 = psc->ifp; |
---|
1114 | psc = device_get_softc(sc->port[1].dev); |
---|
1115 | ifp2 = psc->ifp; |
---|
1116 | if ((ifp1->if_flags & IFF_UP) == 0 && (ifp2->if_flags & IFF_UP) == 0) |
---|
1117 | return (1); |
---|
1118 | |
---|
1119 | return (0); |
---|
1120 | } |
---|
1121 | |
---|
1122 | static void |
---|
1123 | cpswp_init(void *arg) |
---|
1124 | { |
---|
1125 | struct cpswp_softc *sc = arg; |
---|
1126 | |
---|
1127 | CPSW_DEBUGF(sc->swsc, ("")); |
---|
1128 | CPSW_PORT_LOCK(sc); |
---|
1129 | cpswp_init_locked(arg); |
---|
1130 | CPSW_PORT_UNLOCK(sc); |
---|
1131 | } |
---|
1132 | |
---|
1133 | static void |
---|
1134 | cpswp_init_locked(void *arg) |
---|
1135 | { |
---|
1136 | #ifdef CPSW_ETHERSWITCH |
---|
1137 | int i; |
---|
1138 | #endif |
---|
1139 | struct cpswp_softc *sc = arg; |
---|
1140 | struct ifnet *ifp; |
---|
1141 | uint32_t reg; |
---|
1142 | |
---|
1143 | CPSW_DEBUGF(sc->swsc, ("")); |
---|
1144 | CPSW_PORT_LOCK_ASSERT(sc); |
---|
1145 | ifp = sc->ifp; |
---|
1146 | if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) |
---|
1147 | return; |
---|
1148 | |
---|
1149 | getbinuptime(&sc->init_uptime); |
---|
1150 | |
---|
1151 | if (!sc->swsc->rx.running && !sc->swsc->tx.running) { |
---|
1152 | /* Reset the controller. */ |
---|
1153 | cpsw_reset(sc->swsc); |
---|
1154 | cpsw_init(sc->swsc); |
---|
1155 | } |
---|
1156 | |
---|
1157 | /* Set Slave Mapping. */ |
---|
1158 | cpsw_write_4(sc->swsc, CPSW_SL_RX_PRI_MAP(sc->unit), 0x76543210); |
---|
1159 | cpsw_write_4(sc->swsc, CPSW_PORT_P_TX_PRI_MAP(sc->unit + 1), |
---|
1160 | 0x33221100); |
---|
1161 | cpsw_write_4(sc->swsc, CPSW_SL_RX_MAXLEN(sc->unit), 0x5f2); |
---|
1162 | /* Enable MAC RX/TX modules. */ |
---|
1163 | /* TODO: Docs claim that IFCTL_B and IFCTL_A do the same thing? */ |
---|
1164 | /* Huh? Docs call bit 0 "Loopback" some places, "FullDuplex" others. */ |
---|
1165 | reg = cpsw_read_4(sc->swsc, CPSW_SL_MACCONTROL(sc->unit)); |
---|
1166 | reg |= CPSW_SL_MACTL_GMII_ENABLE; |
---|
1167 | cpsw_write_4(sc->swsc, CPSW_SL_MACCONTROL(sc->unit), reg); |
---|
1168 | |
---|
1169 | /* Initialize ALE: set port to forwarding, initialize addrs */ |
---|
1170 | cpsw_write_4(sc->swsc, CPSW_ALE_PORTCTL(sc->unit + 1), |
---|
1171 | ALE_PORTCTL_INGRESS | ALE_PORTCTL_FORWARD); |
---|
1172 | cpswp_ale_update_addresses(sc, 1); |
---|
1173 | |
---|
1174 | if (sc->swsc->dualemac) { |
---|
1175 | /* Set Port VID. */ |
---|
1176 | cpsw_write_4(sc->swsc, CPSW_PORT_P_VLAN(sc->unit + 1), |
---|
1177 | sc->vlan & 0xfff); |
---|
1178 | cpsw_ale_update_vlan_table(sc->swsc, sc->vlan, |
---|
1179 | (1 << (sc->unit + 1)) | (1 << 0), /* Member list */ |
---|
1180 | (1 << (sc->unit + 1)) | (1 << 0), /* Untagged egress */ |
---|
1181 | (1 << (sc->unit + 1)) | (1 << 0), 0); /* mcast reg flood */ |
---|
1182 | #ifdef CPSW_ETHERSWITCH |
---|
1183 | for (i = 0; i < CPSW_VLANS; i++) { |
---|
1184 | if (cpsw_vgroups[i].vid != -1) |
---|
1185 | continue; |
---|
1186 | cpsw_vgroups[i].vid = sc->vlan; |
---|
1187 | break; |
---|
1188 | } |
---|
1189 | #endif |
---|
1190 | } |
---|
1191 | |
---|
1192 | mii_mediachg(sc->mii); |
---|
1193 | callout_reset(&sc->mii_callout, hz, cpswp_tick, sc); |
---|
1194 | ifp->if_drv_flags |= IFF_DRV_RUNNING; |
---|
1195 | ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; |
---|
1196 | } |
---|
1197 | |
---|
1198 | static int |
---|
1199 | cpsw_shutdown(device_t dev) |
---|
1200 | { |
---|
1201 | struct cpsw_softc *sc; |
---|
1202 | struct cpswp_softc *psc; |
---|
1203 | int i; |
---|
1204 | |
---|
1205 | sc = device_get_softc(dev); |
---|
1206 | CPSW_DEBUGF(sc, ("")); |
---|
1207 | for (i = 0; i < CPSW_PORTS; i++) { |
---|
1208 | if (!sc->dualemac && i != sc->active_slave) |
---|
1209 | continue; |
---|
1210 | psc = device_get_softc(sc->port[i].dev); |
---|
1211 | CPSW_PORT_LOCK(psc); |
---|
1212 | cpswp_stop_locked(psc); |
---|
1213 | CPSW_PORT_UNLOCK(psc); |
---|
1214 | } |
---|
1215 | |
---|
1216 | return (0); |
---|
1217 | } |
---|
1218 | |
---|
1219 | static void |
---|
1220 | cpsw_rx_teardown(struct cpsw_softc *sc) |
---|
1221 | { |
---|
1222 | int i = 0; |
---|
1223 | |
---|
1224 | CPSW_RX_LOCK(sc); |
---|
1225 | CPSW_DEBUGF(sc, ("starting RX teardown")); |
---|
1226 | sc->rx.teardown = 1; |
---|
1227 | cpsw_write_4(sc, CPSW_CPDMA_RX_TEARDOWN, 0); |
---|
1228 | CPSW_RX_UNLOCK(sc); |
---|
1229 | while (sc->rx.running) { |
---|
1230 | if (++i > 10) { |
---|
1231 | device_printf(sc->dev, |
---|
1232 | "Unable to cleanly shutdown receiver\n"); |
---|
1233 | return; |
---|
1234 | } |
---|
1235 | DELAY(200); |
---|
1236 | } |
---|
1237 | if (!sc->rx.running) |
---|
1238 | CPSW_DEBUGF(sc, ("finished RX teardown (%d retries)", i)); |
---|
1239 | } |
---|
1240 | |
---|
1241 | static void |
---|
1242 | cpsw_tx_teardown(struct cpsw_softc *sc) |
---|
1243 | { |
---|
1244 | int i = 0; |
---|
1245 | |
---|
1246 | CPSW_TX_LOCK(sc); |
---|
1247 | CPSW_DEBUGF(sc, ("starting TX teardown")); |
---|
1248 | /* Start the TX queue teardown if queue is not empty. */ |
---|
1249 | if (STAILQ_FIRST(&sc->tx.active) != NULL) |
---|
1250 | cpsw_write_4(sc, CPSW_CPDMA_TX_TEARDOWN, 0); |
---|
1251 | else |
---|
1252 | sc->tx.teardown = 1; |
---|
1253 | cpsw_tx_dequeue(sc); |
---|
1254 | while (sc->tx.running && ++i < 10) { |
---|
1255 | DELAY(200); |
---|
1256 | cpsw_tx_dequeue(sc); |
---|
1257 | } |
---|
1258 | if (sc->tx.running) { |
---|
1259 | device_printf(sc->dev, |
---|
1260 | "Unable to cleanly shutdown transmitter\n"); |
---|
1261 | } |
---|
1262 | CPSW_DEBUGF(sc, |
---|
1263 | ("finished TX teardown (%d retries, %d idle buffers)", i, |
---|
1264 | sc->tx.active_queue_len)); |
---|
1265 | CPSW_TX_UNLOCK(sc); |
---|
1266 | } |
---|
1267 | |
---|
1268 | static void |
---|
1269 | cpswp_stop_locked(struct cpswp_softc *sc) |
---|
1270 | { |
---|
1271 | struct ifnet *ifp; |
---|
1272 | uint32_t reg; |
---|
1273 | |
---|
1274 | ifp = sc->ifp; |
---|
1275 | CPSW_DEBUGF(sc->swsc, ("")); |
---|
1276 | CPSW_PORT_LOCK_ASSERT(sc); |
---|
1277 | |
---|
1278 | if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) |
---|
1279 | return; |
---|
1280 | |
---|
1281 | /* Disable interface */ |
---|
1282 | ifp->if_drv_flags &= ~IFF_DRV_RUNNING; |
---|
1283 | ifp->if_drv_flags |= IFF_DRV_OACTIVE; |
---|
1284 | |
---|
1285 | /* Stop ticker */ |
---|
1286 | callout_stop(&sc->mii_callout); |
---|
1287 | |
---|
1288 | /* Tear down the RX/TX queues. */ |
---|
1289 | if (cpsw_ports_down(sc->swsc)) { |
---|
1290 | cpsw_rx_teardown(sc->swsc); |
---|
1291 | cpsw_tx_teardown(sc->swsc); |
---|
1292 | } |
---|
1293 | |
---|
1294 | /* Stop MAC RX/TX modules. */ |
---|
1295 | reg = cpsw_read_4(sc->swsc, CPSW_SL_MACCONTROL(sc->unit)); |
---|
1296 | reg &= ~CPSW_SL_MACTL_GMII_ENABLE; |
---|
1297 | cpsw_write_4(sc->swsc, CPSW_SL_MACCONTROL(sc->unit), reg); |
---|
1298 | |
---|
1299 | if (cpsw_ports_down(sc->swsc)) { |
---|
1300 | /* Capture stats before we reset controller. */ |
---|
1301 | cpsw_stats_collect(sc->swsc); |
---|
1302 | |
---|
1303 | cpsw_reset(sc->swsc); |
---|
1304 | cpsw_init(sc->swsc); |
---|
1305 | } |
---|
1306 | } |
---|
1307 | |
---|
1308 | /* |
---|
1309 | * Suspend/Resume. |
---|
1310 | */ |
---|
1311 | |
---|
1312 | static int |
---|
1313 | cpsw_suspend(device_t dev) |
---|
1314 | { |
---|
1315 | struct cpsw_softc *sc; |
---|
1316 | struct cpswp_softc *psc; |
---|
1317 | int i; |
---|
1318 | |
---|
1319 | sc = device_get_softc(dev); |
---|
1320 | CPSW_DEBUGF(sc, ("")); |
---|
1321 | for (i = 0; i < CPSW_PORTS; i++) { |
---|
1322 | if (!sc->dualemac && i != sc->active_slave) |
---|
1323 | continue; |
---|
1324 | psc = device_get_softc(sc->port[i].dev); |
---|
1325 | CPSW_PORT_LOCK(psc); |
---|
1326 | cpswp_stop_locked(psc); |
---|
1327 | CPSW_PORT_UNLOCK(psc); |
---|
1328 | } |
---|
1329 | |
---|
1330 | return (0); |
---|
1331 | } |
---|
1332 | |
---|
1333 | static int |
---|
1334 | cpsw_resume(device_t dev) |
---|
1335 | { |
---|
1336 | struct cpsw_softc *sc; |
---|
1337 | |
---|
1338 | sc = device_get_softc(dev); |
---|
1339 | CPSW_DEBUGF(sc, ("UNIMPLEMENTED")); |
---|
1340 | |
---|
1341 | return (0); |
---|
1342 | } |
---|
1343 | |
---|
1344 | /* |
---|
1345 | * |
---|
1346 | * IOCTL |
---|
1347 | * |
---|
1348 | */ |
---|
1349 | |
---|
1350 | static void |
---|
1351 | cpsw_set_promisc(struct cpswp_softc *sc, int set) |
---|
1352 | { |
---|
1353 | uint32_t reg; |
---|
1354 | |
---|
1355 | /* |
---|
1356 | * Enabling promiscuous mode requires ALE_BYPASS to be enabled. |
---|
1357 | * That disables the ALE forwarding logic and causes every |
---|
1358 | * packet to be sent only to the host port. In bypass mode, |
---|
1359 | * the ALE processes host port transmit packets the same as in |
---|
1360 | * normal mode. |
---|
1361 | */ |
---|
1362 | reg = cpsw_read_4(sc->swsc, CPSW_ALE_CONTROL); |
---|
1363 | reg &= ~CPSW_ALE_CTL_BYPASS; |
---|
1364 | if (set) |
---|
1365 | reg |= CPSW_ALE_CTL_BYPASS; |
---|
1366 | cpsw_write_4(sc->swsc, CPSW_ALE_CONTROL, reg); |
---|
1367 | } |
---|
1368 | |
---|
1369 | static void |
---|
1370 | cpsw_set_allmulti(struct cpswp_softc *sc, int set) |
---|
1371 | { |
---|
1372 | if (set) { |
---|
1373 | printf("All-multicast mode unimplemented\n"); |
---|
1374 | } |
---|
1375 | } |
---|
1376 | |
---|
1377 | static int |
---|
1378 | cpswp_ioctl(struct ifnet *ifp, u_long command, caddr_t data) |
---|
1379 | { |
---|
1380 | struct cpswp_softc *sc; |
---|
1381 | struct ifreq *ifr; |
---|
1382 | int error; |
---|
1383 | uint32_t changed; |
---|
1384 | |
---|
1385 | error = 0; |
---|
1386 | sc = ifp->if_softc; |
---|
1387 | ifr = (struct ifreq *)data; |
---|
1388 | |
---|
1389 | switch (command) { |
---|
1390 | case SIOCSIFCAP: |
---|
1391 | changed = ifp->if_capenable ^ ifr->ifr_reqcap; |
---|
1392 | if (changed & IFCAP_HWCSUM) { |
---|
1393 | if ((ifr->ifr_reqcap & changed) & IFCAP_HWCSUM) |
---|
1394 | ifp->if_capenable |= IFCAP_HWCSUM; |
---|
1395 | else |
---|
1396 | ifp->if_capenable &= ~IFCAP_HWCSUM; |
---|
1397 | } |
---|
1398 | error = 0; |
---|
1399 | break; |
---|
1400 | case SIOCSIFFLAGS: |
---|
1401 | CPSW_PORT_LOCK(sc); |
---|
1402 | if (ifp->if_flags & IFF_UP) { |
---|
1403 | if (ifp->if_drv_flags & IFF_DRV_RUNNING) { |
---|
1404 | changed = ifp->if_flags ^ sc->if_flags; |
---|
1405 | CPSW_DEBUGF(sc->swsc, |
---|
1406 | ("SIOCSIFFLAGS: UP & RUNNING (changed=0x%x)", |
---|
1407 | changed)); |
---|
1408 | if (changed & IFF_PROMISC) |
---|
1409 | cpsw_set_promisc(sc, |
---|
1410 | ifp->if_flags & IFF_PROMISC); |
---|
1411 | if (changed & IFF_ALLMULTI) |
---|
1412 | cpsw_set_allmulti(sc, |
---|
1413 | ifp->if_flags & IFF_ALLMULTI); |
---|
1414 | } else { |
---|
1415 | CPSW_DEBUGF(sc->swsc, |
---|
1416 | ("SIOCSIFFLAGS: starting up")); |
---|
1417 | cpswp_init_locked(sc); |
---|
1418 | } |
---|
1419 | } else if (ifp->if_drv_flags & IFF_DRV_RUNNING) { |
---|
1420 | CPSW_DEBUGF(sc->swsc, ("SIOCSIFFLAGS: shutting down")); |
---|
1421 | cpswp_stop_locked(sc); |
---|
1422 | } |
---|
1423 | |
---|
1424 | sc->if_flags = ifp->if_flags; |
---|
1425 | CPSW_PORT_UNLOCK(sc); |
---|
1426 | break; |
---|
1427 | case SIOCADDMULTI: |
---|
1428 | cpswp_ale_update_addresses(sc, 0); |
---|
1429 | break; |
---|
1430 | case SIOCDELMULTI: |
---|
1431 | /* Ugh. DELMULTI doesn't provide the specific address |
---|
1432 | being removed, so the best we can do is remove |
---|
1433 | everything and rebuild it all. */ |
---|
1434 | cpswp_ale_update_addresses(sc, 1); |
---|
1435 | break; |
---|
1436 | case SIOCGIFMEDIA: |
---|
1437 | case SIOCSIFMEDIA: |
---|
1438 | error = ifmedia_ioctl(ifp, ifr, &sc->mii->mii_media, command); |
---|
1439 | break; |
---|
1440 | default: |
---|
1441 | error = ether_ioctl(ifp, command, data); |
---|
1442 | } |
---|
1443 | return (error); |
---|
1444 | } |
---|
1445 | |
---|
1446 | /* |
---|
1447 | * |
---|
1448 | * MIIBUS |
---|
1449 | * |
---|
1450 | */ |
---|
1451 | static int |
---|
1452 | cpswp_miibus_ready(struct cpsw_softc *sc, uint32_t reg) |
---|
1453 | { |
---|
1454 | uint32_t r, retries = CPSW_MIIBUS_RETRIES; |
---|
1455 | |
---|
1456 | while (--retries) { |
---|
1457 | r = cpsw_read_4(sc, reg); |
---|
1458 | if ((r & MDIO_PHYACCESS_GO) == 0) |
---|
1459 | return (1); |
---|
1460 | DELAY(CPSW_MIIBUS_DELAY); |
---|
1461 | } |
---|
1462 | |
---|
1463 | return (0); |
---|
1464 | } |
---|
1465 | |
---|
1466 | static int |
---|
1467 | cpswp_miibus_readreg(device_t dev, int phy, int reg) |
---|
1468 | { |
---|
1469 | struct cpswp_softc *sc; |
---|
1470 | uint32_t cmd, r; |
---|
1471 | |
---|
1472 | sc = device_get_softc(dev); |
---|
1473 | if (!cpswp_miibus_ready(sc->swsc, sc->phyaccess)) { |
---|
1474 | device_printf(dev, "MDIO not ready to read\n"); |
---|
1475 | return (0); |
---|
1476 | } |
---|
1477 | |
---|
1478 | /* Set GO, reg, phy */ |
---|
1479 | cmd = MDIO_PHYACCESS_GO | (reg & 0x1F) << 21 | (phy & 0x1F) << 16; |
---|
1480 | cpsw_write_4(sc->swsc, sc->phyaccess, cmd); |
---|
1481 | |
---|
1482 | if (!cpswp_miibus_ready(sc->swsc, sc->phyaccess)) { |
---|
1483 | device_printf(dev, "MDIO timed out during read\n"); |
---|
1484 | return (0); |
---|
1485 | } |
---|
1486 | |
---|
1487 | r = cpsw_read_4(sc->swsc, sc->phyaccess); |
---|
1488 | if ((r & MDIO_PHYACCESS_ACK) == 0) { |
---|
1489 | device_printf(dev, "Failed to read from PHY.\n"); |
---|
1490 | r = 0; |
---|
1491 | } |
---|
1492 | return (r & 0xFFFF); |
---|
1493 | } |
---|
1494 | |
---|
1495 | static int |
---|
1496 | cpswp_miibus_writereg(device_t dev, int phy, int reg, int value) |
---|
1497 | { |
---|
1498 | struct cpswp_softc *sc; |
---|
1499 | uint32_t cmd; |
---|
1500 | |
---|
1501 | sc = device_get_softc(dev); |
---|
1502 | if (!cpswp_miibus_ready(sc->swsc, sc->phyaccess)) { |
---|
1503 | device_printf(dev, "MDIO not ready to write\n"); |
---|
1504 | return (0); |
---|
1505 | } |
---|
1506 | |
---|
1507 | /* Set GO, WRITE, reg, phy, and value */ |
---|
1508 | cmd = MDIO_PHYACCESS_GO | MDIO_PHYACCESS_WRITE | |
---|
1509 | (reg & 0x1F) << 21 | (phy & 0x1F) << 16 | (value & 0xFFFF); |
---|
1510 | cpsw_write_4(sc->swsc, sc->phyaccess, cmd); |
---|
1511 | |
---|
1512 | if (!cpswp_miibus_ready(sc->swsc, sc->phyaccess)) { |
---|
1513 | device_printf(dev, "MDIO timed out during write\n"); |
---|
1514 | return (0); |
---|
1515 | } |
---|
1516 | |
---|
1517 | return (0); |
---|
1518 | } |
---|
1519 | |
---|
1520 | static void |
---|
1521 | cpswp_miibus_statchg(device_t dev) |
---|
1522 | { |
---|
1523 | struct cpswp_softc *sc; |
---|
1524 | uint32_t mac_control, reg; |
---|
1525 | |
---|
1526 | sc = device_get_softc(dev); |
---|
1527 | CPSW_DEBUGF(sc->swsc, ("")); |
---|
1528 | |
---|
1529 | reg = CPSW_SL_MACCONTROL(sc->unit); |
---|
1530 | mac_control = cpsw_read_4(sc->swsc, reg); |
---|
1531 | mac_control &= ~(CPSW_SL_MACTL_GIG | CPSW_SL_MACTL_IFCTL_A | |
---|
1532 | CPSW_SL_MACTL_IFCTL_B | CPSW_SL_MACTL_FULLDUPLEX); |
---|
1533 | |
---|
1534 | switch(IFM_SUBTYPE(sc->mii->mii_media_active)) { |
---|
1535 | case IFM_1000_SX: |
---|
1536 | case IFM_1000_LX: |
---|
1537 | case IFM_1000_CX: |
---|
1538 | case IFM_1000_T: |
---|
1539 | mac_control |= CPSW_SL_MACTL_GIG; |
---|
1540 | break; |
---|
1541 | |
---|
1542 | case IFM_100_TX: |
---|
1543 | mac_control |= CPSW_SL_MACTL_IFCTL_A; |
---|
1544 | break; |
---|
1545 | } |
---|
1546 | if (sc->mii->mii_media_active & IFM_FDX) |
---|
1547 | mac_control |= CPSW_SL_MACTL_FULLDUPLEX; |
---|
1548 | |
---|
1549 | cpsw_write_4(sc->swsc, reg, mac_control); |
---|
1550 | } |
---|
1551 | |
---|
1552 | /* |
---|
1553 | * |
---|
1554 | * Transmit/Receive Packets. |
---|
1555 | * |
---|
1556 | */ |
---|
1557 | static void |
---|
1558 | cpsw_intr_rx(void *arg) |
---|
1559 | { |
---|
1560 | struct cpsw_softc *sc; |
---|
1561 | struct ifnet *ifp; |
---|
1562 | struct mbuf *received, *next; |
---|
1563 | |
---|
1564 | sc = (struct cpsw_softc *)arg; |
---|
1565 | CPSW_RX_LOCK(sc); |
---|
1566 | if (sc->rx.teardown) { |
---|
1567 | sc->rx.running = 0; |
---|
1568 | sc->rx.teardown = 0; |
---|
1569 | cpsw_write_cp(sc, &sc->rx, 0xfffffffc); |
---|
1570 | } |
---|
1571 | received = cpsw_rx_dequeue(sc); |
---|
1572 | cpsw_rx_enqueue(sc); |
---|
1573 | cpsw_write_4(sc, CPSW_CPDMA_CPDMA_EOI_VECTOR, 1); |
---|
1574 | CPSW_RX_UNLOCK(sc); |
---|
1575 | |
---|
1576 | while (received != NULL) { |
---|
1577 | next = received->m_nextpkt; |
---|
1578 | received->m_nextpkt = NULL; |
---|
1579 | ifp = received->m_pkthdr.rcvif; |
---|
1580 | (*ifp->if_input)(ifp, received); |
---|
1581 | if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1); |
---|
1582 | received = next; |
---|
1583 | } |
---|
1584 | } |
---|
1585 | |
---|
1586 | static struct mbuf * |
---|
1587 | cpsw_rx_dequeue(struct cpsw_softc *sc) |
---|
1588 | { |
---|
1589 | int nsegs, port, removed; |
---|
1590 | struct cpsw_cpdma_bd bd; |
---|
1591 | struct cpsw_slot *last, *slot; |
---|
1592 | struct cpswp_softc *psc; |
---|
1593 | struct mbuf *m, *m0, *mb_head, *mb_tail; |
---|
1594 | uint16_t m0_flags; |
---|
1595 | |
---|
1596 | nsegs = 0; |
---|
1597 | m0 = NULL; |
---|
1598 | last = NULL; |
---|
1599 | mb_head = NULL; |
---|
1600 | mb_tail = NULL; |
---|
1601 | removed = 0; |
---|
1602 | |
---|
1603 | /* Pull completed packets off hardware RX queue. */ |
---|
1604 | while ((slot = STAILQ_FIRST(&sc->rx.active)) != NULL) { |
---|
1605 | cpsw_cpdma_read_bd(sc, slot, &bd); |
---|
1606 | |
---|
1607 | /* |
---|
1608 | * Stop on packets still in use by hardware, but do not stop |
---|
1609 | * on packets with the teardown complete flag, they will be |
---|
1610 | * discarded later. |
---|
1611 | */ |
---|
1612 | if ((bd.flags & (CPDMA_BD_OWNER | CPDMA_BD_TDOWNCMPLT)) == |
---|
1613 | CPDMA_BD_OWNER) |
---|
1614 | break; |
---|
1615 | |
---|
1616 | last = slot; |
---|
1617 | ++removed; |
---|
1618 | STAILQ_REMOVE_HEAD(&sc->rx.active, next); |
---|
1619 | STAILQ_INSERT_TAIL(&sc->rx.avail, slot, next); |
---|
1620 | |
---|
1621 | bus_dmamap_sync(sc->mbuf_dtag, slot->dmamap, BUS_DMASYNC_POSTREAD); |
---|
1622 | bus_dmamap_unload(sc->mbuf_dtag, slot->dmamap); |
---|
1623 | |
---|
1624 | m = slot->mbuf; |
---|
1625 | slot->mbuf = NULL; |
---|
1626 | |
---|
1627 | if (bd.flags & CPDMA_BD_TDOWNCMPLT) { |
---|
1628 | CPSW_DEBUGF(sc, ("RX teardown is complete")); |
---|
1629 | m_freem(m); |
---|
1630 | sc->rx.running = 0; |
---|
1631 | sc->rx.teardown = 0; |
---|
1632 | break; |
---|
1633 | } |
---|
1634 | |
---|
1635 | port = (bd.flags & CPDMA_BD_PORT_MASK) - 1; |
---|
1636 | KASSERT(port >= 0 && port <= 1, |
---|
1637 | ("patcket received with invalid port: %d", port)); |
---|
1638 | psc = device_get_softc(sc->port[port].dev); |
---|
1639 | |
---|
1640 | /* Set up mbuf */ |
---|
1641 | m->m_data += bd.bufoff; |
---|
1642 | m->m_len = bd.buflen; |
---|
1643 | if (bd.flags & CPDMA_BD_SOP) { |
---|
1644 | m->m_pkthdr.len = bd.pktlen; |
---|
1645 | m->m_pkthdr.rcvif = psc->ifp; |
---|
1646 | m->m_flags |= M_PKTHDR; |
---|
1647 | m0_flags = bd.flags; |
---|
1648 | m0 = m; |
---|
1649 | } |
---|
1650 | nsegs++; |
---|
1651 | m->m_next = NULL; |
---|
1652 | m->m_nextpkt = NULL; |
---|
1653 | if (bd.flags & CPDMA_BD_EOP && m0 != NULL) { |
---|
1654 | if (m0_flags & CPDMA_BD_PASS_CRC) |
---|
1655 | m_adj(m0, -ETHER_CRC_LEN); |
---|
1656 | m0_flags = 0; |
---|
1657 | m0 = NULL; |
---|
1658 | if (nsegs > sc->rx.longest_chain) |
---|
1659 | sc->rx.longest_chain = nsegs; |
---|
1660 | nsegs = 0; |
---|
1661 | } |
---|
1662 | |
---|
1663 | if ((psc->ifp->if_capenable & IFCAP_RXCSUM) != 0) { |
---|
1664 | /* check for valid CRC by looking into pkt_err[5:4] */ |
---|
1665 | if ((bd.flags & |
---|
1666 | (CPDMA_BD_SOP | CPDMA_BD_PKT_ERR_MASK)) == |
---|
1667 | CPDMA_BD_SOP) { |
---|
1668 | m->m_pkthdr.csum_flags |= CSUM_IP_CHECKED; |
---|
1669 | m->m_pkthdr.csum_flags |= CSUM_IP_VALID; |
---|
1670 | m->m_pkthdr.csum_data = 0xffff; |
---|
1671 | } |
---|
1672 | } |
---|
1673 | |
---|
1674 | if (STAILQ_FIRST(&sc->rx.active) != NULL && |
---|
1675 | (bd.flags & (CPDMA_BD_EOP | CPDMA_BD_EOQ)) == |
---|
1676 | (CPDMA_BD_EOP | CPDMA_BD_EOQ)) { |
---|
1677 | cpsw_write_hdp_slot(sc, &sc->rx, |
---|
1678 | STAILQ_FIRST(&sc->rx.active)); |
---|
1679 | sc->rx.queue_restart++; |
---|
1680 | } |
---|
1681 | |
---|
1682 | /* Add mbuf to packet list to be returned. */ |
---|
1683 | if (mb_tail != NULL && (bd.flags & CPDMA_BD_SOP)) { |
---|
1684 | mb_tail->m_nextpkt = m; |
---|
1685 | } else if (mb_tail != NULL) { |
---|
1686 | mb_tail->m_next = m; |
---|
1687 | } else if (mb_tail == NULL && (bd.flags & CPDMA_BD_SOP) == 0) { |
---|
1688 | if (bootverbose) |
---|
1689 | printf( |
---|
1690 | "%s: %s: discanding fragment packet w/o header\n", |
---|
1691 | __func__, psc->ifp->if_xname); |
---|
1692 | m_freem(m); |
---|
1693 | continue; |
---|
1694 | } else { |
---|
1695 | mb_head = m; |
---|
1696 | } |
---|
1697 | mb_tail = m; |
---|
1698 | } |
---|
1699 | |
---|
1700 | if (removed != 0) { |
---|
1701 | cpsw_write_cp_slot(sc, &sc->rx, last); |
---|
1702 | sc->rx.queue_removes += removed; |
---|
1703 | sc->rx.avail_queue_len += removed; |
---|
1704 | sc->rx.active_queue_len -= removed; |
---|
1705 | if (sc->rx.avail_queue_len > sc->rx.max_avail_queue_len) |
---|
1706 | sc->rx.max_avail_queue_len = sc->rx.avail_queue_len; |
---|
1707 | CPSW_DEBUGF(sc, ("Removed %d received packet(s) from RX queue", removed)); |
---|
1708 | } |
---|
1709 | |
---|
1710 | return (mb_head); |
---|
1711 | } |
---|
1712 | |
---|
1713 | static void |
---|
1714 | cpsw_rx_enqueue(struct cpsw_softc *sc) |
---|
1715 | { |
---|
1716 | bus_dma_segment_t seg[1]; |
---|
1717 | struct cpsw_cpdma_bd bd; |
---|
1718 | struct cpsw_slot *first_new_slot, *last_old_slot, *next, *slot; |
---|
1719 | int error, nsegs, added = 0; |
---|
1720 | |
---|
1721 | /* Register new mbufs with hardware. */ |
---|
1722 | first_new_slot = NULL; |
---|
1723 | last_old_slot = STAILQ_LAST(&sc->rx.active, cpsw_slot, next); |
---|
1724 | while ((slot = STAILQ_FIRST(&sc->rx.avail)) != NULL) { |
---|
1725 | if (first_new_slot == NULL) |
---|
1726 | first_new_slot = slot; |
---|
1727 | if (slot->mbuf == NULL) { |
---|
1728 | slot->mbuf = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); |
---|
1729 | if (slot->mbuf == NULL) { |
---|
1730 | device_printf(sc->dev, |
---|
1731 | "Unable to fill RX queue\n"); |
---|
1732 | break; |
---|
1733 | } |
---|
1734 | slot->mbuf->m_len = |
---|
1735 | slot->mbuf->m_pkthdr.len = |
---|
1736 | slot->mbuf->m_ext.ext_size; |
---|
1737 | } |
---|
1738 | |
---|
1739 | error = bus_dmamap_load_mbuf_sg(sc->mbuf_dtag, slot->dmamap, |
---|
1740 | slot->mbuf, seg, &nsegs, BUS_DMA_NOWAIT); |
---|
1741 | |
---|
1742 | KASSERT(nsegs == 1, ("More than one segment (nsegs=%d)", nsegs)); |
---|
1743 | KASSERT(error == 0, ("DMA error (error=%d)", error)); |
---|
1744 | if (error != 0 || nsegs != 1) { |
---|
1745 | device_printf(sc->dev, |
---|
1746 | "%s: Can't prep RX buf for DMA (nsegs=%d, error=%d)\n", |
---|
1747 | __func__, nsegs, error); |
---|
1748 | bus_dmamap_unload(sc->mbuf_dtag, slot->dmamap); |
---|
1749 | m_freem(slot->mbuf); |
---|
1750 | slot->mbuf = NULL; |
---|
1751 | break; |
---|
1752 | } |
---|
1753 | |
---|
1754 | bus_dmamap_sync(sc->mbuf_dtag, slot->dmamap, BUS_DMASYNC_PREREAD); |
---|
1755 | |
---|
1756 | /* Create and submit new rx descriptor. */ |
---|
1757 | if ((next = STAILQ_NEXT(slot, next)) != NULL) |
---|
1758 | bd.next = cpsw_cpdma_bd_paddr(sc, next); |
---|
1759 | else |
---|
1760 | bd.next = 0; |
---|
1761 | bd.bufptr = seg->ds_addr; |
---|
1762 | bd.bufoff = 0; |
---|
1763 | bd.buflen = MCLBYTES - 1; |
---|
1764 | bd.pktlen = bd.buflen; |
---|
1765 | bd.flags = CPDMA_BD_OWNER; |
---|
1766 | cpsw_cpdma_write_bd(sc, slot, &bd); |
---|
1767 | ++added; |
---|
1768 | |
---|
1769 | STAILQ_REMOVE_HEAD(&sc->rx.avail, next); |
---|
1770 | STAILQ_INSERT_TAIL(&sc->rx.active, slot, next); |
---|
1771 | } |
---|
1772 | |
---|
1773 | if (added == 0 || first_new_slot == NULL) |
---|
1774 | return; |
---|
1775 | |
---|
1776 | CPSW_DEBUGF(sc, ("Adding %d buffers to RX queue", added)); |
---|
1777 | |
---|
1778 | /* Link new entries to hardware RX queue. */ |
---|
1779 | if (last_old_slot == NULL) { |
---|
1780 | /* Start a fresh queue. */ |
---|
1781 | cpsw_write_hdp_slot(sc, &sc->rx, first_new_slot); |
---|
1782 | } else { |
---|
1783 | /* Add buffers to end of current queue. */ |
---|
1784 | cpsw_cpdma_write_bd_next(sc, last_old_slot, first_new_slot); |
---|
1785 | } |
---|
1786 | sc->rx.queue_adds += added; |
---|
1787 | sc->rx.avail_queue_len -= added; |
---|
1788 | sc->rx.active_queue_len += added; |
---|
1789 | cpsw_write_4(sc, CPSW_CPDMA_RX_FREEBUFFER(0), added); |
---|
1790 | if (sc->rx.active_queue_len > sc->rx.max_active_queue_len) |
---|
1791 | sc->rx.max_active_queue_len = sc->rx.active_queue_len; |
---|
1792 | } |
---|
1793 | |
---|
1794 | static void |
---|
1795 | cpswp_start(struct ifnet *ifp) |
---|
1796 | { |
---|
1797 | struct cpswp_softc *sc; |
---|
1798 | |
---|
1799 | sc = ifp->if_softc; |
---|
1800 | if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0 || |
---|
1801 | sc->swsc->tx.running == 0) { |
---|
1802 | return; |
---|
1803 | } |
---|
1804 | CPSW_TX_LOCK(sc->swsc); |
---|
1805 | cpswp_tx_enqueue(sc); |
---|
1806 | cpsw_tx_dequeue(sc->swsc); |
---|
1807 | CPSW_TX_UNLOCK(sc->swsc); |
---|
1808 | } |
---|
1809 | |
---|
1810 | static void |
---|
1811 | cpsw_intr_tx(void *arg) |
---|
1812 | { |
---|
1813 | struct cpsw_softc *sc; |
---|
1814 | |
---|
1815 | sc = (struct cpsw_softc *)arg; |
---|
1816 | CPSW_TX_LOCK(sc); |
---|
1817 | if (cpsw_read_4(sc, CPSW_CPDMA_TX_CP(0)) == 0xfffffffc) |
---|
1818 | cpsw_write_cp(sc, &sc->tx, 0xfffffffc); |
---|
1819 | cpsw_tx_dequeue(sc); |
---|
1820 | cpsw_write_4(sc, CPSW_CPDMA_CPDMA_EOI_VECTOR, 2); |
---|
1821 | CPSW_TX_UNLOCK(sc); |
---|
1822 | } |
---|
1823 | |
---|
1824 | static void |
---|
1825 | cpswp_tx_enqueue(struct cpswp_softc *sc) |
---|
1826 | { |
---|
1827 | bus_dma_segment_t segs[CPSW_TXFRAGS]; |
---|
1828 | struct cpsw_cpdma_bd bd; |
---|
1829 | struct cpsw_slot *first_new_slot, *last, *last_old_slot, *next, *slot; |
---|
1830 | struct mbuf *m0; |
---|
1831 | int error, nsegs, seg, added = 0, padlen; |
---|
1832 | |
---|
1833 | /* Pull pending packets from IF queue and prep them for DMA. */ |
---|
1834 | last = NULL; |
---|
1835 | first_new_slot = NULL; |
---|
1836 | last_old_slot = STAILQ_LAST(&sc->swsc->tx.active, cpsw_slot, next); |
---|
1837 | while ((slot = STAILQ_FIRST(&sc->swsc->tx.avail)) != NULL) { |
---|
1838 | IF_DEQUEUE(&sc->ifp->if_snd, m0); |
---|
1839 | if (m0 == NULL) |
---|
1840 | break; |
---|
1841 | |
---|
1842 | slot->mbuf = m0; |
---|
1843 | padlen = ETHER_MIN_LEN - ETHER_CRC_LEN - m0->m_pkthdr.len; |
---|
1844 | if (padlen < 0) |
---|
1845 | padlen = 0; |
---|
1846 | else if (padlen > 0) |
---|
1847 | m_append(slot->mbuf, padlen, sc->swsc->nullpad); |
---|
1848 | |
---|
1849 | /* Create mapping in DMA memory */ |
---|
1850 | error = bus_dmamap_load_mbuf_sg(sc->swsc->mbuf_dtag, |
---|
1851 | slot->dmamap, slot->mbuf, segs, &nsegs, BUS_DMA_NOWAIT); |
---|
1852 | /* If the packet is too fragmented, try to simplify. */ |
---|
1853 | if (error == EFBIG || |
---|
1854 | (error == 0 && nsegs > sc->swsc->tx.avail_queue_len)) { |
---|
1855 | bus_dmamap_unload(sc->swsc->mbuf_dtag, slot->dmamap); |
---|
1856 | m0 = m_defrag(slot->mbuf, M_NOWAIT); |
---|
1857 | if (m0 == NULL) { |
---|
1858 | device_printf(sc->dev, |
---|
1859 | "Can't defragment packet; dropping\n"); |
---|
1860 | m_freem(slot->mbuf); |
---|
1861 | } else { |
---|
1862 | CPSW_DEBUGF(sc->swsc, |
---|
1863 | ("Requeueing defragmented packet")); |
---|
1864 | IF_PREPEND(&sc->ifp->if_snd, m0); |
---|
1865 | } |
---|
1866 | slot->mbuf = NULL; |
---|
1867 | continue; |
---|
1868 | } |
---|
1869 | if (error != 0) { |
---|
1870 | device_printf(sc->dev, |
---|
1871 | "%s: Can't setup DMA (error=%d), dropping packet\n", |
---|
1872 | __func__, error); |
---|
1873 | bus_dmamap_unload(sc->swsc->mbuf_dtag, slot->dmamap); |
---|
1874 | m_freem(slot->mbuf); |
---|
1875 | slot->mbuf = NULL; |
---|
1876 | break; |
---|
1877 | } |
---|
1878 | |
---|
1879 | bus_dmamap_sync(sc->swsc->mbuf_dtag, slot->dmamap, |
---|
1880 | BUS_DMASYNC_PREWRITE); |
---|
1881 | |
---|
1882 | CPSW_DEBUGF(sc->swsc, |
---|
1883 | ("Queueing TX packet: %d segments + %d pad bytes", |
---|
1884 | nsegs, padlen)); |
---|
1885 | |
---|
1886 | if (first_new_slot == NULL) |
---|
1887 | first_new_slot = slot; |
---|
1888 | |
---|
1889 | /* Link from the previous descriptor. */ |
---|
1890 | if (last != NULL) |
---|
1891 | cpsw_cpdma_write_bd_next(sc->swsc, last, slot); |
---|
1892 | |
---|
1893 | slot->ifp = sc->ifp; |
---|
1894 | |
---|
1895 | /* If there is only one segment, the for() loop |
---|
1896 | * gets skipped and the single buffer gets set up |
---|
1897 | * as both SOP and EOP. */ |
---|
1898 | if (nsegs > 1) { |
---|
1899 | next = STAILQ_NEXT(slot, next); |
---|
1900 | bd.next = cpsw_cpdma_bd_paddr(sc->swsc, next); |
---|
1901 | } else |
---|
1902 | bd.next = 0; |
---|
1903 | /* Start by setting up the first buffer. */ |
---|
1904 | bd.bufptr = segs[0].ds_addr; |
---|
1905 | bd.bufoff = 0; |
---|
1906 | bd.buflen = segs[0].ds_len; |
---|
1907 | bd.pktlen = m_length(slot->mbuf, NULL); |
---|
1908 | bd.flags = CPDMA_BD_SOP | CPDMA_BD_OWNER; |
---|
1909 | if (sc->swsc->dualemac) { |
---|
1910 | bd.flags |= CPDMA_BD_TO_PORT; |
---|
1911 | bd.flags |= ((sc->unit + 1) & CPDMA_BD_PORT_MASK); |
---|
1912 | } |
---|
1913 | for (seg = 1; seg < nsegs; ++seg) { |
---|
1914 | /* Save the previous buffer (which isn't EOP) */ |
---|
1915 | cpsw_cpdma_write_bd(sc->swsc, slot, &bd); |
---|
1916 | STAILQ_REMOVE_HEAD(&sc->swsc->tx.avail, next); |
---|
1917 | STAILQ_INSERT_TAIL(&sc->swsc->tx.active, slot, next); |
---|
1918 | slot = STAILQ_FIRST(&sc->swsc->tx.avail); |
---|
1919 | |
---|
1920 | /* Setup next buffer (which isn't SOP) */ |
---|
1921 | if (nsegs > seg + 1) { |
---|
1922 | next = STAILQ_NEXT(slot, next); |
---|
1923 | bd.next = cpsw_cpdma_bd_paddr(sc->swsc, next); |
---|
1924 | } else |
---|
1925 | bd.next = 0; |
---|
1926 | bd.bufptr = segs[seg].ds_addr; |
---|
1927 | bd.bufoff = 0; |
---|
1928 | bd.buflen = segs[seg].ds_len; |
---|
1929 | bd.pktlen = 0; |
---|
1930 | bd.flags = CPDMA_BD_OWNER; |
---|
1931 | } |
---|
1932 | |
---|
1933 | /* Save the final buffer. */ |
---|
1934 | bd.flags |= CPDMA_BD_EOP; |
---|
1935 | cpsw_cpdma_write_bd(sc->swsc, slot, &bd); |
---|
1936 | STAILQ_REMOVE_HEAD(&sc->swsc->tx.avail, next); |
---|
1937 | STAILQ_INSERT_TAIL(&sc->swsc->tx.active, slot, next); |
---|
1938 | |
---|
1939 | last = slot; |
---|
1940 | added += nsegs; |
---|
1941 | if (nsegs > sc->swsc->tx.longest_chain) |
---|
1942 | sc->swsc->tx.longest_chain = nsegs; |
---|
1943 | |
---|
1944 | BPF_MTAP(sc->ifp, m0); |
---|
1945 | } |
---|
1946 | |
---|
1947 | if (first_new_slot == NULL) |
---|
1948 | return; |
---|
1949 | |
---|
1950 | /* Attach the list of new buffers to the hardware TX queue. */ |
---|
1951 | if (last_old_slot != NULL && |
---|
1952 | (cpsw_cpdma_read_bd_flags(sc->swsc, last_old_slot) & |
---|
1953 | CPDMA_BD_EOQ) == 0) { |
---|
1954 | /* Add buffers to end of current queue. */ |
---|
1955 | cpsw_cpdma_write_bd_next(sc->swsc, last_old_slot, |
---|
1956 | first_new_slot); |
---|
1957 | } else { |
---|
1958 | /* Start a fresh queue. */ |
---|
1959 | cpsw_write_hdp_slot(sc->swsc, &sc->swsc->tx, first_new_slot); |
---|
1960 | } |
---|
1961 | sc->swsc->tx.queue_adds += added; |
---|
1962 | sc->swsc->tx.avail_queue_len -= added; |
---|
1963 | sc->swsc->tx.active_queue_len += added; |
---|
1964 | if (sc->swsc->tx.active_queue_len > sc->swsc->tx.max_active_queue_len) { |
---|
1965 | sc->swsc->tx.max_active_queue_len = sc->swsc->tx.active_queue_len; |
---|
1966 | } |
---|
1967 | CPSW_DEBUGF(sc->swsc, ("Queued %d TX packet(s)", added)); |
---|
1968 | } |
---|
1969 | |
---|
1970 | static int |
---|
1971 | cpsw_tx_dequeue(struct cpsw_softc *sc) |
---|
1972 | { |
---|
1973 | struct cpsw_slot *slot, *last_removed_slot = NULL; |
---|
1974 | struct cpsw_cpdma_bd bd; |
---|
1975 | uint32_t flags, removed = 0; |
---|
1976 | |
---|
1977 | /* Pull completed buffers off the hardware TX queue. */ |
---|
1978 | slot = STAILQ_FIRST(&sc->tx.active); |
---|
1979 | while (slot != NULL) { |
---|
1980 | flags = cpsw_cpdma_read_bd_flags(sc, slot); |
---|
1981 | |
---|
1982 | /* TearDown complete is only marked on the SOP for the packet. */ |
---|
1983 | if ((flags & (CPDMA_BD_SOP | CPDMA_BD_TDOWNCMPLT)) == |
---|
1984 | (CPDMA_BD_SOP | CPDMA_BD_TDOWNCMPLT)) { |
---|
1985 | sc->tx.teardown = 1; |
---|
1986 | } |
---|
1987 | |
---|
1988 | if ((flags & (CPDMA_BD_SOP | CPDMA_BD_OWNER)) == |
---|
1989 | (CPDMA_BD_SOP | CPDMA_BD_OWNER) && sc->tx.teardown == 0) |
---|
1990 | break; /* Hardware is still using this packet. */ |
---|
1991 | |
---|
1992 | bus_dmamap_sync(sc->mbuf_dtag, slot->dmamap, BUS_DMASYNC_POSTWRITE); |
---|
1993 | bus_dmamap_unload(sc->mbuf_dtag, slot->dmamap); |
---|
1994 | m_freem(slot->mbuf); |
---|
1995 | slot->mbuf = NULL; |
---|
1996 | |
---|
1997 | if (slot->ifp) { |
---|
1998 | if (sc->tx.teardown == 0) |
---|
1999 | if_inc_counter(slot->ifp, IFCOUNTER_OPACKETS, 1); |
---|
2000 | else |
---|
2001 | if_inc_counter(slot->ifp, IFCOUNTER_OQDROPS, 1); |
---|
2002 | } |
---|
2003 | |
---|
2004 | /* Dequeue any additional buffers used by this packet. */ |
---|
2005 | while (slot != NULL && slot->mbuf == NULL) { |
---|
2006 | STAILQ_REMOVE_HEAD(&sc->tx.active, next); |
---|
2007 | STAILQ_INSERT_TAIL(&sc->tx.avail, slot, next); |
---|
2008 | ++removed; |
---|
2009 | last_removed_slot = slot; |
---|
2010 | slot = STAILQ_FIRST(&sc->tx.active); |
---|
2011 | } |
---|
2012 | |
---|
2013 | cpsw_write_cp_slot(sc, &sc->tx, last_removed_slot); |
---|
2014 | |
---|
2015 | /* Restart the TX queue if necessary. */ |
---|
2016 | cpsw_cpdma_read_bd(sc, last_removed_slot, &bd); |
---|
2017 | if (slot != NULL && bd.next != 0 && (bd.flags & |
---|
2018 | (CPDMA_BD_EOP | CPDMA_BD_OWNER | CPDMA_BD_EOQ)) == |
---|
2019 | (CPDMA_BD_EOP | CPDMA_BD_EOQ)) { |
---|
2020 | cpsw_write_hdp_slot(sc, &sc->tx, slot); |
---|
2021 | sc->tx.queue_restart++; |
---|
2022 | break; |
---|
2023 | } |
---|
2024 | } |
---|
2025 | |
---|
2026 | if (removed != 0) { |
---|
2027 | sc->tx.queue_removes += removed; |
---|
2028 | sc->tx.active_queue_len -= removed; |
---|
2029 | sc->tx.avail_queue_len += removed; |
---|
2030 | if (sc->tx.avail_queue_len > sc->tx.max_avail_queue_len) |
---|
2031 | sc->tx.max_avail_queue_len = sc->tx.avail_queue_len; |
---|
2032 | CPSW_DEBUGF(sc, ("TX removed %d completed packet(s)", removed)); |
---|
2033 | } |
---|
2034 | |
---|
2035 | if (sc->tx.teardown && STAILQ_EMPTY(&sc->tx.active)) { |
---|
2036 | CPSW_DEBUGF(sc, ("TX teardown is complete")); |
---|
2037 | sc->tx.teardown = 0; |
---|
2038 | sc->tx.running = 0; |
---|
2039 | } |
---|
2040 | |
---|
2041 | return (removed); |
---|
2042 | } |
---|
2043 | |
---|
2044 | /* |
---|
2045 | * |
---|
2046 | * Miscellaneous interrupts. |
---|
2047 | * |
---|
2048 | */ |
---|
2049 | |
---|
2050 | static void |
---|
2051 | cpsw_intr_rx_thresh(void *arg) |
---|
2052 | { |
---|
2053 | struct cpsw_softc *sc; |
---|
2054 | struct ifnet *ifp; |
---|
2055 | struct mbuf *received, *next; |
---|
2056 | |
---|
2057 | sc = (struct cpsw_softc *)arg; |
---|
2058 | CPSW_RX_LOCK(sc); |
---|
2059 | received = cpsw_rx_dequeue(sc); |
---|
2060 | cpsw_rx_enqueue(sc); |
---|
2061 | cpsw_write_4(sc, CPSW_CPDMA_CPDMA_EOI_VECTOR, 0); |
---|
2062 | CPSW_RX_UNLOCK(sc); |
---|
2063 | |
---|
2064 | while (received != NULL) { |
---|
2065 | next = received->m_nextpkt; |
---|
2066 | received->m_nextpkt = NULL; |
---|
2067 | ifp = received->m_pkthdr.rcvif; |
---|
2068 | (*ifp->if_input)(ifp, received); |
---|
2069 | if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1); |
---|
2070 | received = next; |
---|
2071 | } |
---|
2072 | } |
---|
2073 | |
---|
2074 | static void |
---|
2075 | cpsw_intr_misc_host_error(struct cpsw_softc *sc) |
---|
2076 | { |
---|
2077 | uint32_t intstat; |
---|
2078 | uint32_t dmastat; |
---|
2079 | int txerr, rxerr, txchan, rxchan; |
---|
2080 | |
---|
2081 | printf("\n\n"); |
---|
2082 | device_printf(sc->dev, |
---|
2083 | "HOST ERROR: PROGRAMMING ERROR DETECTED BY HARDWARE\n"); |
---|
2084 | printf("\n\n"); |
---|
2085 | intstat = cpsw_read_4(sc, CPSW_CPDMA_DMA_INTSTAT_MASKED); |
---|
2086 | device_printf(sc->dev, "CPSW_CPDMA_DMA_INTSTAT_MASKED=0x%x\n", intstat); |
---|
2087 | dmastat = cpsw_read_4(sc, CPSW_CPDMA_DMASTATUS); |
---|
2088 | device_printf(sc->dev, "CPSW_CPDMA_DMASTATUS=0x%x\n", dmastat); |
---|
2089 | |
---|
2090 | txerr = (dmastat >> 20) & 15; |
---|
2091 | txchan = (dmastat >> 16) & 7; |
---|
2092 | rxerr = (dmastat >> 12) & 15; |
---|
2093 | rxchan = (dmastat >> 8) & 7; |
---|
2094 | |
---|
2095 | switch (txerr) { |
---|
2096 | case 0: break; |
---|
2097 | case 1: printf("SOP error on TX channel %d\n", txchan); |
---|
2098 | break; |
---|
2099 | case 2: printf("Ownership bit not set on SOP buffer on TX channel %d\n", txchan); |
---|
2100 | break; |
---|
2101 | case 3: printf("Zero Next Buffer but not EOP on TX channel %d\n", txchan); |
---|
2102 | break; |
---|
2103 | case 4: printf("Zero Buffer Pointer on TX channel %d\n", txchan); |
---|
2104 | break; |
---|
2105 | case 5: printf("Zero Buffer Length on TX channel %d\n", txchan); |
---|
2106 | break; |
---|
2107 | case 6: printf("Packet length error on TX channel %d\n", txchan); |
---|
2108 | break; |
---|
2109 | default: printf("Unknown error on TX channel %d\n", txchan); |
---|
2110 | break; |
---|
2111 | } |
---|
2112 | |
---|
2113 | if (txerr != 0) { |
---|
2114 | printf("CPSW_CPDMA_TX%d_HDP=0x%x\n", |
---|
2115 | txchan, cpsw_read_4(sc, CPSW_CPDMA_TX_HDP(txchan))); |
---|
2116 | printf("CPSW_CPDMA_TX%d_CP=0x%x\n", |
---|
2117 | txchan, cpsw_read_4(sc, CPSW_CPDMA_TX_CP(txchan))); |
---|
2118 | cpsw_dump_queue(sc, &sc->tx.active); |
---|
2119 | } |
---|
2120 | |
---|
2121 | switch (rxerr) { |
---|
2122 | case 0: break; |
---|
2123 | case 2: printf("Ownership bit not set on RX channel %d\n", rxchan); |
---|
2124 | break; |
---|
2125 | case 4: printf("Zero Buffer Pointer on RX channel %d\n", rxchan); |
---|
2126 | break; |
---|
2127 | case 5: printf("Zero Buffer Length on RX channel %d\n", rxchan); |
---|
2128 | break; |
---|
2129 | case 6: printf("Buffer offset too big on RX channel %d\n", rxchan); |
---|
2130 | break; |
---|
2131 | default: printf("Unknown RX error on RX channel %d\n", rxchan); |
---|
2132 | break; |
---|
2133 | } |
---|
2134 | |
---|
2135 | if (rxerr != 0) { |
---|
2136 | printf("CPSW_CPDMA_RX%d_HDP=0x%x\n", |
---|
2137 | rxchan, cpsw_read_4(sc,CPSW_CPDMA_RX_HDP(rxchan))); |
---|
2138 | printf("CPSW_CPDMA_RX%d_CP=0x%x\n", |
---|
2139 | rxchan, cpsw_read_4(sc, CPSW_CPDMA_RX_CP(rxchan))); |
---|
2140 | cpsw_dump_queue(sc, &sc->rx.active); |
---|
2141 | } |
---|
2142 | |
---|
2143 | printf("\nALE Table\n"); |
---|
2144 | cpsw_ale_dump_table(sc); |
---|
2145 | |
---|
2146 | // XXX do something useful here?? |
---|
2147 | panic("CPSW HOST ERROR INTERRUPT"); |
---|
2148 | |
---|
2149 | // Suppress this interrupt in the future. |
---|
2150 | cpsw_write_4(sc, CPSW_CPDMA_DMA_INTMASK_CLEAR, intstat); |
---|
2151 | printf("XXX HOST ERROR INTERRUPT SUPPRESSED\n"); |
---|
2152 | // The watchdog will probably reset the controller |
---|
2153 | // in a little while. It will probably fail again. |
---|
2154 | } |
---|
2155 | |
---|
2156 | static void |
---|
2157 | cpsw_intr_misc(void *arg) |
---|
2158 | { |
---|
2159 | struct cpsw_softc *sc = arg; |
---|
2160 | uint32_t stat = cpsw_read_4(sc, CPSW_WR_C_MISC_STAT(0)); |
---|
2161 | |
---|
2162 | if (stat & CPSW_WR_C_MISC_EVNT_PEND) |
---|
2163 | CPSW_DEBUGF(sc, ("Time sync event interrupt unimplemented")); |
---|
2164 | if (stat & CPSW_WR_C_MISC_STAT_PEND) |
---|
2165 | cpsw_stats_collect(sc); |
---|
2166 | if (stat & CPSW_WR_C_MISC_HOST_PEND) |
---|
2167 | cpsw_intr_misc_host_error(sc); |
---|
2168 | if (stat & CPSW_WR_C_MISC_MDIOLINK) { |
---|
2169 | cpsw_write_4(sc, MDIOLINKINTMASKED, |
---|
2170 | cpsw_read_4(sc, MDIOLINKINTMASKED)); |
---|
2171 | } |
---|
2172 | if (stat & CPSW_WR_C_MISC_MDIOUSER) { |
---|
2173 | CPSW_DEBUGF(sc, |
---|
2174 | ("MDIO operation completed interrupt unimplemented")); |
---|
2175 | } |
---|
2176 | cpsw_write_4(sc, CPSW_CPDMA_CPDMA_EOI_VECTOR, 3); |
---|
2177 | } |
---|
2178 | |
---|
2179 | /* |
---|
2180 | * |
---|
2181 | * Periodic Checks and Watchdog. |
---|
2182 | * |
---|
2183 | */ |
---|
2184 | |
---|
2185 | static void |
---|
2186 | cpswp_tick(void *msc) |
---|
2187 | { |
---|
2188 | struct cpswp_softc *sc = msc; |
---|
2189 | |
---|
2190 | /* Check for media type change */ |
---|
2191 | mii_tick(sc->mii); |
---|
2192 | if (sc->media_status != sc->mii->mii_media.ifm_media) { |
---|
2193 | printf("%s: media type changed (ifm_media=%x)\n", __func__, |
---|
2194 | sc->mii->mii_media.ifm_media); |
---|
2195 | cpswp_ifmedia_upd(sc->ifp); |
---|
2196 | } |
---|
2197 | |
---|
2198 | /* Schedule another timeout one second from now */ |
---|
2199 | callout_reset(&sc->mii_callout, hz, cpswp_tick, sc); |
---|
2200 | } |
---|
2201 | |
---|
2202 | static void |
---|
2203 | cpswp_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr) |
---|
2204 | { |
---|
2205 | struct cpswp_softc *sc; |
---|
2206 | struct mii_data *mii; |
---|
2207 | |
---|
2208 | sc = ifp->if_softc; |
---|
2209 | CPSW_DEBUGF(sc->swsc, ("")); |
---|
2210 | CPSW_PORT_LOCK(sc); |
---|
2211 | |
---|
2212 | mii = sc->mii; |
---|
2213 | mii_pollstat(mii); |
---|
2214 | |
---|
2215 | ifmr->ifm_active = mii->mii_media_active; |
---|
2216 | ifmr->ifm_status = mii->mii_media_status; |
---|
2217 | CPSW_PORT_UNLOCK(sc); |
---|
2218 | } |
---|
2219 | |
---|
2220 | static int |
---|
2221 | cpswp_ifmedia_upd(struct ifnet *ifp) |
---|
2222 | { |
---|
2223 | struct cpswp_softc *sc; |
---|
2224 | |
---|
2225 | sc = ifp->if_softc; |
---|
2226 | CPSW_DEBUGF(sc->swsc, ("")); |
---|
2227 | CPSW_PORT_LOCK(sc); |
---|
2228 | mii_mediachg(sc->mii); |
---|
2229 | sc->media_status = sc->mii->mii_media.ifm_media; |
---|
2230 | CPSW_PORT_UNLOCK(sc); |
---|
2231 | |
---|
2232 | return (0); |
---|
2233 | } |
---|
2234 | |
---|
2235 | static void |
---|
2236 | cpsw_tx_watchdog_full_reset(struct cpsw_softc *sc) |
---|
2237 | { |
---|
2238 | struct cpswp_softc *psc; |
---|
2239 | int i; |
---|
2240 | |
---|
2241 | cpsw_debugf_head("CPSW watchdog"); |
---|
2242 | device_printf(sc->dev, "watchdog timeout\n"); |
---|
2243 | printf("CPSW_CPDMA_TX%d_HDP=0x%x\n", 0, |
---|
2244 | cpsw_read_4(sc, CPSW_CPDMA_TX_HDP(0))); |
---|
2245 | printf("CPSW_CPDMA_TX%d_CP=0x%x\n", 0, |
---|
2246 | cpsw_read_4(sc, CPSW_CPDMA_TX_CP(0))); |
---|
2247 | cpsw_dump_queue(sc, &sc->tx.active); |
---|
2248 | for (i = 0; i < CPSW_PORTS; i++) { |
---|
2249 | if (!sc->dualemac && i != sc->active_slave) |
---|
2250 | continue; |
---|
2251 | psc = device_get_softc(sc->port[i].dev); |
---|
2252 | CPSW_PORT_LOCK(psc); |
---|
2253 | cpswp_stop_locked(psc); |
---|
2254 | CPSW_PORT_UNLOCK(psc); |
---|
2255 | } |
---|
2256 | } |
---|
2257 | |
---|
2258 | static void |
---|
2259 | cpsw_tx_watchdog(void *msc) |
---|
2260 | { |
---|
2261 | struct cpsw_softc *sc; |
---|
2262 | |
---|
2263 | sc = msc; |
---|
2264 | CPSW_TX_LOCK(sc); |
---|
2265 | if (sc->tx.active_queue_len == 0 || !sc->tx.running) { |
---|
2266 | sc->watchdog.timer = 0; /* Nothing to do. */ |
---|
2267 | } else if (sc->tx.queue_removes > sc->tx.queue_removes_at_last_tick) { |
---|
2268 | sc->watchdog.timer = 0; /* Stuff done while we weren't looking. */ |
---|
2269 | } else if (cpsw_tx_dequeue(sc) > 0) { |
---|
2270 | sc->watchdog.timer = 0; /* We just did something. */ |
---|
2271 | } else { |
---|
2272 | /* There was something to do but it didn't get done. */ |
---|
2273 | ++sc->watchdog.timer; |
---|
2274 | if (sc->watchdog.timer > 5) { |
---|
2275 | sc->watchdog.timer = 0; |
---|
2276 | ++sc->watchdog.resets; |
---|
2277 | cpsw_tx_watchdog_full_reset(sc); |
---|
2278 | } |
---|
2279 | } |
---|
2280 | sc->tx.queue_removes_at_last_tick = sc->tx.queue_removes; |
---|
2281 | CPSW_TX_UNLOCK(sc); |
---|
2282 | |
---|
2283 | /* Schedule another timeout one second from now */ |
---|
2284 | callout_reset(&sc->watchdog.callout, hz, cpsw_tx_watchdog, sc); |
---|
2285 | } |
---|
2286 | |
---|
2287 | /* |
---|
2288 | * |
---|
2289 | * ALE support routines. |
---|
2290 | * |
---|
2291 | */ |
---|
2292 | |
---|
2293 | static void |
---|
2294 | cpsw_ale_read_entry(struct cpsw_softc *sc, uint16_t idx, uint32_t *ale_entry) |
---|
2295 | { |
---|
2296 | cpsw_write_4(sc, CPSW_ALE_TBLCTL, idx & 1023); |
---|
2297 | ale_entry[0] = cpsw_read_4(sc, CPSW_ALE_TBLW0); |
---|
2298 | ale_entry[1] = cpsw_read_4(sc, CPSW_ALE_TBLW1); |
---|
2299 | ale_entry[2] = cpsw_read_4(sc, CPSW_ALE_TBLW2); |
---|
2300 | } |
---|
2301 | |
---|
2302 | static void |
---|
2303 | cpsw_ale_write_entry(struct cpsw_softc *sc, uint16_t idx, uint32_t *ale_entry) |
---|
2304 | { |
---|
2305 | cpsw_write_4(sc, CPSW_ALE_TBLW0, ale_entry[0]); |
---|
2306 | cpsw_write_4(sc, CPSW_ALE_TBLW1, ale_entry[1]); |
---|
2307 | cpsw_write_4(sc, CPSW_ALE_TBLW2, ale_entry[2]); |
---|
2308 | cpsw_write_4(sc, CPSW_ALE_TBLCTL, 1 << 31 | (idx & 1023)); |
---|
2309 | } |
---|
2310 | |
---|
2311 | static void |
---|
2312 | cpsw_ale_remove_all_mc_entries(struct cpsw_softc *sc) |
---|
2313 | { |
---|
2314 | int i; |
---|
2315 | uint32_t ale_entry[3]; |
---|
2316 | |
---|
2317 | /* First four entries are link address and broadcast. */ |
---|
2318 | for (i = 10; i < CPSW_MAX_ALE_ENTRIES; i++) { |
---|
2319 | cpsw_ale_read_entry(sc, i, ale_entry); |
---|
2320 | if ((ALE_TYPE(ale_entry) == ALE_TYPE_ADDR || |
---|
2321 | ALE_TYPE(ale_entry) == ALE_TYPE_VLAN_ADDR) && |
---|
2322 | ALE_MCAST(ale_entry) == 1) { /* MCast link addr */ |
---|
2323 | ale_entry[0] = ale_entry[1] = ale_entry[2] = 0; |
---|
2324 | cpsw_ale_write_entry(sc, i, ale_entry); |
---|
2325 | } |
---|
2326 | } |
---|
2327 | } |
---|
2328 | |
---|
2329 | static int |
---|
2330 | cpsw_ale_mc_entry_set(struct cpsw_softc *sc, uint8_t portmap, int vlan, |
---|
2331 | uint8_t *mac) |
---|
2332 | { |
---|
2333 | int free_index = -1, matching_index = -1, i; |
---|
2334 | uint32_t ale_entry[3], ale_type; |
---|
2335 | |
---|
2336 | /* Find a matching entry or a free entry. */ |
---|
2337 | for (i = 10; i < CPSW_MAX_ALE_ENTRIES; i++) { |
---|
2338 | cpsw_ale_read_entry(sc, i, ale_entry); |
---|
2339 | |
---|
2340 | /* Entry Type[61:60] is 0 for free entry */ |
---|
2341 | if (free_index < 0 && ALE_TYPE(ale_entry) == 0) |
---|
2342 | free_index = i; |
---|
2343 | |
---|
2344 | if ((((ale_entry[1] >> 8) & 0xFF) == mac[0]) && |
---|
2345 | (((ale_entry[1] >> 0) & 0xFF) == mac[1]) && |
---|
2346 | (((ale_entry[0] >>24) & 0xFF) == mac[2]) && |
---|
2347 | (((ale_entry[0] >>16) & 0xFF) == mac[3]) && |
---|
2348 | (((ale_entry[0] >> 8) & 0xFF) == mac[4]) && |
---|
2349 | (((ale_entry[0] >> 0) & 0xFF) == mac[5])) { |
---|
2350 | matching_index = i; |
---|
2351 | break; |
---|
2352 | } |
---|
2353 | } |
---|
2354 | |
---|
2355 | if (matching_index < 0) { |
---|
2356 | if (free_index < 0) |
---|
2357 | return (ENOMEM); |
---|
2358 | i = free_index; |
---|
2359 | } |
---|
2360 | |
---|
2361 | if (vlan != -1) |
---|
2362 | ale_type = ALE_TYPE_VLAN_ADDR << 28 | vlan << 16; |
---|
2363 | else |
---|
2364 | ale_type = ALE_TYPE_ADDR << 28; |
---|
2365 | |
---|
2366 | /* Set MAC address */ |
---|
2367 | ale_entry[0] = mac[2] << 24 | mac[3] << 16 | mac[4] << 8 | mac[5]; |
---|
2368 | ale_entry[1] = mac[0] << 8 | mac[1]; |
---|
2369 | |
---|
2370 | /* Entry type[61:60] and Mcast fwd state[63:62] is fw(3). */ |
---|
2371 | ale_entry[1] |= ALE_MCAST_FWD | ale_type; |
---|
2372 | |
---|
2373 | /* Set portmask [68:66] */ |
---|
2374 | ale_entry[2] = (portmap & 7) << 2; |
---|
2375 | |
---|
2376 | cpsw_ale_write_entry(sc, i, ale_entry); |
---|
2377 | |
---|
2378 | return 0; |
---|
2379 | } |
---|
2380 | |
---|
2381 | static void |
---|
2382 | cpsw_ale_dump_table(struct cpsw_softc *sc) { |
---|
2383 | int i; |
---|
2384 | uint32_t ale_entry[3]; |
---|
2385 | for (i = 0; i < CPSW_MAX_ALE_ENTRIES; i++) { |
---|
2386 | cpsw_ale_read_entry(sc, i, ale_entry); |
---|
2387 | switch (ALE_TYPE(ale_entry)) { |
---|
2388 | case ALE_TYPE_VLAN: |
---|
2389 | printf("ALE[%4u] %08x %08x %08x ", i, ale_entry[2], |
---|
2390 | ale_entry[1], ale_entry[0]); |
---|
2391 | printf("type: %u ", ALE_TYPE(ale_entry)); |
---|
2392 | printf("vlan: %u ", ALE_VLAN(ale_entry)); |
---|
2393 | printf("untag: %u ", ALE_VLAN_UNTAG(ale_entry)); |
---|
2394 | printf("reg flood: %u ", ALE_VLAN_REGFLOOD(ale_entry)); |
---|
2395 | printf("unreg flood: %u ", ALE_VLAN_UNREGFLOOD(ale_entry)); |
---|
2396 | printf("members: %u ", ALE_VLAN_MEMBERS(ale_entry)); |
---|
2397 | printf("\n"); |
---|
2398 | break; |
---|
2399 | case ALE_TYPE_ADDR: |
---|
2400 | case ALE_TYPE_VLAN_ADDR: |
---|
2401 | printf("ALE[%4u] %08x %08x %08x ", i, ale_entry[2], |
---|
2402 | ale_entry[1], ale_entry[0]); |
---|
2403 | printf("type: %u ", ALE_TYPE(ale_entry)); |
---|
2404 | printf("mac: %02x:%02x:%02x:%02x:%02x:%02x ", |
---|
2405 | (ale_entry[1] >> 8) & 0xFF, |
---|
2406 | (ale_entry[1] >> 0) & 0xFF, |
---|
2407 | (ale_entry[0] >>24) & 0xFF, |
---|
2408 | (ale_entry[0] >>16) & 0xFF, |
---|
2409 | (ale_entry[0] >> 8) & 0xFF, |
---|
2410 | (ale_entry[0] >> 0) & 0xFF); |
---|
2411 | printf(ALE_MCAST(ale_entry) ? "mcast " : "ucast "); |
---|
2412 | if (ALE_TYPE(ale_entry) == ALE_TYPE_VLAN_ADDR) |
---|
2413 | printf("vlan: %u ", ALE_VLAN(ale_entry)); |
---|
2414 | printf("port: %u ", ALE_PORTS(ale_entry)); |
---|
2415 | printf("\n"); |
---|
2416 | break; |
---|
2417 | } |
---|
2418 | } |
---|
2419 | printf("\n"); |
---|
2420 | } |
---|
2421 | |
---|
2422 | static int |
---|
2423 | cpswp_ale_update_addresses(struct cpswp_softc *sc, int purge) |
---|
2424 | { |
---|
2425 | uint8_t *mac; |
---|
2426 | uint32_t ale_entry[3], ale_type, portmask; |
---|
2427 | struct ifmultiaddr *ifma; |
---|
2428 | |
---|
2429 | if (sc->swsc->dualemac) { |
---|
2430 | ale_type = ALE_TYPE_VLAN_ADDR << 28 | sc->vlan << 16; |
---|
2431 | portmask = 1 << (sc->unit + 1) | 1 << 0; |
---|
2432 | } else { |
---|
2433 | ale_type = ALE_TYPE_ADDR << 28; |
---|
2434 | portmask = 7; |
---|
2435 | } |
---|
2436 | |
---|
2437 | /* |
---|
2438 | * Route incoming packets for our MAC address to Port 0 (host). |
---|
2439 | * For simplicity, keep this entry at table index 0 for port 1 and |
---|
2440 | * at index 2 for port 2 in the ALE. |
---|
2441 | */ |
---|
2442 | if_addr_rlock(sc->ifp); |
---|
2443 | mac = LLADDR((struct sockaddr_dl *)sc->ifp->if_addr->ifa_addr); |
---|
2444 | ale_entry[0] = mac[2] << 24 | mac[3] << 16 | mac[4] << 8 | mac[5]; |
---|
2445 | ale_entry[1] = ale_type | mac[0] << 8 | mac[1]; /* addr entry + mac */ |
---|
2446 | ale_entry[2] = 0; /* port = 0 */ |
---|
2447 | cpsw_ale_write_entry(sc->swsc, 0 + 2 * sc->unit, ale_entry); |
---|
2448 | |
---|
2449 | /* Set outgoing MAC Address for slave port. */ |
---|
2450 | cpsw_write_4(sc->swsc, CPSW_PORT_P_SA_HI(sc->unit + 1), |
---|
2451 | mac[3] << 24 | mac[2] << 16 | mac[1] << 8 | mac[0]); |
---|
2452 | cpsw_write_4(sc->swsc, CPSW_PORT_P_SA_LO(sc->unit + 1), |
---|
2453 | mac[5] << 8 | mac[4]); |
---|
2454 | if_addr_runlock(sc->ifp); |
---|
2455 | |
---|
2456 | /* Keep the broadcast address at table entry 1 (or 3). */ |
---|
2457 | ale_entry[0] = 0xffffffff; /* Lower 32 bits of MAC */ |
---|
2458 | /* ALE_MCAST_FWD, Addr type, upper 16 bits of Mac */ |
---|
2459 | ale_entry[1] = ALE_MCAST_FWD | ale_type | 0xffff; |
---|
2460 | ale_entry[2] = portmask << 2; |
---|
2461 | cpsw_ale_write_entry(sc->swsc, 1 + 2 * sc->unit, ale_entry); |
---|
2462 | |
---|
2463 | /* SIOCDELMULTI doesn't specify the particular address |
---|
2464 | being removed, so we have to remove all and rebuild. */ |
---|
2465 | if (purge) |
---|
2466 | cpsw_ale_remove_all_mc_entries(sc->swsc); |
---|
2467 | |
---|
2468 | /* Set other multicast addrs desired. */ |
---|
2469 | if_maddr_rlock(sc->ifp); |
---|
2470 | CK_STAILQ_FOREACH(ifma, &sc->ifp->if_multiaddrs, ifma_link) { |
---|
2471 | if (ifma->ifma_addr->sa_family != AF_LINK) |
---|
2472 | continue; |
---|
2473 | cpsw_ale_mc_entry_set(sc->swsc, portmask, sc->vlan, |
---|
2474 | LLADDR((struct sockaddr_dl *)ifma->ifma_addr)); |
---|
2475 | } |
---|
2476 | if_maddr_runlock(sc->ifp); |
---|
2477 | |
---|
2478 | return (0); |
---|
2479 | } |
---|
2480 | |
---|
2481 | static int |
---|
2482 | cpsw_ale_update_vlan_table(struct cpsw_softc *sc, int vlan, int ports, |
---|
2483 | int untag, int mcregflood, int mcunregflood) |
---|
2484 | { |
---|
2485 | int free_index, i, matching_index; |
---|
2486 | uint32_t ale_entry[3]; |
---|
2487 | |
---|
2488 | free_index = matching_index = -1; |
---|
2489 | /* Find a matching entry or a free entry. */ |
---|
2490 | for (i = 5; i < CPSW_MAX_ALE_ENTRIES; i++) { |
---|
2491 | cpsw_ale_read_entry(sc, i, ale_entry); |
---|
2492 | |
---|
2493 | /* Entry Type[61:60] is 0 for free entry */ |
---|
2494 | if (free_index < 0 && ALE_TYPE(ale_entry) == 0) |
---|
2495 | free_index = i; |
---|
2496 | |
---|
2497 | if (ALE_VLAN(ale_entry) == vlan) { |
---|
2498 | matching_index = i; |
---|
2499 | break; |
---|
2500 | } |
---|
2501 | } |
---|
2502 | |
---|
2503 | if (matching_index < 0) { |
---|
2504 | if (free_index < 0) |
---|
2505 | return (-1); |
---|
2506 | i = free_index; |
---|
2507 | } |
---|
2508 | |
---|
2509 | ale_entry[0] = (untag & 7) << 24 | (mcregflood & 7) << 16 | |
---|
2510 | (mcunregflood & 7) << 8 | (ports & 7); |
---|
2511 | ale_entry[1] = ALE_TYPE_VLAN << 28 | vlan << 16; |
---|
2512 | ale_entry[2] = 0; |
---|
2513 | cpsw_ale_write_entry(sc, i, ale_entry); |
---|
2514 | |
---|
2515 | return (0); |
---|
2516 | } |
---|
2517 | |
---|
2518 | /* |
---|
2519 | * |
---|
2520 | * Statistics and Sysctls. |
---|
2521 | * |
---|
2522 | */ |
---|
2523 | |
---|
2524 | #if 0 |
---|
2525 | static void |
---|
2526 | cpsw_stats_dump(struct cpsw_softc *sc) |
---|
2527 | { |
---|
2528 | int i; |
---|
2529 | uint32_t r; |
---|
2530 | |
---|
2531 | for (i = 0; i < CPSW_SYSCTL_COUNT; ++i) { |
---|
2532 | r = cpsw_read_4(sc, CPSW_STATS_OFFSET + |
---|
2533 | cpsw_stat_sysctls[i].reg); |
---|
2534 | CPSW_DEBUGF(sc, ("%s: %ju + %u = %ju", cpsw_stat_sysctls[i].oid, |
---|
2535 | (intmax_t)sc->shadow_stats[i], r, |
---|
2536 | (intmax_t)sc->shadow_stats[i] + r)); |
---|
2537 | } |
---|
2538 | } |
---|
2539 | #endif |
---|
2540 | |
---|
2541 | static void |
---|
2542 | cpsw_stats_collect(struct cpsw_softc *sc) |
---|
2543 | { |
---|
2544 | int i; |
---|
2545 | uint32_t r; |
---|
2546 | |
---|
2547 | CPSW_DEBUGF(sc, ("Controller shadow statistics updated.")); |
---|
2548 | |
---|
2549 | for (i = 0; i < CPSW_SYSCTL_COUNT; ++i) { |
---|
2550 | r = cpsw_read_4(sc, CPSW_STATS_OFFSET + |
---|
2551 | cpsw_stat_sysctls[i].reg); |
---|
2552 | sc->shadow_stats[i] += r; |
---|
2553 | cpsw_write_4(sc, CPSW_STATS_OFFSET + cpsw_stat_sysctls[i].reg, |
---|
2554 | r); |
---|
2555 | } |
---|
2556 | } |
---|
2557 | |
---|
2558 | static int |
---|
2559 | cpsw_stats_sysctl(SYSCTL_HANDLER_ARGS) |
---|
2560 | { |
---|
2561 | struct cpsw_softc *sc; |
---|
2562 | struct cpsw_stat *stat; |
---|
2563 | uint64_t result; |
---|
2564 | |
---|
2565 | sc = (struct cpsw_softc *)arg1; |
---|
2566 | stat = &cpsw_stat_sysctls[oidp->oid_number]; |
---|
2567 | result = sc->shadow_stats[oidp->oid_number]; |
---|
2568 | result += cpsw_read_4(sc, CPSW_STATS_OFFSET + stat->reg); |
---|
2569 | return (sysctl_handle_64(oidp, &result, 0, req)); |
---|
2570 | } |
---|
2571 | |
---|
2572 | static int |
---|
2573 | cpsw_stat_attached(SYSCTL_HANDLER_ARGS) |
---|
2574 | { |
---|
2575 | struct cpsw_softc *sc; |
---|
2576 | struct bintime t; |
---|
2577 | unsigned result; |
---|
2578 | |
---|
2579 | sc = (struct cpsw_softc *)arg1; |
---|
2580 | getbinuptime(&t); |
---|
2581 | bintime_sub(&t, &sc->attach_uptime); |
---|
2582 | result = t.sec; |
---|
2583 | return (sysctl_handle_int(oidp, &result, 0, req)); |
---|
2584 | } |
---|
2585 | |
---|
2586 | static int |
---|
2587 | cpsw_intr_coalesce(SYSCTL_HANDLER_ARGS) |
---|
2588 | { |
---|
2589 | int error; |
---|
2590 | struct cpsw_softc *sc; |
---|
2591 | uint32_t ctrl, intr_per_ms; |
---|
2592 | |
---|
2593 | sc = (struct cpsw_softc *)arg1; |
---|
2594 | error = sysctl_handle_int(oidp, &sc->coal_us, 0, req); |
---|
2595 | if (error != 0 || req->newptr == NULL) |
---|
2596 | return (error); |
---|
2597 | |
---|
2598 | ctrl = cpsw_read_4(sc, CPSW_WR_INT_CONTROL); |
---|
2599 | ctrl &= ~(CPSW_WR_INT_PACE_EN | CPSW_WR_INT_PRESCALE_MASK); |
---|
2600 | if (sc->coal_us == 0) { |
---|
2601 | /* Disable the interrupt pace hardware. */ |
---|
2602 | cpsw_write_4(sc, CPSW_WR_INT_CONTROL, ctrl); |
---|
2603 | cpsw_write_4(sc, CPSW_WR_C_RX_IMAX(0), 0); |
---|
2604 | cpsw_write_4(sc, CPSW_WR_C_TX_IMAX(0), 0); |
---|
2605 | return (0); |
---|
2606 | } |
---|
2607 | |
---|
2608 | if (sc->coal_us > CPSW_WR_C_IMAX_US_MAX) |
---|
2609 | sc->coal_us = CPSW_WR_C_IMAX_US_MAX; |
---|
2610 | if (sc->coal_us < CPSW_WR_C_IMAX_US_MIN) |
---|
2611 | sc->coal_us = CPSW_WR_C_IMAX_US_MIN; |
---|
2612 | intr_per_ms = 1000 / sc->coal_us; |
---|
2613 | /* Just to make sure... */ |
---|
2614 | if (intr_per_ms > CPSW_WR_C_IMAX_MAX) |
---|
2615 | intr_per_ms = CPSW_WR_C_IMAX_MAX; |
---|
2616 | if (intr_per_ms < CPSW_WR_C_IMAX_MIN) |
---|
2617 | intr_per_ms = CPSW_WR_C_IMAX_MIN; |
---|
2618 | |
---|
2619 | /* Set the prescale to produce 4us pulses from the 125 Mhz clock. */ |
---|
2620 | ctrl |= (125 * 4) & CPSW_WR_INT_PRESCALE_MASK; |
---|
2621 | |
---|
2622 | /* Enable the interrupt pace hardware. */ |
---|
2623 | cpsw_write_4(sc, CPSW_WR_C_RX_IMAX(0), intr_per_ms); |
---|
2624 | cpsw_write_4(sc, CPSW_WR_C_TX_IMAX(0), intr_per_ms); |
---|
2625 | ctrl |= CPSW_WR_INT_C0_RX_PULSE | CPSW_WR_INT_C0_TX_PULSE; |
---|
2626 | cpsw_write_4(sc, CPSW_WR_INT_CONTROL, ctrl); |
---|
2627 | |
---|
2628 | return (0); |
---|
2629 | } |
---|
2630 | |
---|
2631 | static int |
---|
2632 | cpsw_stat_uptime(SYSCTL_HANDLER_ARGS) |
---|
2633 | { |
---|
2634 | struct cpsw_softc *swsc; |
---|
2635 | struct cpswp_softc *sc; |
---|
2636 | struct bintime t; |
---|
2637 | unsigned result; |
---|
2638 | |
---|
2639 | swsc = arg1; |
---|
2640 | sc = device_get_softc(swsc->port[arg2].dev); |
---|
2641 | if (sc->ifp->if_drv_flags & IFF_DRV_RUNNING) { |
---|
2642 | getbinuptime(&t); |
---|
2643 | bintime_sub(&t, &sc->init_uptime); |
---|
2644 | result = t.sec; |
---|
2645 | } else |
---|
2646 | result = 0; |
---|
2647 | return (sysctl_handle_int(oidp, &result, 0, req)); |
---|
2648 | } |
---|
2649 | |
---|
2650 | static void |
---|
2651 | cpsw_add_queue_sysctls(struct sysctl_ctx_list *ctx, struct sysctl_oid *node, |
---|
2652 | struct cpsw_queue *queue) |
---|
2653 | { |
---|
2654 | struct sysctl_oid_list *parent; |
---|
2655 | |
---|
2656 | parent = SYSCTL_CHILDREN(node); |
---|
2657 | SYSCTL_ADD_INT(ctx, parent, OID_AUTO, "totalBuffers", |
---|
2658 | CTLFLAG_RD, &queue->queue_slots, 0, |
---|
2659 | "Total buffers currently assigned to this queue"); |
---|
2660 | SYSCTL_ADD_INT(ctx, parent, OID_AUTO, "activeBuffers", |
---|
2661 | CTLFLAG_RD, &queue->active_queue_len, 0, |
---|
2662 | "Buffers currently registered with hardware controller"); |
---|
2663 | SYSCTL_ADD_INT(ctx, parent, OID_AUTO, "maxActiveBuffers", |
---|
2664 | CTLFLAG_RD, &queue->max_active_queue_len, 0, |
---|
2665 | "Max value of activeBuffers since last driver reset"); |
---|
2666 | SYSCTL_ADD_INT(ctx, parent, OID_AUTO, "availBuffers", |
---|
2667 | CTLFLAG_RD, &queue->avail_queue_len, 0, |
---|
2668 | "Buffers allocated to this queue but not currently " |
---|
2669 | "registered with hardware controller"); |
---|
2670 | SYSCTL_ADD_INT(ctx, parent, OID_AUTO, "maxAvailBuffers", |
---|
2671 | CTLFLAG_RD, &queue->max_avail_queue_len, 0, |
---|
2672 | "Max value of availBuffers since last driver reset"); |
---|
2673 | SYSCTL_ADD_UINT(ctx, parent, OID_AUTO, "totalEnqueued", |
---|
2674 | CTLFLAG_RD, &queue->queue_adds, 0, |
---|
2675 | "Total buffers added to queue"); |
---|
2676 | SYSCTL_ADD_UINT(ctx, parent, OID_AUTO, "totalDequeued", |
---|
2677 | CTLFLAG_RD, &queue->queue_removes, 0, |
---|
2678 | "Total buffers removed from queue"); |
---|
2679 | SYSCTL_ADD_UINT(ctx, parent, OID_AUTO, "queueRestart", |
---|
2680 | CTLFLAG_RD, &queue->queue_restart, 0, |
---|
2681 | "Total times the queue has been restarted"); |
---|
2682 | SYSCTL_ADD_UINT(ctx, parent, OID_AUTO, "longestChain", |
---|
2683 | CTLFLAG_RD, &queue->longest_chain, 0, |
---|
2684 | "Max buffers used for a single packet"); |
---|
2685 | } |
---|
2686 | |
---|
2687 | static void |
---|
2688 | cpsw_add_watchdog_sysctls(struct sysctl_ctx_list *ctx, struct sysctl_oid *node, |
---|
2689 | struct cpsw_softc *sc) |
---|
2690 | { |
---|
2691 | struct sysctl_oid_list *parent; |
---|
2692 | |
---|
2693 | parent = SYSCTL_CHILDREN(node); |
---|
2694 | SYSCTL_ADD_INT(ctx, parent, OID_AUTO, "resets", |
---|
2695 | CTLFLAG_RD, &sc->watchdog.resets, 0, |
---|
2696 | "Total number of watchdog resets"); |
---|
2697 | } |
---|
2698 | |
---|
2699 | static void |
---|
2700 | cpsw_add_sysctls(struct cpsw_softc *sc) |
---|
2701 | { |
---|
2702 | struct sysctl_ctx_list *ctx; |
---|
2703 | struct sysctl_oid *stats_node, *queue_node, *node; |
---|
2704 | struct sysctl_oid_list *parent, *stats_parent, *queue_parent; |
---|
2705 | struct sysctl_oid_list *ports_parent, *port_parent; |
---|
2706 | char port[16]; |
---|
2707 | int i; |
---|
2708 | |
---|
2709 | ctx = device_get_sysctl_ctx(sc->dev); |
---|
2710 | parent = SYSCTL_CHILDREN(device_get_sysctl_tree(sc->dev)); |
---|
2711 | |
---|
2712 | SYSCTL_ADD_INT(ctx, parent, OID_AUTO, "debug", |
---|
2713 | CTLFLAG_RW, &sc->debug, 0, "Enable switch debug messages"); |
---|
2714 | |
---|
2715 | SYSCTL_ADD_PROC(ctx, parent, OID_AUTO, "attachedSecs", |
---|
2716 | CTLTYPE_UINT | CTLFLAG_RD, sc, 0, cpsw_stat_attached, "IU", |
---|
2717 | "Time since driver attach"); |
---|
2718 | |
---|
2719 | SYSCTL_ADD_PROC(ctx, parent, OID_AUTO, "intr_coalesce_us", |
---|
2720 | CTLTYPE_UINT | CTLFLAG_RW, sc, 0, cpsw_intr_coalesce, "IU", |
---|
2721 | "minimum time between interrupts"); |
---|
2722 | |
---|
2723 | node = SYSCTL_ADD_NODE(ctx, parent, OID_AUTO, "ports", |
---|
2724 | CTLFLAG_RD, NULL, "CPSW Ports Statistics"); |
---|
2725 | ports_parent = SYSCTL_CHILDREN(node); |
---|
2726 | for (i = 0; i < CPSW_PORTS; i++) { |
---|
2727 | if (!sc->dualemac && i != sc->active_slave) |
---|
2728 | continue; |
---|
2729 | port[0] = '0' + i; |
---|
2730 | port[1] = '\0'; |
---|
2731 | node = SYSCTL_ADD_NODE(ctx, ports_parent, OID_AUTO, |
---|
2732 | port, CTLFLAG_RD, NULL, "CPSW Port Statistics"); |
---|
2733 | port_parent = SYSCTL_CHILDREN(node); |
---|
2734 | SYSCTL_ADD_PROC(ctx, port_parent, OID_AUTO, "uptime", |
---|
2735 | CTLTYPE_UINT | CTLFLAG_RD, sc, i, |
---|
2736 | cpsw_stat_uptime, "IU", "Seconds since driver init"); |
---|
2737 | } |
---|
2738 | |
---|
2739 | stats_node = SYSCTL_ADD_NODE(ctx, parent, OID_AUTO, "stats", |
---|
2740 | CTLFLAG_RD, NULL, "CPSW Statistics"); |
---|
2741 | stats_parent = SYSCTL_CHILDREN(stats_node); |
---|
2742 | for (i = 0; i < CPSW_SYSCTL_COUNT; ++i) { |
---|
2743 | SYSCTL_ADD_PROC(ctx, stats_parent, i, |
---|
2744 | cpsw_stat_sysctls[i].oid, |
---|
2745 | CTLTYPE_U64 | CTLFLAG_RD, sc, 0, |
---|
2746 | cpsw_stats_sysctl, "IU", |
---|
2747 | cpsw_stat_sysctls[i].oid); |
---|
2748 | } |
---|
2749 | |
---|
2750 | queue_node = SYSCTL_ADD_NODE(ctx, parent, OID_AUTO, "queue", |
---|
2751 | CTLFLAG_RD, NULL, "CPSW Queue Statistics"); |
---|
2752 | queue_parent = SYSCTL_CHILDREN(queue_node); |
---|
2753 | |
---|
2754 | node = SYSCTL_ADD_NODE(ctx, queue_parent, OID_AUTO, "tx", |
---|
2755 | CTLFLAG_RD, NULL, "TX Queue Statistics"); |
---|
2756 | cpsw_add_queue_sysctls(ctx, node, &sc->tx); |
---|
2757 | |
---|
2758 | node = SYSCTL_ADD_NODE(ctx, queue_parent, OID_AUTO, "rx", |
---|
2759 | CTLFLAG_RD, NULL, "RX Queue Statistics"); |
---|
2760 | cpsw_add_queue_sysctls(ctx, node, &sc->rx); |
---|
2761 | |
---|
2762 | node = SYSCTL_ADD_NODE(ctx, parent, OID_AUTO, "watchdog", |
---|
2763 | CTLFLAG_RD, NULL, "Watchdog Statistics"); |
---|
2764 | cpsw_add_watchdog_sysctls(ctx, node, sc); |
---|
2765 | } |
---|
2766 | |
---|
2767 | #ifdef CPSW_ETHERSWITCH |
---|
2768 | static etherswitch_info_t etherswitch_info = { |
---|
2769 | .es_nports = CPSW_PORTS + 1, |
---|
2770 | .es_nvlangroups = CPSW_VLANS, |
---|
2771 | .es_name = "TI Common Platform Ethernet Switch (CPSW)", |
---|
2772 | .es_vlan_caps = ETHERSWITCH_VLAN_DOT1Q, |
---|
2773 | }; |
---|
2774 | |
---|
2775 | static etherswitch_info_t * |
---|
2776 | cpsw_getinfo(device_t dev) |
---|
2777 | { |
---|
2778 | return (ðerswitch_info); |
---|
2779 | } |
---|
2780 | |
---|
2781 | static int |
---|
2782 | cpsw_getport(device_t dev, etherswitch_port_t *p) |
---|
2783 | { |
---|
2784 | int err; |
---|
2785 | struct cpsw_softc *sc; |
---|
2786 | struct cpswp_softc *psc; |
---|
2787 | struct ifmediareq *ifmr; |
---|
2788 | uint32_t reg; |
---|
2789 | |
---|
2790 | if (p->es_port < 0 || p->es_port > CPSW_PORTS) |
---|
2791 | return (ENXIO); |
---|
2792 | |
---|
2793 | err = 0; |
---|
2794 | sc = device_get_softc(dev); |
---|
2795 | if (p->es_port == CPSW_CPU_PORT) { |
---|
2796 | p->es_flags |= ETHERSWITCH_PORT_CPU; |
---|
2797 | ifmr = &p->es_ifmr; |
---|
2798 | ifmr->ifm_current = ifmr->ifm_active = |
---|
2799 | IFM_ETHER | IFM_1000_T | IFM_FDX; |
---|
2800 | ifmr->ifm_mask = 0; |
---|
2801 | ifmr->ifm_status = IFM_ACTIVE | IFM_AVALID; |
---|
2802 | ifmr->ifm_count = 0; |
---|
2803 | } else { |
---|
2804 | psc = device_get_softc(sc->port[p->es_port - 1].dev); |
---|
2805 | err = ifmedia_ioctl(psc->ifp, &p->es_ifr, |
---|
2806 | &psc->mii->mii_media, SIOCGIFMEDIA); |
---|
2807 | } |
---|
2808 | reg = cpsw_read_4(sc, CPSW_PORT_P_VLAN(p->es_port)); |
---|
2809 | p->es_pvid = reg & ETHERSWITCH_VID_MASK; |
---|
2810 | |
---|
2811 | reg = cpsw_read_4(sc, CPSW_ALE_PORTCTL(p->es_port)); |
---|
2812 | if (reg & ALE_PORTCTL_DROP_UNTAGGED) |
---|
2813 | p->es_flags |= ETHERSWITCH_PORT_DROPUNTAGGED; |
---|
2814 | if (reg & ALE_PORTCTL_INGRESS) |
---|
2815 | p->es_flags |= ETHERSWITCH_PORT_INGRESS; |
---|
2816 | |
---|
2817 | return (err); |
---|
2818 | } |
---|
2819 | |
---|
2820 | static int |
---|
2821 | cpsw_setport(device_t dev, etherswitch_port_t *p) |
---|
2822 | { |
---|
2823 | struct cpsw_softc *sc; |
---|
2824 | struct cpswp_softc *psc; |
---|
2825 | struct ifmedia *ifm; |
---|
2826 | uint32_t reg; |
---|
2827 | |
---|
2828 | if (p->es_port < 0 || p->es_port > CPSW_PORTS) |
---|
2829 | return (ENXIO); |
---|
2830 | |
---|
2831 | sc = device_get_softc(dev); |
---|
2832 | if (p->es_pvid != 0) { |
---|
2833 | cpsw_write_4(sc, CPSW_PORT_P_VLAN(p->es_port), |
---|
2834 | p->es_pvid & ETHERSWITCH_VID_MASK); |
---|
2835 | } |
---|
2836 | |
---|
2837 | reg = cpsw_read_4(sc, CPSW_ALE_PORTCTL(p->es_port)); |
---|
2838 | if (p->es_flags & ETHERSWITCH_PORT_DROPUNTAGGED) |
---|
2839 | reg |= ALE_PORTCTL_DROP_UNTAGGED; |
---|
2840 | else |
---|
2841 | reg &= ~ALE_PORTCTL_DROP_UNTAGGED; |
---|
2842 | if (p->es_flags & ETHERSWITCH_PORT_INGRESS) |
---|
2843 | reg |= ALE_PORTCTL_INGRESS; |
---|
2844 | else |
---|
2845 | reg &= ~ALE_PORTCTL_INGRESS; |
---|
2846 | cpsw_write_4(sc, CPSW_ALE_PORTCTL(p->es_port), reg); |
---|
2847 | |
---|
2848 | /* CPU port does not allow media settings. */ |
---|
2849 | if (p->es_port == CPSW_CPU_PORT) |
---|
2850 | return (0); |
---|
2851 | |
---|
2852 | psc = device_get_softc(sc->port[p->es_port - 1].dev); |
---|
2853 | ifm = &psc->mii->mii_media; |
---|
2854 | |
---|
2855 | return (ifmedia_ioctl(psc->ifp, &p->es_ifr, ifm, SIOCSIFMEDIA)); |
---|
2856 | } |
---|
2857 | |
---|
2858 | static int |
---|
2859 | cpsw_getconf(device_t dev, etherswitch_conf_t *conf) |
---|
2860 | { |
---|
2861 | |
---|
2862 | /* Return the VLAN mode. */ |
---|
2863 | conf->cmd = ETHERSWITCH_CONF_VLAN_MODE; |
---|
2864 | conf->vlan_mode = ETHERSWITCH_VLAN_DOT1Q; |
---|
2865 | |
---|
2866 | return (0); |
---|
2867 | } |
---|
2868 | |
---|
2869 | static int |
---|
2870 | cpsw_getvgroup(device_t dev, etherswitch_vlangroup_t *vg) |
---|
2871 | { |
---|
2872 | int i, vid; |
---|
2873 | uint32_t ale_entry[3]; |
---|
2874 | struct cpsw_softc *sc; |
---|
2875 | |
---|
2876 | sc = device_get_softc(dev); |
---|
2877 | |
---|
2878 | if (vg->es_vlangroup >= CPSW_VLANS) |
---|
2879 | return (EINVAL); |
---|
2880 | |
---|
2881 | vg->es_vid = 0; |
---|
2882 | vid = cpsw_vgroups[vg->es_vlangroup].vid; |
---|
2883 | if (vid == -1) |
---|
2884 | return (0); |
---|
2885 | |
---|
2886 | for (i = 0; i < CPSW_MAX_ALE_ENTRIES; i++) { |
---|
2887 | cpsw_ale_read_entry(sc, i, ale_entry); |
---|
2888 | if (ALE_TYPE(ale_entry) != ALE_TYPE_VLAN) |
---|
2889 | continue; |
---|
2890 | if (vid != ALE_VLAN(ale_entry)) |
---|
2891 | continue; |
---|
2892 | |
---|
2893 | vg->es_fid = 0; |
---|
2894 | vg->es_vid = ALE_VLAN(ale_entry) | ETHERSWITCH_VID_VALID; |
---|
2895 | vg->es_member_ports = ALE_VLAN_MEMBERS(ale_entry); |
---|
2896 | vg->es_untagged_ports = ALE_VLAN_UNTAG(ale_entry); |
---|
2897 | } |
---|
2898 | |
---|
2899 | return (0); |
---|
2900 | } |
---|
2901 | |
---|
2902 | static void |
---|
2903 | cpsw_remove_vlan(struct cpsw_softc *sc, int vlan) |
---|
2904 | { |
---|
2905 | int i; |
---|
2906 | uint32_t ale_entry[3]; |
---|
2907 | |
---|
2908 | for (i = 0; i < CPSW_MAX_ALE_ENTRIES; i++) { |
---|
2909 | cpsw_ale_read_entry(sc, i, ale_entry); |
---|
2910 | if (ALE_TYPE(ale_entry) != ALE_TYPE_VLAN) |
---|
2911 | continue; |
---|
2912 | if (vlan != ALE_VLAN(ale_entry)) |
---|
2913 | continue; |
---|
2914 | ale_entry[0] = ale_entry[1] = ale_entry[2] = 0; |
---|
2915 | cpsw_ale_write_entry(sc, i, ale_entry); |
---|
2916 | break; |
---|
2917 | } |
---|
2918 | } |
---|
2919 | |
---|
2920 | static int |
---|
2921 | cpsw_setvgroup(device_t dev, etherswitch_vlangroup_t *vg) |
---|
2922 | { |
---|
2923 | int i; |
---|
2924 | struct cpsw_softc *sc; |
---|
2925 | |
---|
2926 | sc = device_get_softc(dev); |
---|
2927 | |
---|
2928 | for (i = 0; i < CPSW_VLANS; i++) { |
---|
2929 | /* Is this Vlan ID in use by another vlangroup ? */ |
---|
2930 | if (vg->es_vlangroup != i && cpsw_vgroups[i].vid == vg->es_vid) |
---|
2931 | return (EINVAL); |
---|
2932 | } |
---|
2933 | |
---|
2934 | if (vg->es_vid == 0) { |
---|
2935 | if (cpsw_vgroups[vg->es_vlangroup].vid == -1) |
---|
2936 | return (0); |
---|
2937 | cpsw_remove_vlan(sc, cpsw_vgroups[vg->es_vlangroup].vid); |
---|
2938 | cpsw_vgroups[vg->es_vlangroup].vid = -1; |
---|
2939 | vg->es_untagged_ports = 0; |
---|
2940 | vg->es_member_ports = 0; |
---|
2941 | vg->es_vid = 0; |
---|
2942 | return (0); |
---|
2943 | } |
---|
2944 | |
---|
2945 | vg->es_vid &= ETHERSWITCH_VID_MASK; |
---|
2946 | vg->es_member_ports &= CPSW_PORTS_MASK; |
---|
2947 | vg->es_untagged_ports &= CPSW_PORTS_MASK; |
---|
2948 | |
---|
2949 | if (cpsw_vgroups[vg->es_vlangroup].vid != -1 && |
---|
2950 | cpsw_vgroups[vg->es_vlangroup].vid != vg->es_vid) |
---|
2951 | return (EINVAL); |
---|
2952 | |
---|
2953 | cpsw_vgroups[vg->es_vlangroup].vid = vg->es_vid; |
---|
2954 | cpsw_ale_update_vlan_table(sc, vg->es_vid, vg->es_member_ports, |
---|
2955 | vg->es_untagged_ports, vg->es_member_ports, 0); |
---|
2956 | |
---|
2957 | return (0); |
---|
2958 | } |
---|
2959 | |
---|
2960 | static int |
---|
2961 | cpsw_readreg(device_t dev, int addr) |
---|
2962 | { |
---|
2963 | |
---|
2964 | /* Not supported. */ |
---|
2965 | return (0); |
---|
2966 | } |
---|
2967 | |
---|
2968 | static int |
---|
2969 | cpsw_writereg(device_t dev, int addr, int value) |
---|
2970 | { |
---|
2971 | |
---|
2972 | /* Not supported. */ |
---|
2973 | return (0); |
---|
2974 | } |
---|
2975 | |
---|
2976 | static int |
---|
2977 | cpsw_readphy(device_t dev, int phy, int reg) |
---|
2978 | { |
---|
2979 | |
---|
2980 | /* Not supported. */ |
---|
2981 | return (0); |
---|
2982 | } |
---|
2983 | |
---|
2984 | static int |
---|
2985 | cpsw_writephy(device_t dev, int phy, int reg, int data) |
---|
2986 | { |
---|
2987 | |
---|
2988 | /* Not supported. */ |
---|
2989 | return (0); |
---|
2990 | } |
---|
2991 | #endif |
---|