xref: /freebsd/sys/arm/ti/cpsw/if_cpsw.c (revision e453e498cbb88570a3ff7b3679de65c88707da95)
1e53470feSOleksandr Tymoshenko /*-
24d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
3af3dc4a7SPedro F. Giffuni  *
4e53470feSOleksandr Tymoshenko  * Copyright (c) 2012 Damjan Marion <dmarion@Freebsd.org>
523cd11b6SLuiz Otavio O Souza  * Copyright (c) 2016 Rubicon Communications, LLC (Netgate)
6e53470feSOleksandr Tymoshenko  * All rights reserved.
7e53470feSOleksandr Tymoshenko  *
8e53470feSOleksandr Tymoshenko  * Redistribution and use in source and binary forms, with or without
9e53470feSOleksandr Tymoshenko  * modification, are permitted provided that the following conditions
10e53470feSOleksandr Tymoshenko  * are met:
11e53470feSOleksandr Tymoshenko  * 1. Redistributions of source code must retain the above copyright
12e53470feSOleksandr Tymoshenko  *    notice, this list of conditions and the following disclaimer.
13e53470feSOleksandr Tymoshenko  * 2. Redistributions in binary form must reproduce the above copyright
14e53470feSOleksandr Tymoshenko  *    notice, this list of conditions and the following disclaimer in the
15e53470feSOleksandr Tymoshenko  *    documentation and/or other materials provided with the distribution.
16e53470feSOleksandr Tymoshenko  *
17e53470feSOleksandr Tymoshenko  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18e53470feSOleksandr Tymoshenko  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19e53470feSOleksandr Tymoshenko  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20e53470feSOleksandr Tymoshenko  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21e53470feSOleksandr Tymoshenko  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22e53470feSOleksandr Tymoshenko  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23e53470feSOleksandr Tymoshenko  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24e53470feSOleksandr Tymoshenko  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25e53470feSOleksandr Tymoshenko  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26e53470feSOleksandr Tymoshenko  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27e53470feSOleksandr Tymoshenko  * SUCH DAMAGE.
28e53470feSOleksandr Tymoshenko  */
29e53470feSOleksandr Tymoshenko 
30e53470feSOleksandr Tymoshenko /*
31ae6aefafSTim Kientzle  * TI Common Platform Ethernet Switch (CPSW) Driver
32ae6aefafSTim Kientzle  * Found in TI8148 "DaVinci" and AM335x "Sitara" SoCs.
33ae6aefafSTim Kientzle  *
34ae6aefafSTim Kientzle  * This controller is documented in the AM335x Technical Reference
35ae6aefafSTim Kientzle  * Manual, in the TMS320DM814x DaVinci Digital Video Processors TRM
36ae6aefafSTim Kientzle  * and in the TMS320C6452 3 Port Switch Ethernet Subsystem TRM.
37ae6aefafSTim Kientzle  *
38ae6aefafSTim Kientzle  * It is basically a single Ethernet port (port 0) wired internally to
39ae6aefafSTim Kientzle  * a 3-port store-and-forward switch connected to two independent
40ae6aefafSTim Kientzle  * "sliver" controllers (port 1 and port 2).  You can operate the
41ae6aefafSTim Kientzle  * controller in a variety of different ways by suitably configuring
42ae6aefafSTim Kientzle  * the slivers and the Address Lookup Engine (ALE) that routes packets
43ae6aefafSTim Kientzle  * between the ports.
44ae6aefafSTim Kientzle  *
45ae6aefafSTim Kientzle  * This code was developed and tested on a BeagleBone with
46ae6aefafSTim Kientzle  * an AM335x SoC.
47e53470feSOleksandr Tymoshenko  */
48e53470feSOleksandr Tymoshenko 
49e53470feSOleksandr Tymoshenko #include <sys/cdefs.h>
50a2c46b94SLuiz Otavio O Souza #include "opt_cpsw.h"
51a2c46b94SLuiz Otavio O Souza 
52e53470feSOleksandr Tymoshenko #include <sys/param.h>
53c9334570SLuiz Otavio O Souza #include <sys/bus.h>
54e53470feSOleksandr Tymoshenko #include <sys/kernel.h>
55c9334570SLuiz Otavio O Souza #include <sys/lock.h>
56c9334570SLuiz Otavio O Souza #include <sys/mbuf.h>
57e53470feSOleksandr Tymoshenko #include <sys/module.h>
58c9334570SLuiz Otavio O Souza #include <sys/mutex.h>
59c9334570SLuiz Otavio O Souza #include <sys/rman.h>
60e53470feSOleksandr Tymoshenko #include <sys/socket.h>
61c9334570SLuiz Otavio O Souza #include <sys/sockio.h>
62*e453e498SBrooks Davis #include <sys/stdarg.h>
63e53470feSOleksandr Tymoshenko #include <sys/sysctl.h>
64e53470feSOleksandr Tymoshenko 
65c9334570SLuiz Otavio O Souza #include <machine/bus.h>
66c9334570SLuiz Otavio O Souza #include <machine/resource.h>
67c9334570SLuiz Otavio O Souza 
68e53470feSOleksandr Tymoshenko #include <net/ethernet.h>
69e53470feSOleksandr Tymoshenko #include <net/bpf.h>
70e53470feSOleksandr Tymoshenko #include <net/if.h>
71e53470feSOleksandr Tymoshenko #include <net/if_dl.h>
72e53470feSOleksandr Tymoshenko #include <net/if_media.h>
73e53470feSOleksandr Tymoshenko #include <net/if_types.h>
74e53470feSOleksandr Tymoshenko 
7562e8ccc3SEmmanuel Vadot #include <dev/syscon/syscon.h>
760050ea24SMichal Meloun #include "syscon_if.h"
77dc3155c1SLuiz Otavio O Souza #include <arm/ti/am335x/am335x_scm.h>
78dc3155c1SLuiz Otavio O Souza 
79e53470feSOleksandr Tymoshenko #include <dev/mii/mii.h>
80e53470feSOleksandr Tymoshenko #include <dev/mii/miivar.h>
81e53470feSOleksandr Tymoshenko 
82e53470feSOleksandr Tymoshenko #include <dev/ofw/ofw_bus.h>
83e53470feSOleksandr Tymoshenko #include <dev/ofw/ofw_bus_subr.h>
84e53470feSOleksandr Tymoshenko 
855eb26411SEd Maste #include <dev/fdt/fdt_common.h>
865eb26411SEd Maste 
87a2c46b94SLuiz Otavio O Souza #ifdef CPSW_ETHERSWITCH
88a2c46b94SLuiz Otavio O Souza #include <dev/etherswitch/etherswitch.h>
89a2c46b94SLuiz Otavio O Souza #include "etherswitch_if.h"
90a2c46b94SLuiz Otavio O Souza #endif
91a2c46b94SLuiz Otavio O Souza 
92ea4597d0STim Kientzle #include "if_cpswreg.h"
93ea4597d0STim Kientzle #include "if_cpswvar.h"
94e53470feSOleksandr Tymoshenko 
95e53470feSOleksandr Tymoshenko #include "miibus_if.h"
96e53470feSOleksandr Tymoshenko 
97ae6aefafSTim Kientzle /* Device probe/attach/detach. */
98ae6aefafSTim Kientzle static int cpsw_probe(device_t);
99ae6aefafSTim Kientzle static int cpsw_attach(device_t);
100ae6aefafSTim Kientzle static int cpsw_detach(device_t);
10123cd11b6SLuiz Otavio O Souza static int cpswp_probe(device_t);
10223cd11b6SLuiz Otavio O Souza static int cpswp_attach(device_t);
10323cd11b6SLuiz Otavio O Souza static int cpswp_detach(device_t);
10423cd11b6SLuiz Otavio O Souza 
10523cd11b6SLuiz Otavio O Souza static phandle_t cpsw_get_node(device_t, device_t);
106e53470feSOleksandr Tymoshenko 
107ae6aefafSTim Kientzle /* Device Init/shutdown. */
108ae6aefafSTim Kientzle static int cpsw_shutdown(device_t);
10923cd11b6SLuiz Otavio O Souza static void cpswp_init(void *);
11023cd11b6SLuiz Otavio O Souza static void cpswp_init_locked(void *);
11123cd11b6SLuiz Otavio O Souza static void cpswp_stop_locked(struct cpswp_softc *);
112e53470feSOleksandr Tymoshenko 
113ae6aefafSTim Kientzle /* Device Suspend/Resume. */
114ae6aefafSTim Kientzle static int cpsw_suspend(device_t);
115ae6aefafSTim Kientzle static int cpsw_resume(device_t);
116e53470feSOleksandr Tymoshenko 
117ae6aefafSTim Kientzle /* Ioctl. */
1182c7bc0f5SJustin Hibbits static int cpswp_ioctl(if_t, u_long command, caddr_t data);
119e53470feSOleksandr Tymoshenko 
12023cd11b6SLuiz Otavio O Souza static int cpswp_miibus_readreg(device_t, int phy, int reg);
12123cd11b6SLuiz Otavio O Souza static int cpswp_miibus_writereg(device_t, int phy, int reg, int value);
12223cd11b6SLuiz Otavio O Souza static void cpswp_miibus_statchg(device_t);
123ae6aefafSTim Kientzle 
124ae6aefafSTim Kientzle /* Send/Receive packets. */
125e53470feSOleksandr Tymoshenko static void cpsw_intr_rx(void *arg);
126ae6aefafSTim Kientzle static struct mbuf *cpsw_rx_dequeue(struct cpsw_softc *);
127ae6aefafSTim Kientzle static void cpsw_rx_enqueue(struct cpsw_softc *);
1282c7bc0f5SJustin Hibbits static void cpswp_start(if_t);
129430d5eb4SLuiz Otavio O Souza static void cpsw_intr_tx(void *);
13023cd11b6SLuiz Otavio O Souza static void cpswp_tx_enqueue(struct cpswp_softc *);
131ae6aefafSTim Kientzle static int cpsw_tx_dequeue(struct cpsw_softc *);
132e53470feSOleksandr Tymoshenko 
133ae6aefafSTim Kientzle /* Misc interrupts and watchdog. */
134ae6aefafSTim Kientzle static void cpsw_intr_rx_thresh(void *);
135ae6aefafSTim Kientzle static void cpsw_intr_misc(void *);
13623cd11b6SLuiz Otavio O Souza static void cpswp_tick(void *);
1372c7bc0f5SJustin Hibbits static void cpswp_ifmedia_sts(if_t, struct ifmediareq *);
1382c7bc0f5SJustin Hibbits static int cpswp_ifmedia_upd(if_t);
13923cd11b6SLuiz Otavio O Souza static void cpsw_tx_watchdog(void *);
140ae6aefafSTim Kientzle 
141ae6aefafSTim Kientzle /* ALE support */
14223cd11b6SLuiz Otavio O Souza static void cpsw_ale_read_entry(struct cpsw_softc *, uint16_t, uint32_t *);
14323cd11b6SLuiz Otavio O Souza static void cpsw_ale_write_entry(struct cpsw_softc *, uint16_t, uint32_t *);
14423cd11b6SLuiz Otavio O Souza static int cpsw_ale_mc_entry_set(struct cpsw_softc *, uint8_t, int, uint8_t *);
145ae6aefafSTim Kientzle static void cpsw_ale_dump_table(struct cpsw_softc *);
14651f8a15cSLuiz Otavio O Souza static int cpsw_ale_update_vlan_table(struct cpsw_softc *, int, int, int, int,
14751f8a15cSLuiz Otavio O Souza 	int);
14823cd11b6SLuiz Otavio O Souza static int cpswp_ale_update_addresses(struct cpswp_softc *, int);
149ae6aefafSTim Kientzle 
150ae6aefafSTim Kientzle /* Statistics and sysctls. */
151ae6aefafSTim Kientzle static void cpsw_add_sysctls(struct cpsw_softc *);
152ae6aefafSTim Kientzle static void cpsw_stats_collect(struct cpsw_softc *);
153ae6aefafSTim Kientzle static int cpsw_stats_sysctl(SYSCTL_HANDLER_ARGS);
154ae6aefafSTim Kientzle 
155a2c46b94SLuiz Otavio O Souza #ifdef CPSW_ETHERSWITCH
156a2c46b94SLuiz Otavio O Souza static etherswitch_info_t *cpsw_getinfo(device_t);
157a2c46b94SLuiz Otavio O Souza static int cpsw_getport(device_t, etherswitch_port_t *);
158a2c46b94SLuiz Otavio O Souza static int cpsw_setport(device_t, etherswitch_port_t *);
159a2c46b94SLuiz Otavio O Souza static int cpsw_getconf(device_t, etherswitch_conf_t *);
160a2c46b94SLuiz Otavio O Souza static int cpsw_getvgroup(device_t, etherswitch_vlangroup_t *);
161a2c46b94SLuiz Otavio O Souza static int cpsw_setvgroup(device_t, etherswitch_vlangroup_t *);
162a2c46b94SLuiz Otavio O Souza static int cpsw_readreg(device_t, int);
163a2c46b94SLuiz Otavio O Souza static int cpsw_writereg(device_t, int, int);
164a2c46b94SLuiz Otavio O Souza static int cpsw_readphy(device_t, int, int);
165a2c46b94SLuiz Otavio O Souza static int cpsw_writephy(device_t, int, int, int);
166a2c46b94SLuiz Otavio O Souza #endif
167a2c46b94SLuiz Otavio O Souza 
168ae6aefafSTim Kientzle /*
169ae6aefafSTim Kientzle  * Arbitrary limit on number of segments in an mbuf to be transmitted.
170ae6aefafSTim Kientzle  * Packets with more segments than this will be defragmented before
171ae6aefafSTim Kientzle  * they are queued.
172ae6aefafSTim Kientzle  */
1737996fcdcSLuiz Otavio O Souza #define	CPSW_TXFRAGS		16
174ae6aefafSTim Kientzle 
17523cd11b6SLuiz Otavio O Souza /* Shared resources. */
176e53470feSOleksandr Tymoshenko static device_method_t cpsw_methods[] = {
177e53470feSOleksandr Tymoshenko 	/* Device interface */
178e53470feSOleksandr Tymoshenko 	DEVMETHOD(device_probe,		cpsw_probe),
179e53470feSOleksandr Tymoshenko 	DEVMETHOD(device_attach,	cpsw_attach),
180e53470feSOleksandr Tymoshenko 	DEVMETHOD(device_detach,	cpsw_detach),
181e53470feSOleksandr Tymoshenko 	DEVMETHOD(device_shutdown,	cpsw_shutdown),
182e53470feSOleksandr Tymoshenko 	DEVMETHOD(device_suspend,	cpsw_suspend),
183e53470feSOleksandr Tymoshenko 	DEVMETHOD(device_resume,	cpsw_resume),
184a2c46b94SLuiz Otavio O Souza 	/* Bus interface */
185a2c46b94SLuiz Otavio O Souza 	DEVMETHOD(bus_add_child,	device_add_child_ordered),
18623cd11b6SLuiz Otavio O Souza 	/* OFW methods */
18723cd11b6SLuiz Otavio O Souza 	DEVMETHOD(ofw_bus_get_node,	cpsw_get_node),
188a2c46b94SLuiz Otavio O Souza #ifdef CPSW_ETHERSWITCH
189a2c46b94SLuiz Otavio O Souza 	/* etherswitch interface */
190a2c46b94SLuiz Otavio O Souza 	DEVMETHOD(etherswitch_getinfo,	cpsw_getinfo),
191a2c46b94SLuiz Otavio O Souza 	DEVMETHOD(etherswitch_readreg,	cpsw_readreg),
192a2c46b94SLuiz Otavio O Souza 	DEVMETHOD(etherswitch_writereg,	cpsw_writereg),
193a2c46b94SLuiz Otavio O Souza 	DEVMETHOD(etherswitch_readphyreg,	cpsw_readphy),
194a2c46b94SLuiz Otavio O Souza 	DEVMETHOD(etherswitch_writephyreg,	cpsw_writephy),
195a2c46b94SLuiz Otavio O Souza 	DEVMETHOD(etherswitch_getport,	cpsw_getport),
196a2c46b94SLuiz Otavio O Souza 	DEVMETHOD(etherswitch_setport,	cpsw_setport),
197a2c46b94SLuiz Otavio O Souza 	DEVMETHOD(etherswitch_getvgroup,	cpsw_getvgroup),
198a2c46b94SLuiz Otavio O Souza 	DEVMETHOD(etherswitch_setvgroup,	cpsw_setvgroup),
199a2c46b94SLuiz Otavio O Souza 	DEVMETHOD(etherswitch_getconf,	cpsw_getconf),
200a2c46b94SLuiz Otavio O Souza #endif
20123cd11b6SLuiz Otavio O Souza 	DEVMETHOD_END
202e53470feSOleksandr Tymoshenko };
203e53470feSOleksandr Tymoshenko 
204e53470feSOleksandr Tymoshenko static driver_t cpsw_driver = {
20523cd11b6SLuiz Otavio O Souza 	"cpswss",
206e53470feSOleksandr Tymoshenko 	cpsw_methods,
207e53470feSOleksandr Tymoshenko 	sizeof(struct cpsw_softc),
208e53470feSOleksandr Tymoshenko };
209e53470feSOleksandr Tymoshenko 
2108537e671SJohn Baldwin DRIVER_MODULE(cpswss, simplebus, cpsw_driver, 0, 0);
21123cd11b6SLuiz Otavio O Souza 
21223cd11b6SLuiz Otavio O Souza /* Port/Slave resources. */
21323cd11b6SLuiz Otavio O Souza static device_method_t cpswp_methods[] = {
21423cd11b6SLuiz Otavio O Souza 	/* Device interface */
21523cd11b6SLuiz Otavio O Souza 	DEVMETHOD(device_probe,		cpswp_probe),
21623cd11b6SLuiz Otavio O Souza 	DEVMETHOD(device_attach,	cpswp_attach),
21723cd11b6SLuiz Otavio O Souza 	DEVMETHOD(device_detach,	cpswp_detach),
21823cd11b6SLuiz Otavio O Souza 	/* MII interface */
21923cd11b6SLuiz Otavio O Souza 	DEVMETHOD(miibus_readreg,	cpswp_miibus_readreg),
22023cd11b6SLuiz Otavio O Souza 	DEVMETHOD(miibus_writereg,	cpswp_miibus_writereg),
22123cd11b6SLuiz Otavio O Souza 	DEVMETHOD(miibus_statchg,	cpswp_miibus_statchg),
22223cd11b6SLuiz Otavio O Souza 	DEVMETHOD_END
22323cd11b6SLuiz Otavio O Souza };
22423cd11b6SLuiz Otavio O Souza 
22523cd11b6SLuiz Otavio O Souza static driver_t cpswp_driver = {
22623cd11b6SLuiz Otavio O Souza 	"cpsw",
22723cd11b6SLuiz Otavio O Souza 	cpswp_methods,
22823cd11b6SLuiz Otavio O Souza 	sizeof(struct cpswp_softc),
22923cd11b6SLuiz Otavio O Souza };
23023cd11b6SLuiz Otavio O Souza 
231a2c46b94SLuiz Otavio O Souza #ifdef CPSW_ETHERSWITCH
232829a13faSJohn Baldwin DRIVER_MODULE(etherswitch, cpswss, etherswitch_driver, 0, 0);
233a2c46b94SLuiz Otavio O Souza MODULE_DEPEND(cpswss, etherswitch, 1, 1, 1);
234a2c46b94SLuiz Otavio O Souza #endif
235a2c46b94SLuiz Otavio O Souza 
2368537e671SJohn Baldwin DRIVER_MODULE(cpsw, cpswss, cpswp_driver, 0, 0);
2373e38757dSJohn Baldwin DRIVER_MODULE(miibus, cpsw, miibus_driver, 0, 0);
238e53470feSOleksandr Tymoshenko MODULE_DEPEND(cpsw, ether, 1, 1, 1);
239e53470feSOleksandr Tymoshenko MODULE_DEPEND(cpsw, miibus, 1, 1, 1);
240e53470feSOleksandr Tymoshenko 
241a2c46b94SLuiz Otavio O Souza #ifdef CPSW_ETHERSWITCH
242a2c46b94SLuiz Otavio O Souza static struct cpsw_vlangroups cpsw_vgroups[CPSW_VLANS];
243a2c46b94SLuiz Otavio O Souza #endif
244a2c46b94SLuiz Otavio O Souza 
24523cd11b6SLuiz Otavio O Souza static uint32_t slave_mdio_addr[] = { 0x4a100200, 0x4a100300 };
24623cd11b6SLuiz Otavio O Souza 
2475b03aba6SOleksandr Tymoshenko static struct resource_spec irq_res_spec[] = {
248e53470feSOleksandr Tymoshenko 	{ SYS_RES_IRQ, 0, RF_ACTIVE | RF_SHAREABLE },
249e53470feSOleksandr Tymoshenko 	{ SYS_RES_IRQ, 1, RF_ACTIVE | RF_SHAREABLE },
250e53470feSOleksandr Tymoshenko 	{ SYS_RES_IRQ, 2, RF_ACTIVE | RF_SHAREABLE },
251e53470feSOleksandr Tymoshenko 	{ SYS_RES_IRQ, 3, RF_ACTIVE | RF_SHAREABLE },
252e53470feSOleksandr Tymoshenko 	{ -1, 0 }
253e53470feSOleksandr Tymoshenko };
254e53470feSOleksandr Tymoshenko 
255430d5eb4SLuiz Otavio O Souza static struct {
256430d5eb4SLuiz Otavio O Souza 	void (*cb)(void *);
257430d5eb4SLuiz Otavio O Souza } cpsw_intr_cb[] = {
258430d5eb4SLuiz Otavio O Souza 	{ cpsw_intr_rx_thresh },
259430d5eb4SLuiz Otavio O Souza 	{ cpsw_intr_rx },
260430d5eb4SLuiz Otavio O Souza 	{ cpsw_intr_tx },
261430d5eb4SLuiz Otavio O Souza 	{ cpsw_intr_misc },
262430d5eb4SLuiz Otavio O Souza };
263430d5eb4SLuiz Otavio O Souza 
264ae6aefafSTim Kientzle /* Number of entries here must match size of stats
26523cd11b6SLuiz Otavio O Souza  * array in struct cpswp_softc. */
266ae6aefafSTim Kientzle static struct cpsw_stat {
267ae6aefafSTim Kientzle 	int	reg;
268ae6aefafSTim Kientzle 	char *oid;
269ae6aefafSTim Kientzle } cpsw_stat_sysctls[CPSW_SYSCTL_COUNT] = {
270ae6aefafSTim Kientzle 	{0x00, "GoodRxFrames"},
271ae6aefafSTim Kientzle 	{0x04, "BroadcastRxFrames"},
272ae6aefafSTim Kientzle 	{0x08, "MulticastRxFrames"},
273ae6aefafSTim Kientzle 	{0x0C, "PauseRxFrames"},
274ae6aefafSTim Kientzle 	{0x10, "RxCrcErrors"},
275ae6aefafSTim Kientzle 	{0x14, "RxAlignErrors"},
276ae6aefafSTim Kientzle 	{0x18, "OversizeRxFrames"},
277ae6aefafSTim Kientzle 	{0x1c, "RxJabbers"},
278ae6aefafSTim Kientzle 	{0x20, "ShortRxFrames"},
279ae6aefafSTim Kientzle 	{0x24, "RxFragments"},
280ae6aefafSTim Kientzle 	{0x30, "RxOctets"},
281ae6aefafSTim Kientzle 	{0x34, "GoodTxFrames"},
282ae6aefafSTim Kientzle 	{0x38, "BroadcastTxFrames"},
283ae6aefafSTim Kientzle 	{0x3c, "MulticastTxFrames"},
284ae6aefafSTim Kientzle 	{0x40, "PauseTxFrames"},
285ae6aefafSTim Kientzle 	{0x44, "DeferredTxFrames"},
286ae6aefafSTim Kientzle 	{0x48, "CollisionsTxFrames"},
287ae6aefafSTim Kientzle 	{0x4c, "SingleCollisionTxFrames"},
288ae6aefafSTim Kientzle 	{0x50, "MultipleCollisionTxFrames"},
289ae6aefafSTim Kientzle 	{0x54, "ExcessiveCollisions"},
290ae6aefafSTim Kientzle 	{0x58, "LateCollisions"},
291ae6aefafSTim Kientzle 	{0x5c, "TxUnderrun"},
292ae6aefafSTim Kientzle 	{0x60, "CarrierSenseErrors"},
293ae6aefafSTim Kientzle 	{0x64, "TxOctets"},
294ae6aefafSTim Kientzle 	{0x68, "RxTx64OctetFrames"},
295ae6aefafSTim Kientzle 	{0x6c, "RxTx65to127OctetFrames"},
296ae6aefafSTim Kientzle 	{0x70, "RxTx128to255OctetFrames"},
297ae6aefafSTim Kientzle 	{0x74, "RxTx256to511OctetFrames"},
298ae6aefafSTim Kientzle 	{0x78, "RxTx512to1024OctetFrames"},
299ae6aefafSTim Kientzle 	{0x7c, "RxTx1024upOctetFrames"},
300ae6aefafSTim Kientzle 	{0x80, "NetOctets"},
301ae6aefafSTim Kientzle 	{0x84, "RxStartOfFrameOverruns"},
302ae6aefafSTim Kientzle 	{0x88, "RxMiddleOfFrameOverruns"},
303ae6aefafSTim Kientzle 	{0x8c, "RxDmaOverruns"}
304e53470feSOleksandr Tymoshenko };
305e53470feSOleksandr Tymoshenko 
306ae6aefafSTim Kientzle /*
307ae6aefafSTim Kientzle  * Basic debug support.
308ae6aefafSTim Kientzle  */
309e53470feSOleksandr Tymoshenko 
3105bf32555STim Kientzle static void
cpsw_debugf_head(const char * funcname)3115bf32555STim Kientzle cpsw_debugf_head(const char *funcname)
3125bf32555STim Kientzle {
3135bf32555STim Kientzle 	int t = (int)(time_second % (24 * 60 * 60));
3145bf32555STim Kientzle 
3155bf32555STim Kientzle 	printf("%02d:%02d:%02d %s ", t / (60 * 60), (t / 60) % 60, t % 60, funcname);
3165bf32555STim Kientzle }
3175bf32555STim Kientzle 
3185bf32555STim Kientzle static void
cpsw_debugf(const char * fmt,...)3195bf32555STim Kientzle cpsw_debugf(const char *fmt, ...)
3205bf32555STim Kientzle {
3215bf32555STim Kientzle 	va_list ap;
3225bf32555STim Kientzle 
3235bf32555STim Kientzle 	va_start(ap, fmt);
3245bf32555STim Kientzle 	vprintf(fmt, ap);
3255bf32555STim Kientzle 	va_end(ap);
3265bf32555STim Kientzle 	printf("\n");
3275bf32555STim Kientzle 
3285bf32555STim Kientzle }
3295bf32555STim Kientzle 
33023cd11b6SLuiz Otavio O Souza #define	CPSW_DEBUGF(_sc, a) do {					\
3315a9c270aSLuiz Otavio O Souza 	if ((_sc)->debug) {						\
3325bf32555STim Kientzle 		cpsw_debugf_head(__func__);				\
3335bf32555STim Kientzle 		cpsw_debugf a;						\
3345bf32555STim Kientzle 	}								\
3355bf32555STim Kientzle } while (0)
3365bf32555STim Kientzle 
337ae6aefafSTim Kientzle /*
338ae6aefafSTim Kientzle  * Locking macros
339ae6aefafSTim Kientzle  */
340ae6aefafSTim Kientzle #define	CPSW_TX_LOCK(sc) do {						\
341ae6aefafSTim Kientzle 		mtx_assert(&(sc)->rx.lock, MA_NOTOWNED);		\
342ae6aefafSTim Kientzle 		mtx_lock(&(sc)->tx.lock);				\
343ae6aefafSTim Kientzle } while (0)
344ae6aefafSTim Kientzle 
345ae6aefafSTim Kientzle #define	CPSW_TX_UNLOCK(sc)	mtx_unlock(&(sc)->tx.lock)
346ae6aefafSTim Kientzle #define	CPSW_TX_LOCK_ASSERT(sc)	mtx_assert(&(sc)->tx.lock, MA_OWNED)
347ae6aefafSTim Kientzle 
348ae6aefafSTim Kientzle #define	CPSW_RX_LOCK(sc) do {						\
349ae6aefafSTim Kientzle 		mtx_assert(&(sc)->tx.lock, MA_NOTOWNED);		\
350ae6aefafSTim Kientzle 		mtx_lock(&(sc)->rx.lock);				\
351ae6aefafSTim Kientzle } while (0)
352ae6aefafSTim Kientzle 
353ae6aefafSTim Kientzle #define	CPSW_RX_UNLOCK(sc)		mtx_unlock(&(sc)->rx.lock)
354ae6aefafSTim Kientzle #define	CPSW_RX_LOCK_ASSERT(sc)	mtx_assert(&(sc)->rx.lock, MA_OWNED)
355ae6aefafSTim Kientzle 
35623cd11b6SLuiz Otavio O Souza #define CPSW_PORT_LOCK(_sc) do {					\
35723cd11b6SLuiz Otavio O Souza 		mtx_assert(&(_sc)->lock, MA_NOTOWNED);			\
35823cd11b6SLuiz Otavio O Souza 		mtx_lock(&(_sc)->lock);					\
35923cd11b6SLuiz Otavio O Souza } while (0)
36023cd11b6SLuiz Otavio O Souza 
36123cd11b6SLuiz Otavio O Souza #define	CPSW_PORT_UNLOCK(_sc)	mtx_unlock(&(_sc)->lock)
36223cd11b6SLuiz Otavio O Souza #define	CPSW_PORT_LOCK_ASSERT(_sc)	mtx_assert(&(_sc)->lock, MA_OWNED)
36323cd11b6SLuiz Otavio O Souza 
364ae6aefafSTim Kientzle /*
365ae6aefafSTim Kientzle  * Read/Write macros
366ae6aefafSTim Kientzle  */
36723cd11b6SLuiz Otavio O Souza #define	cpsw_read_4(_sc, _reg)		bus_read_4((_sc)->mem_res, (_reg))
36823cd11b6SLuiz Otavio O Souza #define	cpsw_write_4(_sc, _reg, _val)					\
36923cd11b6SLuiz Otavio O Souza 	bus_write_4((_sc)->mem_res, (_reg), (_val))
370ae6aefafSTim Kientzle 
371ae6aefafSTim Kientzle #define	cpsw_cpdma_bd_offset(i)	(CPSW_CPPI_RAM_OFFSET + ((i)*16))
372ae6aefafSTim Kientzle 
373ae6aefafSTim Kientzle #define	cpsw_cpdma_bd_paddr(sc, slot)					\
3745b03aba6SOleksandr Tymoshenko 	BUS_SPACE_PHYSADDR(sc->mem_res, slot->bd_offset)
375ae6aefafSTim Kientzle #define	cpsw_cpdma_read_bd(sc, slot, val)				\
3765b03aba6SOleksandr Tymoshenko 	bus_read_region_4(sc->mem_res, slot->bd_offset, (uint32_t *) val, 4)
377ae6aefafSTim Kientzle #define	cpsw_cpdma_write_bd(sc, slot, val)				\
3785b03aba6SOleksandr Tymoshenko 	bus_write_region_4(sc->mem_res, slot->bd_offset, (uint32_t *) val, 4)
379ae6aefafSTim Kientzle #define	cpsw_cpdma_write_bd_next(sc, slot, next_slot)			\
380ae6aefafSTim Kientzle 	cpsw_write_4(sc, slot->bd_offset, cpsw_cpdma_bd_paddr(sc, next_slot))
381ba14258fSLuiz Otavio O Souza #define	cpsw_cpdma_write_bd_flags(sc, slot, val)			\
382ba14258fSLuiz Otavio O Souza 	bus_write_2(sc->mem_res, slot->bd_offset + 14, val)
383ae6aefafSTim Kientzle #define	cpsw_cpdma_read_bd_flags(sc, slot)				\
3845b03aba6SOleksandr Tymoshenko 	bus_read_2(sc->mem_res, slot->bd_offset + 14)
385ae6aefafSTim Kientzle #define	cpsw_write_hdp_slot(sc, queue, slot)				\
386ae6aefafSTim Kientzle 	cpsw_write_4(sc, (queue)->hdp_offset, cpsw_cpdma_bd_paddr(sc, slot))
387ae6aefafSTim Kientzle #define	CP_OFFSET (CPSW_CPDMA_TX_CP(0) - CPSW_CPDMA_TX_HDP(0))
388ae6aefafSTim Kientzle #define	cpsw_read_cp(sc, queue)						\
389ae6aefafSTim Kientzle 	cpsw_read_4(sc, (queue)->hdp_offset + CP_OFFSET)
390ae6aefafSTim Kientzle #define	cpsw_write_cp(sc, queue, val)					\
391ae6aefafSTim Kientzle 	cpsw_write_4(sc, (queue)->hdp_offset + CP_OFFSET, (val))
392ae6aefafSTim Kientzle #define	cpsw_write_cp_slot(sc, queue, slot)				\
393ae6aefafSTim Kientzle 	cpsw_write_cp(sc, queue, cpsw_cpdma_bd_paddr(sc, slot))
394ae6aefafSTim Kientzle 
395ae6aefafSTim Kientzle #if 0
396ae6aefafSTim Kientzle /* XXX temporary function versions for debugging. */
397ae6aefafSTim Kientzle static void
398ae6aefafSTim Kientzle cpsw_write_hdp_slotX(struct cpsw_softc *sc, struct cpsw_queue *queue, struct cpsw_slot *slot)
399ae6aefafSTim Kientzle {
400ae6aefafSTim Kientzle 	uint32_t reg = queue->hdp_offset;
401ae6aefafSTim Kientzle 	uint32_t v = cpsw_cpdma_bd_paddr(sc, slot);
402ae6aefafSTim Kientzle 	CPSW_DEBUGF(("HDP <=== 0x%08x (was 0x%08x)", v, cpsw_read_4(sc, reg)));
403ae6aefafSTim Kientzle 	cpsw_write_4(sc, reg, v);
404ae6aefafSTim Kientzle }
405ae6aefafSTim Kientzle 
406ae6aefafSTim Kientzle static void
407ae6aefafSTim Kientzle cpsw_write_cp_slotX(struct cpsw_softc *sc, struct cpsw_queue *queue, struct cpsw_slot *slot)
408ae6aefafSTim Kientzle {
409ae6aefafSTim Kientzle 	uint32_t v = cpsw_cpdma_bd_paddr(sc, slot);
410ae6aefafSTim Kientzle 	CPSW_DEBUGF(("CP <=== 0x%08x (expecting 0x%08x)", v, cpsw_read_cp(sc, queue)));
411ae6aefafSTim Kientzle 	cpsw_write_cp(sc, queue, v);
412ae6aefafSTim Kientzle }
413ae6aefafSTim Kientzle #endif
414ae6aefafSTim Kientzle 
415ae6aefafSTim Kientzle /*
416ae6aefafSTim Kientzle  * Expanded dump routines for verbose debugging.
417ae6aefafSTim Kientzle  */
418ae6aefafSTim Kientzle static void
cpsw_dump_slot(struct cpsw_softc * sc,struct cpsw_slot * slot)419ae6aefafSTim Kientzle cpsw_dump_slot(struct cpsw_softc *sc, struct cpsw_slot *slot)
420ae6aefafSTim Kientzle {
421ae6aefafSTim Kientzle 	static const char *flags[] = {"SOP", "EOP", "Owner", "EOQ",
422ae6aefafSTim Kientzle 	    "TDownCmplt", "PassCRC", "Long", "Short", "MacCtl", "Overrun",
423ae6aefafSTim Kientzle 	    "PktErr1", "PortEn/PktErr0", "RxVlanEncap", "Port2", "Port1",
424ae6aefafSTim Kientzle 	    "Port0"};
425ae6aefafSTim Kientzle 	struct cpsw_cpdma_bd bd;
426ae6aefafSTim Kientzle 	const char *sep;
427ae6aefafSTim Kientzle 	int i;
428ae6aefafSTim Kientzle 
429ae6aefafSTim Kientzle 	cpsw_cpdma_read_bd(sc, slot, &bd);
430ba14258fSLuiz Otavio O Souza 	printf("BD Addr : 0x%08x   Next  : 0x%08x\n",
431ba14258fSLuiz Otavio O Souza 	    cpsw_cpdma_bd_paddr(sc, slot), bd.next);
432ae6aefafSTim Kientzle 	printf("  BufPtr: 0x%08x   BufLen: 0x%08x\n", bd.bufptr, bd.buflen);
433ae6aefafSTim Kientzle 	printf("  BufOff: 0x%08x   PktLen: 0x%08x\n", bd.bufoff, bd.pktlen);
434ae6aefafSTim Kientzle 	printf("  Flags: ");
435ae6aefafSTim Kientzle 	sep = "";
436ae6aefafSTim Kientzle 	for (i = 0; i < 16; ++i) {
437ae6aefafSTim Kientzle 		if (bd.flags & (1 << (15 - i))) {
438ae6aefafSTim Kientzle 			printf("%s%s", sep, flags[i]);
439ae6aefafSTim Kientzle 			sep = ",";
440ae6aefafSTim Kientzle 		}
441ae6aefafSTim Kientzle 	}
442ae6aefafSTim Kientzle 	printf("\n");
443ae6aefafSTim Kientzle 	if (slot->mbuf) {
444ae6aefafSTim Kientzle 		printf("  Ether:  %14D\n",
4456333a401SBjoern A. Zeeb 		    (char *)(slot->mbuf->m_data), " ");
446ae6aefafSTim Kientzle 		printf("  Packet: %16D\n",
4476333a401SBjoern A. Zeeb 		    (char *)(slot->mbuf->m_data) + 14, " ");
448ae6aefafSTim Kientzle 	}
449ae6aefafSTim Kientzle }
450ae6aefafSTim Kientzle 
451ae6aefafSTim Kientzle #define	CPSW_DUMP_SLOT(cs, slot) do {				\
452ae6aefafSTim Kientzle 	IF_DEBUG(sc) {						\
453ae6aefafSTim Kientzle 		cpsw_dump_slot(sc, slot);			\
454ae6aefafSTim Kientzle 	}							\
455ae6aefafSTim Kientzle } while (0)
456ae6aefafSTim Kientzle 
457ae6aefafSTim Kientzle static void
cpsw_dump_queue(struct cpsw_softc * sc,struct cpsw_slots * q)458ae6aefafSTim Kientzle cpsw_dump_queue(struct cpsw_softc *sc, struct cpsw_slots *q)
459ae6aefafSTim Kientzle {
460ae6aefafSTim Kientzle 	struct cpsw_slot *slot;
461ae6aefafSTim Kientzle 	int i = 0;
462ae6aefafSTim Kientzle 	int others = 0;
463ae6aefafSTim Kientzle 
464ae6aefafSTim Kientzle 	STAILQ_FOREACH(slot, q, next) {
465ba14258fSLuiz Otavio O Souza 		if (i > CPSW_TXFRAGS)
466ae6aefafSTim Kientzle 			++others;
467ae6aefafSTim Kientzle 		else
468ae6aefafSTim Kientzle 			cpsw_dump_slot(sc, slot);
469ae6aefafSTim Kientzle 		++i;
470ae6aefafSTim Kientzle 	}
471ae6aefafSTim Kientzle 	if (others)
472ae6aefafSTim Kientzle 		printf(" ... and %d more.\n", others);
473ae6aefafSTim Kientzle 	printf("\n");
474ae6aefafSTim Kientzle }
475ae6aefafSTim Kientzle 
476ae6aefafSTim Kientzle #define CPSW_DUMP_QUEUE(sc, q) do {				\
477ae6aefafSTim Kientzle 	IF_DEBUG(sc) {						\
478ae6aefafSTim Kientzle 		cpsw_dump_queue(sc, q);				\
479ae6aefafSTim Kientzle 	}							\
480ae6aefafSTim Kientzle } while (0)
481ae6aefafSTim Kientzle 
482ae6aefafSTim Kientzle static void
cpsw_init_slots(struct cpsw_softc * sc)483ae6aefafSTim Kientzle cpsw_init_slots(struct cpsw_softc *sc)
484ae6aefafSTim Kientzle {
485ae6aefafSTim Kientzle 	struct cpsw_slot *slot;
486ae6aefafSTim Kientzle 	int i;
487ae6aefafSTim Kientzle 
488ae6aefafSTim Kientzle 	STAILQ_INIT(&sc->avail);
489ae6aefafSTim Kientzle 
490ae6aefafSTim Kientzle 	/* Put the slot descriptors onto the global avail list. */
49133495e5dSPedro F. Giffuni 	for (i = 0; i < nitems(sc->_slots); i++) {
492ae6aefafSTim Kientzle 		slot = &sc->_slots[i];
493ae6aefafSTim Kientzle 		slot->bd_offset = cpsw_cpdma_bd_offset(i);
494ae6aefafSTim Kientzle 		STAILQ_INSERT_TAIL(&sc->avail, slot, next);
495ae6aefafSTim Kientzle 	}
496ae6aefafSTim Kientzle }
497ae6aefafSTim Kientzle 
498ae6aefafSTim Kientzle static int
cpsw_add_slots(struct cpsw_softc * sc,struct cpsw_queue * queue,int requested)499ae6aefafSTim Kientzle cpsw_add_slots(struct cpsw_softc *sc, struct cpsw_queue *queue, int requested)
500ae6aefafSTim Kientzle {
50133495e5dSPedro F. Giffuni 	const int max_slots = nitems(sc->_slots);
502ae6aefafSTim Kientzle 	struct cpsw_slot *slot;
503ae6aefafSTim Kientzle 	int i;
504ae6aefafSTim Kientzle 
505ae6aefafSTim Kientzle 	if (requested < 0)
506ae6aefafSTim Kientzle 		requested = max_slots;
507ae6aefafSTim Kientzle 
508ae6aefafSTim Kientzle 	for (i = 0; i < requested; ++i) {
509ae6aefafSTim Kientzle 		slot = STAILQ_FIRST(&sc->avail);
510ae6aefafSTim Kientzle 		if (slot == NULL)
511ae6aefafSTim Kientzle 			return (0);
512ae6aefafSTim Kientzle 		if (bus_dmamap_create(sc->mbuf_dtag, 0, &slot->dmamap)) {
51323cd11b6SLuiz Otavio O Souza 			device_printf(sc->dev, "failed to create dmamap\n");
514ae6aefafSTim Kientzle 			return (ENOMEM);
515ae6aefafSTim Kientzle 		}
516ae6aefafSTim Kientzle 		STAILQ_REMOVE_HEAD(&sc->avail, next);
517ae6aefafSTim Kientzle 		STAILQ_INSERT_TAIL(&queue->avail, slot, next);
518ae6aefafSTim Kientzle 		++queue->avail_queue_len;
519ae6aefafSTim Kientzle 		++queue->queue_slots;
520ae6aefafSTim Kientzle 	}
521ae6aefafSTim Kientzle 	return (0);
522ae6aefafSTim Kientzle }
523ae6aefafSTim Kientzle 
5245bf32555STim Kientzle static void
cpsw_free_slot(struct cpsw_softc * sc,struct cpsw_slot * slot)5255bf32555STim Kientzle cpsw_free_slot(struct cpsw_softc *sc, struct cpsw_slot *slot)
526e53470feSOleksandr Tymoshenko {
52769c595edSJohn Baldwin 	int error __diagused;
528e53470feSOleksandr Tymoshenko 
5295bf32555STim Kientzle 	if (slot->dmamap) {
53023cd11b6SLuiz Otavio O Souza 		if (slot->mbuf)
53123cd11b6SLuiz Otavio O Souza 			bus_dmamap_unload(sc->mbuf_dtag, slot->dmamap);
5325bf32555STim Kientzle 		error = bus_dmamap_destroy(sc->mbuf_dtag, slot->dmamap);
5335bf32555STim Kientzle 		KASSERT(error == 0, ("Mapping still active"));
5345bf32555STim Kientzle 		slot->dmamap = NULL;
535e53470feSOleksandr Tymoshenko 	}
5365bf32555STim Kientzle 	if (slot->mbuf) {
5375bf32555STim Kientzle 		m_freem(slot->mbuf);
5385bf32555STim Kientzle 		slot->mbuf = NULL;
539e53470feSOleksandr Tymoshenko 	}
540e53470feSOleksandr Tymoshenko }
541e53470feSOleksandr Tymoshenko 
542ae6aefafSTim Kientzle static void
cpsw_reset(struct cpsw_softc * sc)543ae6aefafSTim Kientzle cpsw_reset(struct cpsw_softc *sc)
544ae6aefafSTim Kientzle {
545ae6aefafSTim Kientzle 	int i;
546ae6aefafSTim Kientzle 
54723cd11b6SLuiz Otavio O Souza 	callout_stop(&sc->watchdog.callout);
54823cd11b6SLuiz Otavio O Souza 
549ae6aefafSTim Kientzle 	/* Reset RMII/RGMII wrapper. */
550ae6aefafSTim Kientzle 	cpsw_write_4(sc, CPSW_WR_SOFT_RESET, 1);
551ae6aefafSTim Kientzle 	while (cpsw_read_4(sc, CPSW_WR_SOFT_RESET) & 1)
552ae6aefafSTim Kientzle 		;
553ae6aefafSTim Kientzle 
554ae6aefafSTim Kientzle 	/* Disable TX and RX interrupts for all cores. */
555ae6aefafSTim Kientzle 	for (i = 0; i < 3; ++i) {
556ae6aefafSTim Kientzle 		cpsw_write_4(sc, CPSW_WR_C_RX_THRESH_EN(i), 0x00);
557ae6aefafSTim Kientzle 		cpsw_write_4(sc, CPSW_WR_C_TX_EN(i), 0x00);
558ae6aefafSTim Kientzle 		cpsw_write_4(sc, CPSW_WR_C_RX_EN(i), 0x00);
559ae6aefafSTim Kientzle 		cpsw_write_4(sc, CPSW_WR_C_MISC_EN(i), 0x00);
560ae6aefafSTim Kientzle 	}
561ae6aefafSTim Kientzle 
562ae6aefafSTim Kientzle 	/* Reset CPSW subsystem. */
563ae6aefafSTim Kientzle 	cpsw_write_4(sc, CPSW_SS_SOFT_RESET, 1);
564ae6aefafSTim Kientzle 	while (cpsw_read_4(sc, CPSW_SS_SOFT_RESET) & 1)
565ae6aefafSTim Kientzle 		;
566ae6aefafSTim Kientzle 
567ae6aefafSTim Kientzle 	/* Reset Sliver port 1 and 2 */
568ae6aefafSTim Kientzle 	for (i = 0; i < 2; i++) {
569ae6aefafSTim Kientzle 		/* Reset */
570ae6aefafSTim Kientzle 		cpsw_write_4(sc, CPSW_SL_SOFT_RESET(i), 1);
571ae6aefafSTim Kientzle 		while (cpsw_read_4(sc, CPSW_SL_SOFT_RESET(i)) & 1)
572ae6aefafSTim Kientzle 			;
573ae6aefafSTim Kientzle 	}
574ae6aefafSTim Kientzle 
575ae6aefafSTim Kientzle 	/* Reset DMA controller. */
576ae6aefafSTim Kientzle 	cpsw_write_4(sc, CPSW_CPDMA_SOFT_RESET, 1);
577ae6aefafSTim Kientzle 	while (cpsw_read_4(sc, CPSW_CPDMA_SOFT_RESET) & 1)
578ae6aefafSTim Kientzle 		;
579ae6aefafSTim Kientzle 
580ae6aefafSTim Kientzle 	/* Disable TX & RX DMA */
581ae6aefafSTim Kientzle 	cpsw_write_4(sc, CPSW_CPDMA_TX_CONTROL, 0);
582ae6aefafSTim Kientzle 	cpsw_write_4(sc, CPSW_CPDMA_RX_CONTROL, 0);
583ae6aefafSTim Kientzle 
584ae6aefafSTim Kientzle 	/* Clear all queues. */
585ae6aefafSTim Kientzle 	for (i = 0; i < 8; i++) {
586ae6aefafSTim Kientzle 		cpsw_write_4(sc, CPSW_CPDMA_TX_HDP(i), 0);
587ae6aefafSTim Kientzle 		cpsw_write_4(sc, CPSW_CPDMA_RX_HDP(i), 0);
588ae6aefafSTim Kientzle 		cpsw_write_4(sc, CPSW_CPDMA_TX_CP(i), 0);
589ae6aefafSTim Kientzle 		cpsw_write_4(sc, CPSW_CPDMA_RX_CP(i), 0);
590ae6aefafSTim Kientzle 	}
591ae6aefafSTim Kientzle 
592ae6aefafSTim Kientzle 	/* Clear all interrupt Masks */
593ae6aefafSTim Kientzle 	cpsw_write_4(sc, CPSW_CPDMA_RX_INTMASK_CLEAR, 0xFFFFFFFF);
594ae6aefafSTim Kientzle 	cpsw_write_4(sc, CPSW_CPDMA_TX_INTMASK_CLEAR, 0xFFFFFFFF);
595ae6aefafSTim Kientzle }
596ae6aefafSTim Kientzle 
597ae6aefafSTim Kientzle static void
cpsw_init(struct cpsw_softc * sc)59823cd11b6SLuiz Otavio O Souza cpsw_init(struct cpsw_softc *sc)
599ae6aefafSTim Kientzle {
600ae6aefafSTim Kientzle 	struct cpsw_slot *slot;
60123cd11b6SLuiz Otavio O Souza 	uint32_t reg;
602ae6aefafSTim Kientzle 
603feeb22f3SLuiz Otavio O Souza 	/* Disable the interrupt pacing. */
604feeb22f3SLuiz Otavio O Souza 	reg = cpsw_read_4(sc, CPSW_WR_INT_CONTROL);
605feeb22f3SLuiz Otavio O Souza 	reg &= ~(CPSW_WR_INT_PACE_EN | CPSW_WR_INT_PRESCALE_MASK);
606feeb22f3SLuiz Otavio O Souza 	cpsw_write_4(sc, CPSW_WR_INT_CONTROL, reg);
607feeb22f3SLuiz Otavio O Souza 
60823cd11b6SLuiz Otavio O Souza 	/* Clear ALE */
60923cd11b6SLuiz Otavio O Souza 	cpsw_write_4(sc, CPSW_ALE_CONTROL, CPSW_ALE_CTL_CLEAR_TBL);
610ae6aefafSTim Kientzle 
611ae6aefafSTim Kientzle 	/* Enable ALE */
61223cd11b6SLuiz Otavio O Souza 	reg = CPSW_ALE_CTL_ENABLE;
61323cd11b6SLuiz Otavio O Souza 	if (sc->dualemac)
61423cd11b6SLuiz Otavio O Souza 		reg |= CPSW_ALE_CTL_VLAN_AWARE;
61523cd11b6SLuiz Otavio O Souza 	cpsw_write_4(sc, CPSW_ALE_CONTROL, reg);
616ae6aefafSTim Kientzle 
61723cd11b6SLuiz Otavio O Souza 	/* Set Host Port Mapping. */
618ae6aefafSTim Kientzle 	cpsw_write_4(sc, CPSW_PORT_P0_CPDMA_TX_PRI_MAP, 0x76543210);
619ae6aefafSTim Kientzle 	cpsw_write_4(sc, CPSW_PORT_P0_CPDMA_RX_CH_MAP, 0);
620ae6aefafSTim Kientzle 
62123cd11b6SLuiz Otavio O Souza 	/* Initialize ALE: set host port to forwarding(3). */
622a2c46b94SLuiz Otavio O Souza 	cpsw_write_4(sc, CPSW_ALE_PORTCTL(0),
623a2c46b94SLuiz Otavio O Souza 	    ALE_PORTCTL_INGRESS | ALE_PORTCTL_FORWARD);
624ae6aefafSTim Kientzle 
625ae6aefafSTim Kientzle 	cpsw_write_4(sc, CPSW_SS_PTYPE, 0);
626ae6aefafSTim Kientzle 
627ae6aefafSTim Kientzle 	/* Enable statistics for ports 0, 1 and 2 */
628ae6aefafSTim Kientzle 	cpsw_write_4(sc, CPSW_SS_STAT_PORT_EN, 7);
629ae6aefafSTim Kientzle 
630ba14258fSLuiz Otavio O Souza 	/* Turn off flow control. */
631ae6aefafSTim Kientzle 	cpsw_write_4(sc, CPSW_SS_FLOW_CONTROL, 0);
632ae6aefafSTim Kientzle 
633ae6aefafSTim Kientzle 	/* Make IP hdr aligned with 4 */
634ae6aefafSTim Kientzle 	cpsw_write_4(sc, CPSW_CPDMA_RX_BUFFER_OFFSET, 2);
635ae6aefafSTim Kientzle 
636ae6aefafSTim Kientzle 	/* Initialize RX Buffer Descriptors */
637ba14258fSLuiz Otavio O Souza 	cpsw_write_4(sc, CPSW_CPDMA_RX_PENDTHRESH(0), 0);
638ae6aefafSTim Kientzle 	cpsw_write_4(sc, CPSW_CPDMA_RX_FREEBUFFER(0), 0);
639ae6aefafSTim Kientzle 
640ae6aefafSTim Kientzle 	/* Enable TX & RX DMA */
641ae6aefafSTim Kientzle 	cpsw_write_4(sc, CPSW_CPDMA_TX_CONTROL, 1);
642ae6aefafSTim Kientzle 	cpsw_write_4(sc, CPSW_CPDMA_RX_CONTROL, 1);
643ae6aefafSTim Kientzle 
644ae6aefafSTim Kientzle 	/* Enable Interrupts for core 0 */
645ae6aefafSTim Kientzle 	cpsw_write_4(sc, CPSW_WR_C_RX_THRESH_EN(0), 0xFF);
646ae6aefafSTim Kientzle 	cpsw_write_4(sc, CPSW_WR_C_RX_EN(0), 0xFF);
647430d5eb4SLuiz Otavio O Souza 	cpsw_write_4(sc, CPSW_WR_C_TX_EN(0), 0xFF);
64823cd11b6SLuiz Otavio O Souza 	cpsw_write_4(sc, CPSW_WR_C_MISC_EN(0), 0x1F);
649ae6aefafSTim Kientzle 
650ae6aefafSTim Kientzle 	/* Enable host Error Interrupt */
651ae6aefafSTim Kientzle 	cpsw_write_4(sc, CPSW_CPDMA_DMA_INTMASK_SET, 3);
652ae6aefafSTim Kientzle 
653430d5eb4SLuiz Otavio O Souza 	/* Enable interrupts for RX and TX on Channel 0 */
654ba14258fSLuiz Otavio O Souza 	cpsw_write_4(sc, CPSW_CPDMA_RX_INTMASK_SET,
655ba14258fSLuiz Otavio O Souza 	    CPSW_CPDMA_RX_INT(0) | CPSW_CPDMA_RX_INT_THRESH(0));
656430d5eb4SLuiz Otavio O Souza 	cpsw_write_4(sc, CPSW_CPDMA_TX_INTMASK_SET, 1);
657ae6aefafSTim Kientzle 
658ae6aefafSTim Kientzle 	/* Initialze MDIO - ENABLE, PREAMBLE=0, FAULTENB, CLKDIV=0xFF */
659ae6aefafSTim Kientzle 	/* TODO Calculate MDCLK=CLK/(CLKDIV+1) */
66023cd11b6SLuiz Otavio O Souza 	cpsw_write_4(sc, MDIOCONTROL, MDIOCTL_ENABLE | MDIOCTL_FAULTENB | 0xff);
661ae6aefafSTim Kientzle 
662ae6aefafSTim Kientzle 	/* Select MII in GMII_SEL, Internal Delay mode */
663ae6aefafSTim Kientzle 	//ti_scm_reg_write_4(0x650, 0);
664ae6aefafSTim Kientzle 
665ae6aefafSTim Kientzle 	/* Initialize active queues. */
666ae6aefafSTim Kientzle 	slot = STAILQ_FIRST(&sc->tx.active);
667ae6aefafSTim Kientzle 	if (slot != NULL)
668ae6aefafSTim Kientzle 		cpsw_write_hdp_slot(sc, &sc->tx, slot);
669ae6aefafSTim Kientzle 	slot = STAILQ_FIRST(&sc->rx.active);
670ae6aefafSTim Kientzle 	if (slot != NULL)
671ae6aefafSTim Kientzle 		cpsw_write_hdp_slot(sc, &sc->rx, slot);
672ae6aefafSTim Kientzle 	cpsw_rx_enqueue(sc);
673ba14258fSLuiz Otavio O Souza 	cpsw_write_4(sc, CPSW_CPDMA_RX_FREEBUFFER(0), sc->rx.active_queue_len);
674ba14258fSLuiz Otavio O Souza 	cpsw_write_4(sc, CPSW_CPDMA_RX_PENDTHRESH(0), CPSW_TXFRAGS);
675ae6aefafSTim Kientzle 
67623cd11b6SLuiz Otavio O Souza 	/* Activate network interface. */
677ae6aefafSTim Kientzle 	sc->rx.running = 1;
678ae6aefafSTim Kientzle 	sc->tx.running = 1;
679ae6aefafSTim Kientzle 	sc->watchdog.timer = 0;
68023cd11b6SLuiz Otavio O Souza 	callout_init(&sc->watchdog.callout, 0);
68123cd11b6SLuiz Otavio O Souza 	callout_reset(&sc->watchdog.callout, hz, cpsw_tx_watchdog, sc);
68223cd11b6SLuiz Otavio O Souza }
683ae6aefafSTim Kientzle 
68423cd11b6SLuiz Otavio O Souza /*
68523cd11b6SLuiz Otavio O Souza  *
68623cd11b6SLuiz Otavio O Souza  * Device Probe, Attach, Detach.
68723cd11b6SLuiz Otavio O Souza  *
68823cd11b6SLuiz Otavio O Souza  */
68923cd11b6SLuiz Otavio O Souza 
69023cd11b6SLuiz Otavio O Souza static int
cpsw_probe(device_t dev)69123cd11b6SLuiz Otavio O Souza cpsw_probe(device_t dev)
69223cd11b6SLuiz Otavio O Souza {
69323cd11b6SLuiz Otavio O Souza 
69423cd11b6SLuiz Otavio O Souza 	if (!ofw_bus_status_okay(dev))
69523cd11b6SLuiz Otavio O Souza 		return (ENXIO);
69623cd11b6SLuiz Otavio O Souza 
69723cd11b6SLuiz Otavio O Souza 	if (!ofw_bus_is_compatible(dev, "ti,cpsw"))
69823cd11b6SLuiz Otavio O Souza 		return (ENXIO);
69923cd11b6SLuiz Otavio O Souza 
70023cd11b6SLuiz Otavio O Souza 	device_set_desc(dev, "3-port Switch Ethernet Subsystem");
70123cd11b6SLuiz Otavio O Souza 	return (BUS_PROBE_DEFAULT);
70223cd11b6SLuiz Otavio O Souza }
70323cd11b6SLuiz Otavio O Souza 
70423cd11b6SLuiz Otavio O Souza static int
cpsw_intr_attach(struct cpsw_softc * sc)70523cd11b6SLuiz Otavio O Souza cpsw_intr_attach(struct cpsw_softc *sc)
70623cd11b6SLuiz Otavio O Souza {
707430d5eb4SLuiz Otavio O Souza 	int i;
70823cd11b6SLuiz Otavio O Souza 
709430d5eb4SLuiz Otavio O Souza 	for (i = 0; i < CPSW_INTR_COUNT; i++) {
710430d5eb4SLuiz Otavio O Souza 		if (bus_setup_intr(sc->dev, sc->irq_res[i],
711430d5eb4SLuiz Otavio O Souza 		    INTR_TYPE_NET | INTR_MPSAFE, NULL,
712430d5eb4SLuiz Otavio O Souza 		    cpsw_intr_cb[i].cb, sc, &sc->ih_cookie[i]) != 0) {
71323cd11b6SLuiz Otavio O Souza 			return (-1);
71423cd11b6SLuiz Otavio O Souza 		}
71523cd11b6SLuiz Otavio O Souza 	}
71623cd11b6SLuiz Otavio O Souza 
71723cd11b6SLuiz Otavio O Souza 	return (0);
71823cd11b6SLuiz Otavio O Souza }
71923cd11b6SLuiz Otavio O Souza 
72023cd11b6SLuiz Otavio O Souza static void
cpsw_intr_detach(struct cpsw_softc * sc)72123cd11b6SLuiz Otavio O Souza cpsw_intr_detach(struct cpsw_softc *sc)
72223cd11b6SLuiz Otavio O Souza {
72323cd11b6SLuiz Otavio O Souza 	int i;
72423cd11b6SLuiz Otavio O Souza 
72523cd11b6SLuiz Otavio O Souza 	for (i = 0; i < CPSW_INTR_COUNT; i++) {
72623cd11b6SLuiz Otavio O Souza 		if (sc->ih_cookie[i]) {
72723cd11b6SLuiz Otavio O Souza 			bus_teardown_intr(sc->dev, sc->irq_res[i],
72823cd11b6SLuiz Otavio O Souza 			    sc->ih_cookie[i]);
72923cd11b6SLuiz Otavio O Souza 		}
73023cd11b6SLuiz Otavio O Souza 	}
73123cd11b6SLuiz Otavio O Souza }
73223cd11b6SLuiz Otavio O Souza 
73323cd11b6SLuiz Otavio O Souza static int
cpsw_get_fdt_data(struct cpsw_softc * sc,int port)73423cd11b6SLuiz Otavio O Souza cpsw_get_fdt_data(struct cpsw_softc *sc, int port)
73523cd11b6SLuiz Otavio O Souza {
73623cd11b6SLuiz Otavio O Souza 	char *name;
73723cd11b6SLuiz Otavio O Souza 	int len, phy, vlan;
73823cd11b6SLuiz Otavio O Souza 	pcell_t phy_id[3], vlan_id;
73923cd11b6SLuiz Otavio O Souza 	phandle_t child;
74023cd11b6SLuiz Otavio O Souza 	unsigned long mdio_child_addr;
74123cd11b6SLuiz Otavio O Souza 
7425eb26411SEd Maste 	/* Find any slave with phy-handle/phy_id */
74323cd11b6SLuiz Otavio O Souza 	phy = -1;
74423cd11b6SLuiz Otavio O Souza 	vlan = -1;
74523cd11b6SLuiz Otavio O Souza 	for (child = OF_child(sc->node); child != 0; child = OF_peer(child)) {
746217d17bcSOleksandr Tymoshenko 		if (OF_getprop_alloc(child, "name", (void **)&name) < 0)
74723cd11b6SLuiz Otavio O Souza 			continue;
748e5b16f1dSSvatopluk Kraus 		if (sscanf(name, "slave@%lx", &mdio_child_addr) != 1) {
749bc90a48cSOleksandr Tymoshenko 			OF_prop_free(name);
75023cd11b6SLuiz Otavio O Souza 			continue;
75123cd11b6SLuiz Otavio O Souza 		}
752bc90a48cSOleksandr Tymoshenko 		OF_prop_free(name);
7530ce33c04SEmmanuel Vadot 
7540ce33c04SEmmanuel Vadot 		if (mdio_child_addr != slave_mdio_addr[port] &&
7550ce33c04SEmmanuel Vadot 		    mdio_child_addr != (slave_mdio_addr[port] & 0xFFF))
75623cd11b6SLuiz Otavio O Souza 			continue;
75723cd11b6SLuiz Otavio O Souza 
7585eb26411SEd Maste 		if (fdt_get_phyaddr(child, NULL, &phy, NULL) != 0){
7595eb26411SEd Maste 			/* Users with old DTB will have phy_id instead */
7605eb26411SEd Maste 			phy = -1;
76123cd11b6SLuiz Otavio O Souza 			len = OF_getproplen(child, "phy_id");
76223cd11b6SLuiz Otavio O Souza 			if (len / sizeof(pcell_t) == 2) {
76323cd11b6SLuiz Otavio O Souza 				/* Get phy address from fdt */
76423cd11b6SLuiz Otavio O Souza 				if (OF_getencprop(child, "phy_id", phy_id, len) > 0)
76523cd11b6SLuiz Otavio O Souza 					phy = phy_id[1];
76623cd11b6SLuiz Otavio O Souza 			}
7675eb26411SEd Maste 		}
76823cd11b6SLuiz Otavio O Souza 
76923cd11b6SLuiz Otavio O Souza 		len = OF_getproplen(child, "dual_emac_res_vlan");
77023cd11b6SLuiz Otavio O Souza 		if (len / sizeof(pcell_t) == 1) {
77123cd11b6SLuiz Otavio O Souza 			/* Get phy address from fdt */
77223cd11b6SLuiz Otavio O Souza 			if (OF_getencprop(child, "dual_emac_res_vlan",
77323cd11b6SLuiz Otavio O Souza 			    &vlan_id, len) > 0) {
77423cd11b6SLuiz Otavio O Souza 				vlan = vlan_id;
77523cd11b6SLuiz Otavio O Souza 			}
77623cd11b6SLuiz Otavio O Souza 		}
77723cd11b6SLuiz Otavio O Souza 
77823cd11b6SLuiz Otavio O Souza 		break;
77923cd11b6SLuiz Otavio O Souza 	}
78023cd11b6SLuiz Otavio O Souza 	if (phy == -1)
78123cd11b6SLuiz Otavio O Souza 		return (ENXIO);
78223cd11b6SLuiz Otavio O Souza 	sc->port[port].phy = phy;
78323cd11b6SLuiz Otavio O Souza 	sc->port[port].vlan = vlan;
78423cd11b6SLuiz Otavio O Souza 
78523cd11b6SLuiz Otavio O Souza 	return (0);
78623cd11b6SLuiz Otavio O Souza }
78723cd11b6SLuiz Otavio O Souza 
78823cd11b6SLuiz Otavio O Souza static int
cpsw_attach(device_t dev)78923cd11b6SLuiz Otavio O Souza cpsw_attach(device_t dev)
79023cd11b6SLuiz Otavio O Souza {
791904e8e89SLuiz Otavio O Souza 	int error, i;
79223cd11b6SLuiz Otavio O Souza 	struct cpsw_softc *sc;
79323cd11b6SLuiz Otavio O Souza 	uint32_t reg;
79423cd11b6SLuiz Otavio O Souza 
79523cd11b6SLuiz Otavio O Souza 	sc = device_get_softc(dev);
79623cd11b6SLuiz Otavio O Souza 	sc->dev = dev;
79723cd11b6SLuiz Otavio O Souza 	sc->node = ofw_bus_get_node(dev);
79823cd11b6SLuiz Otavio O Souza 	getbinuptime(&sc->attach_uptime);
79923cd11b6SLuiz Otavio O Souza 
80023cd11b6SLuiz Otavio O Souza 	if (OF_getencprop(sc->node, "active_slave", &sc->active_slave,
80123cd11b6SLuiz Otavio O Souza 	    sizeof(sc->active_slave)) <= 0) {
80223cd11b6SLuiz Otavio O Souza 		sc->active_slave = 0;
80323cd11b6SLuiz Otavio O Souza 	}
80423cd11b6SLuiz Otavio O Souza 	if (sc->active_slave > 1)
80523cd11b6SLuiz Otavio O Souza 		sc->active_slave = 1;
80623cd11b6SLuiz Otavio O Souza 
80723cd11b6SLuiz Otavio O Souza 	if (OF_hasprop(sc->node, "dual_emac"))
80823cd11b6SLuiz Otavio O Souza 		sc->dualemac = 1;
80923cd11b6SLuiz Otavio O Souza 
81023cd11b6SLuiz Otavio O Souza 	for (i = 0; i < CPSW_PORTS; i++) {
81123cd11b6SLuiz Otavio O Souza 		if (!sc->dualemac && i != sc->active_slave)
81223cd11b6SLuiz Otavio O Souza 			continue;
81323cd11b6SLuiz Otavio O Souza 		if (cpsw_get_fdt_data(sc, i) != 0) {
81423cd11b6SLuiz Otavio O Souza 			device_printf(dev,
81523cd11b6SLuiz Otavio O Souza 			    "failed to get PHY address from FDT\n");
81623cd11b6SLuiz Otavio O Souza 			return (ENXIO);
81723cd11b6SLuiz Otavio O Souza 		}
81823cd11b6SLuiz Otavio O Souza 	}
81923cd11b6SLuiz Otavio O Souza 
82023cd11b6SLuiz Otavio O Souza 	/* Initialize mutexes */
82123cd11b6SLuiz Otavio O Souza 	mtx_init(&sc->tx.lock, device_get_nameunit(dev),
82223cd11b6SLuiz Otavio O Souza 	    "cpsw TX lock", MTX_DEF);
82323cd11b6SLuiz Otavio O Souza 	mtx_init(&sc->rx.lock, device_get_nameunit(dev),
82423cd11b6SLuiz Otavio O Souza 	    "cpsw RX lock", MTX_DEF);
82523cd11b6SLuiz Otavio O Souza 
82623cd11b6SLuiz Otavio O Souza 	/* Allocate IRQ resources */
82723cd11b6SLuiz Otavio O Souza 	error = bus_alloc_resources(dev, irq_res_spec, sc->irq_res);
82823cd11b6SLuiz Otavio O Souza 	if (error) {
82923cd11b6SLuiz Otavio O Souza 		device_printf(dev, "could not allocate IRQ resources\n");
83023cd11b6SLuiz Otavio O Souza 		cpsw_detach(dev);
83123cd11b6SLuiz Otavio O Souza 		return (ENXIO);
83223cd11b6SLuiz Otavio O Souza 	}
83323cd11b6SLuiz Otavio O Souza 
83423cd11b6SLuiz Otavio O Souza 	sc->mem_rid = 0;
83523cd11b6SLuiz Otavio O Souza 	sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
83623cd11b6SLuiz Otavio O Souza 	    &sc->mem_rid, RF_ACTIVE);
83723cd11b6SLuiz Otavio O Souza 	if (sc->mem_res == NULL) {
83823cd11b6SLuiz Otavio O Souza 		device_printf(sc->dev, "failed to allocate memory resource\n");
83923cd11b6SLuiz Otavio O Souza 		cpsw_detach(dev);
84023cd11b6SLuiz Otavio O Souza 		return (ENXIO);
84123cd11b6SLuiz Otavio O Souza 	}
84223cd11b6SLuiz Otavio O Souza 
84323cd11b6SLuiz Otavio O Souza 	reg = cpsw_read_4(sc, CPSW_SS_IDVER);
84423cd11b6SLuiz Otavio O Souza 	device_printf(dev, "CPSW SS Version %d.%d (%d)\n", (reg >> 8 & 0x7),
84523cd11b6SLuiz Otavio O Souza 		reg & 0xFF, (reg >> 11) & 0x1F);
84623cd11b6SLuiz Otavio O Souza 
84723cd11b6SLuiz Otavio O Souza 	cpsw_add_sysctls(sc);
84823cd11b6SLuiz Otavio O Souza 
84923cd11b6SLuiz Otavio O Souza 	/* Allocate a busdma tag and DMA safe memory for mbufs. */
85023cd11b6SLuiz Otavio O Souza 	error = bus_dma_tag_create(
85123cd11b6SLuiz Otavio O Souza 		bus_get_dma_tag(sc->dev),	/* parent */
85223cd11b6SLuiz Otavio O Souza 		1, 0,				/* alignment, boundary */
85323cd11b6SLuiz Otavio O Souza 		BUS_SPACE_MAXADDR_32BIT,	/* lowaddr */
85423cd11b6SLuiz Otavio O Souza 		BUS_SPACE_MAXADDR,		/* highaddr */
85523cd11b6SLuiz Otavio O Souza 		NULL, NULL,			/* filtfunc, filtfuncarg */
85623cd11b6SLuiz Otavio O Souza 		MCLBYTES, CPSW_TXFRAGS,		/* maxsize, nsegments */
85723cd11b6SLuiz Otavio O Souza 		MCLBYTES, 0,			/* maxsegsz, flags */
85823cd11b6SLuiz Otavio O Souza 		NULL, NULL,			/* lockfunc, lockfuncarg */
85923cd11b6SLuiz Otavio O Souza 		&sc->mbuf_dtag);		/* dmatag */
86023cd11b6SLuiz Otavio O Souza 	if (error) {
86123cd11b6SLuiz Otavio O Souza 		device_printf(dev, "bus_dma_tag_create failed\n");
86223cd11b6SLuiz Otavio O Souza 		cpsw_detach(dev);
86323cd11b6SLuiz Otavio O Souza 		return (error);
86423cd11b6SLuiz Otavio O Souza 	}
86523cd11b6SLuiz Otavio O Souza 
866904e8e89SLuiz Otavio O Souza 	/* Allocate a NULL buffer for padding. */
867904e8e89SLuiz Otavio O Souza 	sc->nullpad = malloc(ETHER_MIN_LEN, M_DEVBUF, M_WAITOK | M_ZERO);
86823cd11b6SLuiz Otavio O Souza 
86923cd11b6SLuiz Otavio O Souza 	cpsw_init_slots(sc);
87023cd11b6SLuiz Otavio O Souza 
87123cd11b6SLuiz Otavio O Souza 	/* Allocate slots to TX and RX queues. */
87223cd11b6SLuiz Otavio O Souza 	STAILQ_INIT(&sc->rx.avail);
87323cd11b6SLuiz Otavio O Souza 	STAILQ_INIT(&sc->rx.active);
87423cd11b6SLuiz Otavio O Souza 	STAILQ_INIT(&sc->tx.avail);
87523cd11b6SLuiz Otavio O Souza 	STAILQ_INIT(&sc->tx.active);
87623cd11b6SLuiz Otavio O Souza 	// For now:  128 slots to TX, rest to RX.
87723cd11b6SLuiz Otavio O Souza 	// XXX TODO: start with 32/64 and grow dynamically based on demand.
87823cd11b6SLuiz Otavio O Souza 	if (cpsw_add_slots(sc, &sc->tx, 128) ||
87923cd11b6SLuiz Otavio O Souza 	    cpsw_add_slots(sc, &sc->rx, -1)) {
88023cd11b6SLuiz Otavio O Souza 		device_printf(dev, "failed to allocate dmamaps\n");
88123cd11b6SLuiz Otavio O Souza 		cpsw_detach(dev);
88223cd11b6SLuiz Otavio O Souza 		return (ENOMEM);
88323cd11b6SLuiz Otavio O Souza 	}
88423cd11b6SLuiz Otavio O Souza 	device_printf(dev, "Initial queue size TX=%d RX=%d\n",
88523cd11b6SLuiz Otavio O Souza 	    sc->tx.queue_slots, sc->rx.queue_slots);
88623cd11b6SLuiz Otavio O Souza 
88723cd11b6SLuiz Otavio O Souza 	sc->tx.hdp_offset = CPSW_CPDMA_TX_HDP(0);
88823cd11b6SLuiz Otavio O Souza 	sc->rx.hdp_offset = CPSW_CPDMA_RX_HDP(0);
88923cd11b6SLuiz Otavio O Souza 
89023cd11b6SLuiz Otavio O Souza 	if (cpsw_intr_attach(sc) == -1) {
89123cd11b6SLuiz Otavio O Souza 		device_printf(dev, "failed to setup interrupts\n");
89223cd11b6SLuiz Otavio O Souza 		cpsw_detach(dev);
89323cd11b6SLuiz Otavio O Souza 		return (ENXIO);
89423cd11b6SLuiz Otavio O Souza 	}
89523cd11b6SLuiz Otavio O Souza 
896a2c46b94SLuiz Otavio O Souza #ifdef CPSW_ETHERSWITCH
897a2c46b94SLuiz Otavio O Souza 	for (i = 0; i < CPSW_VLANS; i++)
898a2c46b94SLuiz Otavio O Souza 		cpsw_vgroups[i].vid = -1;
899a2c46b94SLuiz Otavio O Souza #endif
900a2c46b94SLuiz Otavio O Souza 
90123cd11b6SLuiz Otavio O Souza 	/* Reset the controller. */
90223cd11b6SLuiz Otavio O Souza 	cpsw_reset(sc);
90323cd11b6SLuiz Otavio O Souza 	cpsw_init(sc);
90423cd11b6SLuiz Otavio O Souza 
90523cd11b6SLuiz Otavio O Souza 	for (i = 0; i < CPSW_PORTS; i++) {
90623cd11b6SLuiz Otavio O Souza 		if (!sc->dualemac && i != sc->active_slave)
90723cd11b6SLuiz Otavio O Souza 			continue;
90823cd11b6SLuiz Otavio O Souza 		sc->port[i].dev = device_add_child(dev, "cpsw", i);
90923cd11b6SLuiz Otavio O Souza 		if (sc->port[i].dev == NULL) {
91023cd11b6SLuiz Otavio O Souza 			cpsw_detach(dev);
91123cd11b6SLuiz Otavio O Souza 			return (ENXIO);
91223cd11b6SLuiz Otavio O Souza 		}
91323cd11b6SLuiz Otavio O Souza 	}
914723da5d9SJohn Baldwin 	bus_identify_children(dev);
91518250ec6SJohn Baldwin 	bus_attach_children(dev);
91623cd11b6SLuiz Otavio O Souza 
91723cd11b6SLuiz Otavio O Souza 	return (0);
91823cd11b6SLuiz Otavio O Souza }
91923cd11b6SLuiz Otavio O Souza 
92023cd11b6SLuiz Otavio O Souza static int
cpsw_detach(device_t dev)92123cd11b6SLuiz Otavio O Souza cpsw_detach(device_t dev)
92223cd11b6SLuiz Otavio O Souza {
92323cd11b6SLuiz Otavio O Souza 	struct cpsw_softc *sc;
92423cd11b6SLuiz Otavio O Souza 	int error, i;
92523cd11b6SLuiz Otavio O Souza 
926b196276cSJohn Baldwin 	error = bus_generic_detach(dev);
927b196276cSJohn Baldwin 	if (error != 0)
928b196276cSJohn Baldwin 		return (error);
92923cd11b6SLuiz Otavio O Souza 
930b196276cSJohn Baldwin 	sc = device_get_softc(dev);
93123cd11b6SLuiz Otavio O Souza 
93223cd11b6SLuiz Otavio O Souza 	if (device_is_attached(dev)) {
93323cd11b6SLuiz Otavio O Souza 		callout_stop(&sc->watchdog.callout);
93423cd11b6SLuiz Otavio O Souza 		callout_drain(&sc->watchdog.callout);
93523cd11b6SLuiz Otavio O Souza 	}
93623cd11b6SLuiz Otavio O Souza 
93723cd11b6SLuiz Otavio O Souza 	/* Stop and release all interrupts */
93823cd11b6SLuiz Otavio O Souza 	cpsw_intr_detach(sc);
93923cd11b6SLuiz Otavio O Souza 
94023cd11b6SLuiz Otavio O Souza 	/* Free dmamaps and mbufs */
94133495e5dSPedro F. Giffuni 	for (i = 0; i < nitems(sc->_slots); ++i)
94223cd11b6SLuiz Otavio O Souza 		cpsw_free_slot(sc, &sc->_slots[i]);
94323cd11b6SLuiz Otavio O Souza 
944904e8e89SLuiz Otavio O Souza 	/* Free null padding buffer. */
945904e8e89SLuiz Otavio O Souza 	if (sc->nullpad)
946904e8e89SLuiz Otavio O Souza 		free(sc->nullpad, M_DEVBUF);
94723cd11b6SLuiz Otavio O Souza 
94823cd11b6SLuiz Otavio O Souza 	/* Free DMA tag */
94923cd11b6SLuiz Otavio O Souza 	if (sc->mbuf_dtag) {
95023cd11b6SLuiz Otavio O Souza 		error = bus_dma_tag_destroy(sc->mbuf_dtag);
95123cd11b6SLuiz Otavio O Souza 		KASSERT(error == 0, ("Unable to destroy DMA tag"));
95223cd11b6SLuiz Otavio O Souza 	}
95323cd11b6SLuiz Otavio O Souza 
95423cd11b6SLuiz Otavio O Souza 	/* Free IO memory handler */
95523cd11b6SLuiz Otavio O Souza 	if (sc->mem_res != NULL)
95623cd11b6SLuiz Otavio O Souza 		bus_release_resource(dev, SYS_RES_MEMORY, sc->mem_rid, sc->mem_res);
95723cd11b6SLuiz Otavio O Souza 	bus_release_resources(dev, irq_res_spec, sc->irq_res);
95823cd11b6SLuiz Otavio O Souza 
95923cd11b6SLuiz Otavio O Souza 	/* Destroy mutexes */
96023cd11b6SLuiz Otavio O Souza 	mtx_destroy(&sc->rx.lock);
96123cd11b6SLuiz Otavio O Souza 	mtx_destroy(&sc->tx.lock);
96223cd11b6SLuiz Otavio O Souza 
963160179eaSJohn Baldwin 	return (0);
96423cd11b6SLuiz Otavio O Souza }
96523cd11b6SLuiz Otavio O Souza 
96623cd11b6SLuiz Otavio O Souza static phandle_t
cpsw_get_node(device_t bus,device_t dev)96723cd11b6SLuiz Otavio O Souza cpsw_get_node(device_t bus, device_t dev)
96823cd11b6SLuiz Otavio O Souza {
96923cd11b6SLuiz Otavio O Souza 
97023cd11b6SLuiz Otavio O Souza 	/* Share controller node with port device. */
97123cd11b6SLuiz Otavio O Souza 	return (ofw_bus_get_node(bus));
97223cd11b6SLuiz Otavio O Souza }
97323cd11b6SLuiz Otavio O Souza 
97423cd11b6SLuiz Otavio O Souza static int
cpswp_probe(device_t dev)97523cd11b6SLuiz Otavio O Souza cpswp_probe(device_t dev)
97623cd11b6SLuiz Otavio O Souza {
97723cd11b6SLuiz Otavio O Souza 
97823cd11b6SLuiz Otavio O Souza 	if (device_get_unit(dev) > 1) {
97923cd11b6SLuiz Otavio O Souza 		device_printf(dev, "Only two ports are supported.\n");
98023cd11b6SLuiz Otavio O Souza 		return (ENXIO);
98123cd11b6SLuiz Otavio O Souza 	}
98223cd11b6SLuiz Otavio O Souza 	device_set_desc(dev, "Ethernet Switch Port");
98323cd11b6SLuiz Otavio O Souza 
98423cd11b6SLuiz Otavio O Souza 	return (BUS_PROBE_DEFAULT);
98523cd11b6SLuiz Otavio O Souza }
98623cd11b6SLuiz Otavio O Souza 
98723cd11b6SLuiz Otavio O Souza static int
cpswp_attach(device_t dev)98823cd11b6SLuiz Otavio O Souza cpswp_attach(device_t dev)
98923cd11b6SLuiz Otavio O Souza {
99023cd11b6SLuiz Otavio O Souza 	int error;
9912c7bc0f5SJustin Hibbits 	if_t ifp;
99223cd11b6SLuiz Otavio O Souza 	struct cpswp_softc *sc;
99323cd11b6SLuiz Otavio O Souza 	uint32_t reg;
99423cd11b6SLuiz Otavio O Souza 	uint8_t mac_addr[ETHER_ADDR_LEN];
9950050ea24SMichal Meloun 	phandle_t opp_table;
9960050ea24SMichal Meloun 	struct syscon *syscon;
99723cd11b6SLuiz Otavio O Souza 
99823cd11b6SLuiz Otavio O Souza 	sc = device_get_softc(dev);
99923cd11b6SLuiz Otavio O Souza 	sc->dev = dev;
100023cd11b6SLuiz Otavio O Souza 	sc->pdev = device_get_parent(dev);
100123cd11b6SLuiz Otavio O Souza 	sc->swsc = device_get_softc(sc->pdev);
100223cd11b6SLuiz Otavio O Souza 	sc->unit = device_get_unit(dev);
100323cd11b6SLuiz Otavio O Souza 	sc->phy = sc->swsc->port[sc->unit].phy;
100423cd11b6SLuiz Otavio O Souza 	sc->vlan = sc->swsc->port[sc->unit].vlan;
100523cd11b6SLuiz Otavio O Souza 	if (sc->swsc->dualemac && sc->vlan == -1)
100623cd11b6SLuiz Otavio O Souza 		sc->vlan = sc->unit + 1;
100723cd11b6SLuiz Otavio O Souza 
100823cd11b6SLuiz Otavio O Souza 	if (sc->unit == 0) {
100923cd11b6SLuiz Otavio O Souza 		sc->physel = MDIOUSERPHYSEL0;
101023cd11b6SLuiz Otavio O Souza 		sc->phyaccess = MDIOUSERACCESS0;
101123cd11b6SLuiz Otavio O Souza 	} else {
101223cd11b6SLuiz Otavio O Souza 		sc->physel = MDIOUSERPHYSEL1;
101323cd11b6SLuiz Otavio O Souza 		sc->phyaccess = MDIOUSERACCESS1;
101423cd11b6SLuiz Otavio O Souza 	}
101523cd11b6SLuiz Otavio O Souza 
101623cd11b6SLuiz Otavio O Souza 	mtx_init(&sc->lock, device_get_nameunit(dev), "cpsw port lock",
101723cd11b6SLuiz Otavio O Souza 	    MTX_DEF);
101823cd11b6SLuiz Otavio O Souza 
101923cd11b6SLuiz Otavio O Souza 	/* Allocate network interface */
102023cd11b6SLuiz Otavio O Souza 	ifp = sc->ifp = if_alloc(IFT_ETHER);
102123cd11b6SLuiz Otavio O Souza 	if_initname(ifp, device_get_name(sc->dev), sc->unit);
10222c7bc0f5SJustin Hibbits 	if_setsoftc(ifp, sc);
10232c7bc0f5SJustin Hibbits 	if_setflags(ifp, IFF_SIMPLEX | IFF_MULTICAST | IFF_BROADCAST);
102423cd11b6SLuiz Otavio O Souza 
10252c7bc0f5SJustin Hibbits 	if_setcapenable(ifp, if_getcapabilities(ifp));
102623cd11b6SLuiz Otavio O Souza 
10272c7bc0f5SJustin Hibbits 	if_setinitfn(ifp, cpswp_init);
10282c7bc0f5SJustin Hibbits 	if_setstartfn(ifp, cpswp_start);
10292c7bc0f5SJustin Hibbits 	if_setioctlfn(ifp, cpswp_ioctl);
10302c7bc0f5SJustin Hibbits 
10312c7bc0f5SJustin Hibbits 	if_setsendqlen(ifp, sc->swsc->tx.queue_slots);
10322c7bc0f5SJustin Hibbits 	if_setsendqready(ifp);
103323cd11b6SLuiz Otavio O Souza 
10340050ea24SMichal Meloun 	/* FIXME: For now; Go and kidnap syscon from opp-table */
10350050ea24SMichal Meloun 	/* ti,cpsw actually have an optional syscon reference but only for am33xx?? */
10360050ea24SMichal Meloun 	opp_table = OF_finddevice("/opp-table");
10370050ea24SMichal Meloun 	if (opp_table == -1) {
10380050ea24SMichal Meloun 		device_printf(dev, "Cant find /opp-table\n");
10390050ea24SMichal Meloun 		cpswp_detach(dev);
10400050ea24SMichal Meloun 		return (ENXIO);
10410050ea24SMichal Meloun 	}
10420050ea24SMichal Meloun 	if (!OF_hasprop(opp_table, "syscon")) {
10430050ea24SMichal Meloun 		device_printf(dev, "/opp-table doesnt have required syscon property\n");
10440050ea24SMichal Meloun 		cpswp_detach(dev);
10450050ea24SMichal Meloun 		return (ENXIO);
10460050ea24SMichal Meloun 	}
10470050ea24SMichal Meloun 	if (syscon_get_by_ofw_property(dev, opp_table, "syscon", &syscon) != 0) {
10480050ea24SMichal Meloun 		device_printf(dev, "Failed to get syscon\n");
10490050ea24SMichal Meloun 		cpswp_detach(dev);
10500050ea24SMichal Meloun 		return (ENXIO);
10510050ea24SMichal Meloun 	}
10520050ea24SMichal Meloun 
105323cd11b6SLuiz Otavio O Souza 	/* Get high part of MAC address from control module (mac_id[0|1]_hi) */
10540050ea24SMichal Meloun 	reg = SYSCON_READ_4(syscon, SCM_MAC_ID0_HI + sc->unit * 8);
105523cd11b6SLuiz Otavio O Souza 	mac_addr[0] = reg & 0xFF;
105623cd11b6SLuiz Otavio O Souza 	mac_addr[1] = (reg >>  8) & 0xFF;
105723cd11b6SLuiz Otavio O Souza 	mac_addr[2] = (reg >> 16) & 0xFF;
105823cd11b6SLuiz Otavio O Souza 	mac_addr[3] = (reg >> 24) & 0xFF;
105923cd11b6SLuiz Otavio O Souza 
106023cd11b6SLuiz Otavio O Souza 	/* Get low part of MAC address from control module (mac_id[0|1]_lo) */
10610050ea24SMichal Meloun 	reg = SYSCON_READ_4(syscon, SCM_MAC_ID0_LO + sc->unit * 8);
106223cd11b6SLuiz Otavio O Souza 	mac_addr[4] = reg & 0xFF;
106323cd11b6SLuiz Otavio O Souza 	mac_addr[5] = (reg >>  8) & 0xFF;
106423cd11b6SLuiz Otavio O Souza 
106523cd11b6SLuiz Otavio O Souza 	error = mii_attach(dev, &sc->miibus, ifp, cpswp_ifmedia_upd,
106623cd11b6SLuiz Otavio O Souza 	    cpswp_ifmedia_sts, BMSR_DEFCAPMASK, sc->phy, MII_OFFSET_ANY, 0);
106723cd11b6SLuiz Otavio O Souza 	if (error) {
106823cd11b6SLuiz Otavio O Souza 		device_printf(dev, "attaching PHYs failed\n");
106923cd11b6SLuiz Otavio O Souza 		cpswp_detach(dev);
107023cd11b6SLuiz Otavio O Souza 		return (error);
107123cd11b6SLuiz Otavio O Souza 	}
107223cd11b6SLuiz Otavio O Souza 	sc->mii = device_get_softc(sc->miibus);
107323cd11b6SLuiz Otavio O Souza 
107423cd11b6SLuiz Otavio O Souza 	/* Select PHY and enable interrupts */
107523cd11b6SLuiz Otavio O Souza 	cpsw_write_4(sc->swsc, sc->physel,
107623cd11b6SLuiz Otavio O Souza 	    MDIO_PHYSEL_LINKINTENB | (sc->phy & 0x1F));
107723cd11b6SLuiz Otavio O Souza 
107823cd11b6SLuiz Otavio O Souza 	ether_ifattach(sc->ifp, mac_addr);
107923cd11b6SLuiz Otavio O Souza 	callout_init(&sc->mii_callout, 0);
108023cd11b6SLuiz Otavio O Souza 
108123cd11b6SLuiz Otavio O Souza 	return (0);
108223cd11b6SLuiz Otavio O Souza }
108323cd11b6SLuiz Otavio O Souza 
108423cd11b6SLuiz Otavio O Souza static int
cpswp_detach(device_t dev)108523cd11b6SLuiz Otavio O Souza cpswp_detach(device_t dev)
108623cd11b6SLuiz Otavio O Souza {
108723cd11b6SLuiz Otavio O Souza 	struct cpswp_softc *sc;
108864d1a02eSJohn Baldwin 	int error;
108964d1a02eSJohn Baldwin 
109064d1a02eSJohn Baldwin 	error = bus_generic_detach(dev);
109164d1a02eSJohn Baldwin 	if (error != 0)
109264d1a02eSJohn Baldwin 		return (error);
109323cd11b6SLuiz Otavio O Souza 
109423cd11b6SLuiz Otavio O Souza 	sc = device_get_softc(dev);
10955a9c270aSLuiz Otavio O Souza 	CPSW_DEBUGF(sc->swsc, (""));
109623cd11b6SLuiz Otavio O Souza 	if (device_is_attached(dev)) {
109723cd11b6SLuiz Otavio O Souza 		ether_ifdetach(sc->ifp);
109823cd11b6SLuiz Otavio O Souza 		CPSW_PORT_LOCK(sc);
109923cd11b6SLuiz Otavio O Souza 		cpswp_stop_locked(sc);
110023cd11b6SLuiz Otavio O Souza 		CPSW_PORT_UNLOCK(sc);
110123cd11b6SLuiz Otavio O Souza 		callout_drain(&sc->mii_callout);
110223cd11b6SLuiz Otavio O Souza 	}
110323cd11b6SLuiz Otavio O Souza 
110423cd11b6SLuiz Otavio O Souza 	if_free(sc->ifp);
110523cd11b6SLuiz Otavio O Souza 	mtx_destroy(&sc->lock);
110623cd11b6SLuiz Otavio O Souza 
110723cd11b6SLuiz Otavio O Souza 	return (0);
110823cd11b6SLuiz Otavio O Souza }
110923cd11b6SLuiz Otavio O Souza 
111023cd11b6SLuiz Otavio O Souza /*
111123cd11b6SLuiz Otavio O Souza  *
111223cd11b6SLuiz Otavio O Souza  * Init/Shutdown.
111323cd11b6SLuiz Otavio O Souza  *
111423cd11b6SLuiz Otavio O Souza  */
111523cd11b6SLuiz Otavio O Souza 
111623cd11b6SLuiz Otavio O Souza static int
cpsw_ports_down(struct cpsw_softc * sc)111723cd11b6SLuiz Otavio O Souza cpsw_ports_down(struct cpsw_softc *sc)
111823cd11b6SLuiz Otavio O Souza {
111923cd11b6SLuiz Otavio O Souza 	struct cpswp_softc *psc;
11202c7bc0f5SJustin Hibbits 	if_t ifp1, ifp2;
112123cd11b6SLuiz Otavio O Souza 
112223cd11b6SLuiz Otavio O Souza 	if (!sc->dualemac)
112323cd11b6SLuiz Otavio O Souza 		return (1);
112423cd11b6SLuiz Otavio O Souza 	psc = device_get_softc(sc->port[0].dev);
112523cd11b6SLuiz Otavio O Souza 	ifp1 = psc->ifp;
112623cd11b6SLuiz Otavio O Souza 	psc = device_get_softc(sc->port[1].dev);
112723cd11b6SLuiz Otavio O Souza 	ifp2 = psc->ifp;
11286d18b481SJustin Hibbits 	if ((if_getflags(ifp1) & IFF_UP) == 0 && (if_getflags(ifp2) & IFF_UP) == 0)
112923cd11b6SLuiz Otavio O Souza 		return (1);
113023cd11b6SLuiz Otavio O Souza 
113123cd11b6SLuiz Otavio O Souza 	return (0);
113223cd11b6SLuiz Otavio O Souza }
113323cd11b6SLuiz Otavio O Souza 
113423cd11b6SLuiz Otavio O Souza static void
cpswp_init(void * arg)113523cd11b6SLuiz Otavio O Souza cpswp_init(void *arg)
113623cd11b6SLuiz Otavio O Souza {
113723cd11b6SLuiz Otavio O Souza 	struct cpswp_softc *sc = arg;
113823cd11b6SLuiz Otavio O Souza 
11395a9c270aSLuiz Otavio O Souza 	CPSW_DEBUGF(sc->swsc, (""));
114023cd11b6SLuiz Otavio O Souza 	CPSW_PORT_LOCK(sc);
114123cd11b6SLuiz Otavio O Souza 	cpswp_init_locked(arg);
114223cd11b6SLuiz Otavio O Souza 	CPSW_PORT_UNLOCK(sc);
114323cd11b6SLuiz Otavio O Souza }
114423cd11b6SLuiz Otavio O Souza 
114523cd11b6SLuiz Otavio O Souza static void
cpswp_init_locked(void * arg)114623cd11b6SLuiz Otavio O Souza cpswp_init_locked(void *arg)
114723cd11b6SLuiz Otavio O Souza {
1148a2c46b94SLuiz Otavio O Souza #ifdef CPSW_ETHERSWITCH
1149a2c46b94SLuiz Otavio O Souza 	int i;
1150a2c46b94SLuiz Otavio O Souza #endif
115123cd11b6SLuiz Otavio O Souza 	struct cpswp_softc *sc = arg;
11522c7bc0f5SJustin Hibbits 	if_t ifp;
115323cd11b6SLuiz Otavio O Souza 	uint32_t reg;
115423cd11b6SLuiz Otavio O Souza 
11555a9c270aSLuiz Otavio O Souza 	CPSW_DEBUGF(sc->swsc, (""));
115623cd11b6SLuiz Otavio O Souza 	CPSW_PORT_LOCK_ASSERT(sc);
115723cd11b6SLuiz Otavio O Souza 	ifp = sc->ifp;
11582c7bc0f5SJustin Hibbits 	if ((if_getdrvflags(ifp) & IFF_DRV_RUNNING) != 0)
115923cd11b6SLuiz Otavio O Souza 		return;
116023cd11b6SLuiz Otavio O Souza 
116123cd11b6SLuiz Otavio O Souza 	getbinuptime(&sc->init_uptime);
116223cd11b6SLuiz Otavio O Souza 
116323cd11b6SLuiz Otavio O Souza 	if (!sc->swsc->rx.running && !sc->swsc->tx.running) {
116423cd11b6SLuiz Otavio O Souza 		/* Reset the controller. */
116523cd11b6SLuiz Otavio O Souza 		cpsw_reset(sc->swsc);
116623cd11b6SLuiz Otavio O Souza 		cpsw_init(sc->swsc);
116723cd11b6SLuiz Otavio O Souza 	}
116823cd11b6SLuiz Otavio O Souza 
116923cd11b6SLuiz Otavio O Souza 	/* Set Slave Mapping. */
117023cd11b6SLuiz Otavio O Souza 	cpsw_write_4(sc->swsc, CPSW_SL_RX_PRI_MAP(sc->unit), 0x76543210);
117123cd11b6SLuiz Otavio O Souza 	cpsw_write_4(sc->swsc, CPSW_PORT_P_TX_PRI_MAP(sc->unit + 1),
117223cd11b6SLuiz Otavio O Souza 	    0x33221100);
117323cd11b6SLuiz Otavio O Souza 	cpsw_write_4(sc->swsc, CPSW_SL_RX_MAXLEN(sc->unit), 0x5f2);
117423cd11b6SLuiz Otavio O Souza 	/* Enable MAC RX/TX modules. */
117523cd11b6SLuiz Otavio O Souza 	/* TODO: Docs claim that IFCTL_B and IFCTL_A do the same thing? */
117623cd11b6SLuiz Otavio O Souza 	/* Huh?  Docs call bit 0 "Loopback" some places, "FullDuplex" others. */
117723cd11b6SLuiz Otavio O Souza 	reg = cpsw_read_4(sc->swsc, CPSW_SL_MACCONTROL(sc->unit));
117823cd11b6SLuiz Otavio O Souza 	reg |= CPSW_SL_MACTL_GMII_ENABLE;
117923cd11b6SLuiz Otavio O Souza 	cpsw_write_4(sc->swsc, CPSW_SL_MACCONTROL(sc->unit), reg);
118023cd11b6SLuiz Otavio O Souza 
1181a2c46b94SLuiz Otavio O Souza 	/* Initialize ALE: set port to forwarding, initialize addrs */
1182a2c46b94SLuiz Otavio O Souza 	cpsw_write_4(sc->swsc, CPSW_ALE_PORTCTL(sc->unit + 1),
1183a2c46b94SLuiz Otavio O Souza 	    ALE_PORTCTL_INGRESS | ALE_PORTCTL_FORWARD);
118423cd11b6SLuiz Otavio O Souza 	cpswp_ale_update_addresses(sc, 1);
118523cd11b6SLuiz Otavio O Souza 
118623cd11b6SLuiz Otavio O Souza 	if (sc->swsc->dualemac) {
118723cd11b6SLuiz Otavio O Souza 		/* Set Port VID. */
118823cd11b6SLuiz Otavio O Souza 		cpsw_write_4(sc->swsc, CPSW_PORT_P_VLAN(sc->unit + 1),
118923cd11b6SLuiz Otavio O Souza 		    sc->vlan & 0xfff);
119023cd11b6SLuiz Otavio O Souza 		cpsw_ale_update_vlan_table(sc->swsc, sc->vlan,
119151f8a15cSLuiz Otavio O Souza 		    (1 << (sc->unit + 1)) | (1 << 0), /* Member list */
119251f8a15cSLuiz Otavio O Souza 		    (1 << (sc->unit + 1)) | (1 << 0), /* Untagged egress */
119351f8a15cSLuiz Otavio O Souza 		    (1 << (sc->unit + 1)) | (1 << 0), 0); /* mcast reg flood */
1194a2c46b94SLuiz Otavio O Souza #ifdef CPSW_ETHERSWITCH
1195a2c46b94SLuiz Otavio O Souza 		for (i = 0; i < CPSW_VLANS; i++) {
1196a2c46b94SLuiz Otavio O Souza 			if (cpsw_vgroups[i].vid != -1)
1197a2c46b94SLuiz Otavio O Souza 				continue;
1198a2c46b94SLuiz Otavio O Souza 			cpsw_vgroups[i].vid = sc->vlan;
1199a2c46b94SLuiz Otavio O Souza 			break;
1200a2c46b94SLuiz Otavio O Souza 		}
1201a2c46b94SLuiz Otavio O Souza #endif
120223cd11b6SLuiz Otavio O Souza 	}
120323cd11b6SLuiz Otavio O Souza 
120423cd11b6SLuiz Otavio O Souza 	mii_mediachg(sc->mii);
120523cd11b6SLuiz Otavio O Souza 	callout_reset(&sc->mii_callout, hz, cpswp_tick, sc);
12062c7bc0f5SJustin Hibbits 	if_setdrvflagbits(ifp, IFF_DRV_RUNNING, 0);
12072c7bc0f5SJustin Hibbits 	if_setdrvflagbits(ifp, 0, IFF_DRV_OACTIVE);
1208ae6aefafSTim Kientzle }
1209ae6aefafSTim Kientzle 
1210ae6aefafSTim Kientzle static int
cpsw_shutdown(device_t dev)1211ae6aefafSTim Kientzle cpsw_shutdown(device_t dev)
1212ae6aefafSTim Kientzle {
121323cd11b6SLuiz Otavio O Souza 	struct cpsw_softc *sc;
121423cd11b6SLuiz Otavio O Souza 	struct cpswp_softc *psc;
121523cd11b6SLuiz Otavio O Souza 	int i;
1216ae6aefafSTim Kientzle 
121723cd11b6SLuiz Otavio O Souza  	sc = device_get_softc(dev);
121823cd11b6SLuiz Otavio O Souza 	CPSW_DEBUGF(sc, (""));
121923cd11b6SLuiz Otavio O Souza 	for (i = 0; i < CPSW_PORTS; i++) {
122023cd11b6SLuiz Otavio O Souza 		if (!sc->dualemac && i != sc->active_slave)
122123cd11b6SLuiz Otavio O Souza 			continue;
122223cd11b6SLuiz Otavio O Souza 		psc = device_get_softc(sc->port[i].dev);
122323cd11b6SLuiz Otavio O Souza 		CPSW_PORT_LOCK(psc);
122423cd11b6SLuiz Otavio O Souza 		cpswp_stop_locked(psc);
122523cd11b6SLuiz Otavio O Souza 		CPSW_PORT_UNLOCK(psc);
122623cd11b6SLuiz Otavio O Souza 	}
122723cd11b6SLuiz Otavio O Souza 
1228ae6aefafSTim Kientzle 	return (0);
1229ae6aefafSTim Kientzle }
1230ae6aefafSTim Kientzle 
1231ae6aefafSTim Kientzle static void
cpsw_rx_teardown(struct cpsw_softc * sc)1232ba14258fSLuiz Otavio O Souza cpsw_rx_teardown(struct cpsw_softc *sc)
1233ae6aefafSTim Kientzle {
1234ae6aefafSTim Kientzle 	int i = 0;
1235ae6aefafSTim Kientzle 
1236848a049bSLuiz Otavio O Souza 	CPSW_RX_LOCK(sc);
1237ba14258fSLuiz Otavio O Souza 	CPSW_DEBUGF(sc, ("starting RX teardown"));
1238ba14258fSLuiz Otavio O Souza 	sc->rx.teardown = 1;
1239ba14258fSLuiz Otavio O Souza 	cpsw_write_4(sc, CPSW_CPDMA_RX_TEARDOWN, 0);
1240ba14258fSLuiz Otavio O Souza 	CPSW_RX_UNLOCK(sc);
1241ba14258fSLuiz Otavio O Souza 	while (sc->rx.running) {
1242ae6aefafSTim Kientzle 		if (++i > 10) {
124323cd11b6SLuiz Otavio O Souza 			device_printf(sc->dev,
124423cd11b6SLuiz Otavio O Souza 			    "Unable to cleanly shutdown receiver\n");
1245ae6aefafSTim Kientzle 			return;
1246ae6aefafSTim Kientzle 		}
1247ba14258fSLuiz Otavio O Souza 		DELAY(200);
1248ae6aefafSTim Kientzle 	}
1249ba14258fSLuiz Otavio O Souza 	if (!sc->rx.running)
1250ba14258fSLuiz Otavio O Souza 		CPSW_DEBUGF(sc, ("finished RX teardown (%d retries)", i));
1251ae6aefafSTim Kientzle }
1252ae6aefafSTim Kientzle 
1253ae6aefafSTim Kientzle static void
cpsw_tx_teardown(struct cpsw_softc * sc)1254ba14258fSLuiz Otavio O Souza cpsw_tx_teardown(struct cpsw_softc *sc)
1255ae6aefafSTim Kientzle {
1256ae6aefafSTim Kientzle 	int i = 0;
1257ae6aefafSTim Kientzle 
1258ba14258fSLuiz Otavio O Souza 	CPSW_TX_LOCK(sc);
125923cd11b6SLuiz Otavio O Souza 	CPSW_DEBUGF(sc, ("starting TX teardown"));
1260ba14258fSLuiz Otavio O Souza 	/* Start the TX queue teardown if queue is not empty. */
1261ba14258fSLuiz Otavio O Souza 	if (STAILQ_FIRST(&sc->tx.active) != NULL)
1262ae6aefafSTim Kientzle 		cpsw_write_4(sc, CPSW_CPDMA_TX_TEARDOWN, 0);
1263ba14258fSLuiz Otavio O Souza 	else
1264ba14258fSLuiz Otavio O Souza 		sc->tx.teardown = 1;
1265ae6aefafSTim Kientzle 	cpsw_tx_dequeue(sc);
1266ae6aefafSTim Kientzle 	while (sc->tx.running && ++i < 10) {
1267ba14258fSLuiz Otavio O Souza 		DELAY(200);
1268ae6aefafSTim Kientzle 		cpsw_tx_dequeue(sc);
1269ae6aefafSTim Kientzle 	}
127023cd11b6SLuiz Otavio O Souza 	if (sc->tx.running) {
127123cd11b6SLuiz Otavio O Souza 		device_printf(sc->dev,
127223cd11b6SLuiz Otavio O Souza 		    "Unable to cleanly shutdown transmitter\n");
127323cd11b6SLuiz Otavio O Souza 	}
1274ba14258fSLuiz Otavio O Souza 	CPSW_DEBUGF(sc,
1275ba14258fSLuiz Otavio O Souza 	    ("finished TX teardown (%d retries, %d idle buffers)", i,
1276ba14258fSLuiz Otavio O Souza 	     sc->tx.active_queue_len));
1277ba14258fSLuiz Otavio O Souza 	CPSW_TX_UNLOCK(sc);
1278ae6aefafSTim Kientzle }
1279ae6aefafSTim Kientzle 
1280ae6aefafSTim Kientzle static void
cpswp_stop_locked(struct cpswp_softc * sc)128123cd11b6SLuiz Otavio O Souza cpswp_stop_locked(struct cpswp_softc *sc)
1282ae6aefafSTim Kientzle {
12832c7bc0f5SJustin Hibbits 	if_t ifp;
128423cd11b6SLuiz Otavio O Souza 	uint32_t reg;
1285ae6aefafSTim Kientzle 
1286e53470feSOleksandr Tymoshenko 	ifp = sc->ifp;
12875a9c270aSLuiz Otavio O Souza 	CPSW_DEBUGF(sc->swsc, (""));
128823cd11b6SLuiz Otavio O Souza 	CPSW_PORT_LOCK_ASSERT(sc);
1289e53470feSOleksandr Tymoshenko 
12902c7bc0f5SJustin Hibbits 	if ((if_getdrvflags(ifp) & IFF_DRV_RUNNING) == 0)
1291e53470feSOleksandr Tymoshenko 		return;
1292e53470feSOleksandr Tymoshenko 
1293e53470feSOleksandr Tymoshenko 	/* Disable interface */
12942c7bc0f5SJustin Hibbits 	if_setdrvflagbits(ifp, 0, IFF_DRV_RUNNING);
12952c7bc0f5SJustin Hibbits 	if_setdrvflagbits(ifp, IFF_DRV_OACTIVE, 0);
12965bf32555STim Kientzle 
1297ae6aefafSTim Kientzle 	/* Stop ticker */
129823cd11b6SLuiz Otavio O Souza 	callout_stop(&sc->mii_callout);
1299e53470feSOleksandr Tymoshenko 
1300ae6aefafSTim Kientzle 	/* Tear down the RX/TX queues. */
130123cd11b6SLuiz Otavio O Souza 	if (cpsw_ports_down(sc->swsc)) {
1302ba14258fSLuiz Otavio O Souza 		cpsw_rx_teardown(sc->swsc);
1303ba14258fSLuiz Otavio O Souza 		cpsw_tx_teardown(sc->swsc);
130423cd11b6SLuiz Otavio O Souza 	}
1305ae6aefafSTim Kientzle 
130623cd11b6SLuiz Otavio O Souza 	/* Stop MAC RX/TX modules. */
130723cd11b6SLuiz Otavio O Souza 	reg = cpsw_read_4(sc->swsc, CPSW_SL_MACCONTROL(sc->unit));
130823cd11b6SLuiz Otavio O Souza 	reg &= ~CPSW_SL_MACTL_GMII_ENABLE;
130923cd11b6SLuiz Otavio O Souza 	cpsw_write_4(sc->swsc, CPSW_SL_MACCONTROL(sc->unit), reg);
131023cd11b6SLuiz Otavio O Souza 
131123cd11b6SLuiz Otavio O Souza 	if (cpsw_ports_down(sc->swsc)) {
1312ae6aefafSTim Kientzle 		/* Capture stats before we reset controller. */
131323cd11b6SLuiz Otavio O Souza 		cpsw_stats_collect(sc->swsc);
1314ae6aefafSTim Kientzle 
131523cd11b6SLuiz Otavio O Souza 		cpsw_reset(sc->swsc);
131623cd11b6SLuiz Otavio O Souza 		cpsw_init(sc->swsc);
131723cd11b6SLuiz Otavio O Souza 	}
13185bf32555STim Kientzle }
1319ae6aefafSTim Kientzle 
1320ae6aefafSTim Kientzle /*
1321ae6aefafSTim Kientzle  *  Suspend/Resume.
1322ae6aefafSTim Kientzle  */
1323ae6aefafSTim Kientzle 
1324ae6aefafSTim Kientzle static int
cpsw_suspend(device_t dev)1325ae6aefafSTim Kientzle cpsw_suspend(device_t dev)
1326ae6aefafSTim Kientzle {
132723cd11b6SLuiz Otavio O Souza 	struct cpsw_softc *sc;
132823cd11b6SLuiz Otavio O Souza 	struct cpswp_softc *psc;
132923cd11b6SLuiz Otavio O Souza 	int i;
1330ae6aefafSTim Kientzle 
133123cd11b6SLuiz Otavio O Souza 	sc = device_get_softc(dev);
133223cd11b6SLuiz Otavio O Souza 	CPSW_DEBUGF(sc, (""));
133323cd11b6SLuiz Otavio O Souza 	for (i = 0; i < CPSW_PORTS; i++) {
133423cd11b6SLuiz Otavio O Souza 		if (!sc->dualemac && i != sc->active_slave)
133523cd11b6SLuiz Otavio O Souza 			continue;
133623cd11b6SLuiz Otavio O Souza 		psc = device_get_softc(sc->port[i].dev);
133723cd11b6SLuiz Otavio O Souza 		CPSW_PORT_LOCK(psc);
133823cd11b6SLuiz Otavio O Souza 		cpswp_stop_locked(psc);
133923cd11b6SLuiz Otavio O Souza 		CPSW_PORT_UNLOCK(psc);
134023cd11b6SLuiz Otavio O Souza 	}
134123cd11b6SLuiz Otavio O Souza 
1342ae6aefafSTim Kientzle 	return (0);
13435bf32555STim Kientzle }
13445bf32555STim Kientzle 
1345ae6aefafSTim Kientzle static int
cpsw_resume(device_t dev)1346ae6aefafSTim Kientzle cpsw_resume(device_t dev)
1347ae6aefafSTim Kientzle {
134823cd11b6SLuiz Otavio O Souza 	struct cpsw_softc *sc;
13495bf32555STim Kientzle 
135023cd11b6SLuiz Otavio O Souza 	sc  = device_get_softc(dev);
135123cd11b6SLuiz Otavio O Souza 	CPSW_DEBUGF(sc, ("UNIMPLEMENTED"));
135223cd11b6SLuiz Otavio O Souza 
1353ae6aefafSTim Kientzle 	return (0);
13545bf32555STim Kientzle }
13555bf32555STim Kientzle 
1356ae6aefafSTim Kientzle /*
1357ae6aefafSTim Kientzle  *
1358ae6aefafSTim Kientzle  *  IOCTL
1359ae6aefafSTim Kientzle  *
1360ae6aefafSTim Kientzle  */
13615bf32555STim Kientzle 
13625bf32555STim Kientzle static void
cpsw_set_promisc(struct cpswp_softc * sc,int set)136323cd11b6SLuiz Otavio O Souza cpsw_set_promisc(struct cpswp_softc *sc, int set)
13645bf32555STim Kientzle {
136523cd11b6SLuiz Otavio O Souza 	uint32_t reg;
136623cd11b6SLuiz Otavio O Souza 
1367ae6aefafSTim Kientzle 	/*
136823cd11b6SLuiz Otavio O Souza 	 * Enabling promiscuous mode requires ALE_BYPASS to be enabled.
136923cd11b6SLuiz Otavio O Souza 	 * That disables the ALE forwarding logic and causes every
137023cd11b6SLuiz Otavio O Souza 	 * packet to be sent only to the host port.  In bypass mode,
137123cd11b6SLuiz Otavio O Souza 	 * the ALE processes host port transmit packets the same as in
137223cd11b6SLuiz Otavio O Souza 	 * normal mode.
1373ae6aefafSTim Kientzle 	 */
137423cd11b6SLuiz Otavio O Souza 	reg = cpsw_read_4(sc->swsc, CPSW_ALE_CONTROL);
137523cd11b6SLuiz Otavio O Souza 	reg &= ~CPSW_ALE_CTL_BYPASS;
137623cd11b6SLuiz Otavio O Souza 	if (set)
137723cd11b6SLuiz Otavio O Souza 		reg |= CPSW_ALE_CTL_BYPASS;
137823cd11b6SLuiz Otavio O Souza 	cpsw_write_4(sc->swsc, CPSW_ALE_CONTROL, reg);
13795bf32555STim Kientzle }
13805bf32555STim Kientzle 
13815bf32555STim Kientzle static void
cpsw_set_allmulti(struct cpswp_softc * sc,int set)138223cd11b6SLuiz Otavio O Souza cpsw_set_allmulti(struct cpswp_softc *sc, int set)
13835bf32555STim Kientzle {
13845bf32555STim Kientzle 	if (set) {
13855bf32555STim Kientzle 		printf("All-multicast mode unimplemented\n");
13865bf32555STim Kientzle 	}
1387e53470feSOleksandr Tymoshenko }
1388e53470feSOleksandr Tymoshenko 
1389e53470feSOleksandr Tymoshenko static int
cpswp_ioctl(if_t ifp,u_long command,caddr_t data)13902c7bc0f5SJustin Hibbits cpswp_ioctl(if_t ifp, u_long command, caddr_t data)
1391e53470feSOleksandr Tymoshenko {
139223cd11b6SLuiz Otavio O Souza 	struct cpswp_softc *sc;
139323cd11b6SLuiz Otavio O Souza 	struct ifreq *ifr;
1394e53470feSOleksandr Tymoshenko 	int error;
13955bf32555STim Kientzle 	uint32_t changed;
1396e53470feSOleksandr Tymoshenko 
1397e53470feSOleksandr Tymoshenko 	error = 0;
13982c7bc0f5SJustin Hibbits 	sc = if_getsoftc(ifp);
139923cd11b6SLuiz Otavio O Souza 	ifr = (struct ifreq *)data;
1400e53470feSOleksandr Tymoshenko 
1401e53470feSOleksandr Tymoshenko 	switch (command) {
1402270da772SLuiz Otavio O Souza 	case SIOCSIFCAP:
14032c7bc0f5SJustin Hibbits 		changed = if_getcapenable(ifp) ^ ifr->ifr_reqcap;
1404270da772SLuiz Otavio O Souza 		if (changed & IFCAP_HWCSUM) {
1405270da772SLuiz Otavio O Souza 			if ((ifr->ifr_reqcap & changed) & IFCAP_HWCSUM)
14062c7bc0f5SJustin Hibbits 				if_setcapenablebit(ifp, IFCAP_HWCSUM, 0);
1407270da772SLuiz Otavio O Souza 			else
14082c7bc0f5SJustin Hibbits 				if_setcapenablebit(ifp, 0, IFCAP_HWCSUM);
1409270da772SLuiz Otavio O Souza 		}
1410270da772SLuiz Otavio O Souza 		error = 0;
1411270da772SLuiz Otavio O Souza 		break;
1412e53470feSOleksandr Tymoshenko 	case SIOCSIFFLAGS:
141323cd11b6SLuiz Otavio O Souza 		CPSW_PORT_LOCK(sc);
14142c7bc0f5SJustin Hibbits 		if (if_getflags(ifp) & IFF_UP) {
14152c7bc0f5SJustin Hibbits 			if (if_getdrvflags(ifp) & IFF_DRV_RUNNING) {
14162c7bc0f5SJustin Hibbits 				changed = if_getflags(ifp) ^ sc->if_flags;
14175a9c270aSLuiz Otavio O Souza 				CPSW_DEBUGF(sc->swsc,
141823cd11b6SLuiz Otavio O Souza 				    ("SIOCSIFFLAGS: UP & RUNNING (changed=0x%x)",
141923cd11b6SLuiz Otavio O Souza 				    changed));
14205bf32555STim Kientzle 				if (changed & IFF_PROMISC)
14215bf32555STim Kientzle 					cpsw_set_promisc(sc,
14222c7bc0f5SJustin Hibbits 					    if_getflags(ifp) & IFF_PROMISC);
14235bf32555STim Kientzle 				if (changed & IFF_ALLMULTI)
14245bf32555STim Kientzle 					cpsw_set_allmulti(sc,
14252c7bc0f5SJustin Hibbits 					    if_getflags(ifp) & IFF_ALLMULTI);
1426e53470feSOleksandr Tymoshenko 			} else {
14275a9c270aSLuiz Otavio O Souza 				CPSW_DEBUGF(sc->swsc,
1428ba14258fSLuiz Otavio O Souza 				    ("SIOCSIFFLAGS: starting up"));
142923cd11b6SLuiz Otavio O Souza 				cpswp_init_locked(sc);
1430e53470feSOleksandr Tymoshenko 			}
14312c7bc0f5SJustin Hibbits 		} else if (if_getdrvflags(ifp) & IFF_DRV_RUNNING) {
1432ba14258fSLuiz Otavio O Souza 			CPSW_DEBUGF(sc->swsc, ("SIOCSIFFLAGS: shutting down"));
143323cd11b6SLuiz Otavio O Souza 			cpswp_stop_locked(sc);
14345bf32555STim Kientzle 		}
1435e53470feSOleksandr Tymoshenko 
14362c7bc0f5SJustin Hibbits 		sc->if_flags = if_getflags(ifp);
143723cd11b6SLuiz Otavio O Souza 		CPSW_PORT_UNLOCK(sc);
1438e53470feSOleksandr Tymoshenko 		break;
1439e53470feSOleksandr Tymoshenko 	case SIOCADDMULTI:
144023cd11b6SLuiz Otavio O Souza 		cpswp_ale_update_addresses(sc, 0);
1441e53470feSOleksandr Tymoshenko 		break;
1442e53470feSOleksandr Tymoshenko 	case SIOCDELMULTI:
1443ae6aefafSTim Kientzle 		/* Ugh.  DELMULTI doesn't provide the specific address
1444ae6aefafSTim Kientzle 		   being removed, so the best we can do is remove
1445ae6aefafSTim Kientzle 		   everything and rebuild it all. */
144623cd11b6SLuiz Otavio O Souza 		cpswp_ale_update_addresses(sc, 1);
1447e53470feSOleksandr Tymoshenko 		break;
14482b539bdcSJohn-Mark Gurney 	case SIOCGIFMEDIA:
1449e53470feSOleksandr Tymoshenko 	case SIOCSIFMEDIA:
1450e53470feSOleksandr Tymoshenko 		error = ifmedia_ioctl(ifp, ifr, &sc->mii->mii_media, command);
1451e53470feSOleksandr Tymoshenko 		break;
1452e53470feSOleksandr Tymoshenko 	default:
1453e53470feSOleksandr Tymoshenko 		error = ether_ioctl(ifp, command, data);
1454e53470feSOleksandr Tymoshenko 	}
1455e53470feSOleksandr Tymoshenko 	return (error);
1456e53470feSOleksandr Tymoshenko }
1457e53470feSOleksandr Tymoshenko 
1458ae6aefafSTim Kientzle /*
1459ae6aefafSTim Kientzle  *
1460ae6aefafSTim Kientzle  * MIIBUS
1461ae6aefafSTim Kientzle  *
1462ae6aefafSTim Kientzle  */
1463ae6aefafSTim Kientzle static int
cpswp_miibus_ready(struct cpsw_softc * sc,uint32_t reg)146423cd11b6SLuiz Otavio O Souza cpswp_miibus_ready(struct cpsw_softc *sc, uint32_t reg)
1465ae6aefafSTim Kientzle {
1466ae6aefafSTim Kientzle 	uint32_t r, retries = CPSW_MIIBUS_RETRIES;
1467ae6aefafSTim Kientzle 
1468ae6aefafSTim Kientzle 	while (--retries) {
146923cd11b6SLuiz Otavio O Souza 		r = cpsw_read_4(sc, reg);
147023cd11b6SLuiz Otavio O Souza 		if ((r & MDIO_PHYACCESS_GO) == 0)
147123cd11b6SLuiz Otavio O Souza 			return (1);
1472ae6aefafSTim Kientzle 		DELAY(CPSW_MIIBUS_DELAY);
1473ae6aefafSTim Kientzle 	}
147423cd11b6SLuiz Otavio O Souza 
147523cd11b6SLuiz Otavio O Souza 	return (0);
1476ae6aefafSTim Kientzle }
1477ae6aefafSTim Kientzle 
1478ae6aefafSTim Kientzle static int
cpswp_miibus_readreg(device_t dev,int phy,int reg)147923cd11b6SLuiz Otavio O Souza cpswp_miibus_readreg(device_t dev, int phy, int reg)
1480ae6aefafSTim Kientzle {
148123cd11b6SLuiz Otavio O Souza 	struct cpswp_softc *sc;
1482ae6aefafSTim Kientzle 	uint32_t cmd, r;
1483ae6aefafSTim Kientzle 
148423cd11b6SLuiz Otavio O Souza 	sc = device_get_softc(dev);
148523cd11b6SLuiz Otavio O Souza 	if (!cpswp_miibus_ready(sc->swsc, sc->phyaccess)) {
1486ae6aefafSTim Kientzle 		device_printf(dev, "MDIO not ready to read\n");
148723cd11b6SLuiz Otavio O Souza 		return (0);
1488ae6aefafSTim Kientzle 	}
1489ae6aefafSTim Kientzle 
1490ae6aefafSTim Kientzle 	/* Set GO, reg, phy */
149123cd11b6SLuiz Otavio O Souza 	cmd = MDIO_PHYACCESS_GO | (reg & 0x1F) << 21 | (phy & 0x1F) << 16;
149223cd11b6SLuiz Otavio O Souza 	cpsw_write_4(sc->swsc, sc->phyaccess, cmd);
1493ae6aefafSTim Kientzle 
149423cd11b6SLuiz Otavio O Souza 	if (!cpswp_miibus_ready(sc->swsc, sc->phyaccess)) {
1495ae6aefafSTim Kientzle 		device_printf(dev, "MDIO timed out during read\n");
149623cd11b6SLuiz Otavio O Souza 		return (0);
1497ae6aefafSTim Kientzle 	}
1498ae6aefafSTim Kientzle 
149923cd11b6SLuiz Otavio O Souza 	r = cpsw_read_4(sc->swsc, sc->phyaccess);
150023cd11b6SLuiz Otavio O Souza 	if ((r & MDIO_PHYACCESS_ACK) == 0) {
1501ae6aefafSTim Kientzle 		device_printf(dev, "Failed to read from PHY.\n");
1502ae6aefafSTim Kientzle 		r = 0;
1503ae6aefafSTim Kientzle 	}
1504ae6aefafSTim Kientzle 	return (r & 0xFFFF);
1505ae6aefafSTim Kientzle }
1506ae6aefafSTim Kientzle 
1507ae6aefafSTim Kientzle static int
cpswp_miibus_writereg(device_t dev,int phy,int reg,int value)150823cd11b6SLuiz Otavio O Souza cpswp_miibus_writereg(device_t dev, int phy, int reg, int value)
1509ae6aefafSTim Kientzle {
151023cd11b6SLuiz Otavio O Souza 	struct cpswp_softc *sc;
1511ae6aefafSTim Kientzle 	uint32_t cmd;
1512ae6aefafSTim Kientzle 
151323cd11b6SLuiz Otavio O Souza 	sc = device_get_softc(dev);
151423cd11b6SLuiz Otavio O Souza 	if (!cpswp_miibus_ready(sc->swsc, sc->phyaccess)) {
1515ae6aefafSTim Kientzle 		device_printf(dev, "MDIO not ready to write\n");
151623cd11b6SLuiz Otavio O Souza 		return (0);
1517ae6aefafSTim Kientzle 	}
1518ae6aefafSTim Kientzle 
1519ae6aefafSTim Kientzle 	/* Set GO, WRITE, reg, phy, and value */
152023cd11b6SLuiz Otavio O Souza 	cmd = MDIO_PHYACCESS_GO | MDIO_PHYACCESS_WRITE |
152123cd11b6SLuiz Otavio O Souza 	    (reg & 0x1F) << 21 | (phy & 0x1F) << 16 | (value & 0xFFFF);
152223cd11b6SLuiz Otavio O Souza 	cpsw_write_4(sc->swsc, sc->phyaccess, cmd);
1523ae6aefafSTim Kientzle 
152423cd11b6SLuiz Otavio O Souza 	if (!cpswp_miibus_ready(sc->swsc, sc->phyaccess)) {
1525ae6aefafSTim Kientzle 		device_printf(dev, "MDIO timed out during write\n");
152623cd11b6SLuiz Otavio O Souza 		return (0);
1527ae6aefafSTim Kientzle 	}
1528ae6aefafSTim Kientzle 
152923cd11b6SLuiz Otavio O Souza 	return (0);
1530ae6aefafSTim Kientzle }
1531ae6aefafSTim Kientzle 
15327c909980SAndrew Turner static void
cpswp_miibus_statchg(device_t dev)153323cd11b6SLuiz Otavio O Souza cpswp_miibus_statchg(device_t dev)
15347c909980SAndrew Turner {
153523cd11b6SLuiz Otavio O Souza 	struct cpswp_softc *sc;
153623cd11b6SLuiz Otavio O Souza 	uint32_t mac_control, reg;
15377c909980SAndrew Turner 
153823cd11b6SLuiz Otavio O Souza 	sc = device_get_softc(dev);
15395a9c270aSLuiz Otavio O Souza 	CPSW_DEBUGF(sc->swsc, (""));
15407c909980SAndrew Turner 
154123cd11b6SLuiz Otavio O Souza 	reg = CPSW_SL_MACCONTROL(sc->unit);
154223cd11b6SLuiz Otavio O Souza 	mac_control = cpsw_read_4(sc->swsc, reg);
154323cd11b6SLuiz Otavio O Souza 	mac_control &= ~(CPSW_SL_MACTL_GIG | CPSW_SL_MACTL_IFCTL_A |
154423cd11b6SLuiz Otavio O Souza 	    CPSW_SL_MACTL_IFCTL_B | CPSW_SL_MACTL_FULLDUPLEX);
15457c909980SAndrew Turner 
15467c909980SAndrew Turner 	switch(IFM_SUBTYPE(sc->mii->mii_media_active)) {
15477c909980SAndrew Turner 	case IFM_1000_SX:
15487c909980SAndrew Turner 	case IFM_1000_LX:
15497c909980SAndrew Turner 	case IFM_1000_CX:
15507c909980SAndrew Turner 	case IFM_1000_T:
155123cd11b6SLuiz Otavio O Souza 		mac_control |= CPSW_SL_MACTL_GIG;
15527c909980SAndrew Turner 		break;
15537c909980SAndrew Turner 
155423cd11b6SLuiz Otavio O Souza 	case IFM_100_TX:
155523cd11b6SLuiz Otavio O Souza 		mac_control |= CPSW_SL_MACTL_IFCTL_A;
15567c909980SAndrew Turner 		break;
15577c909980SAndrew Turner 	}
155823cd11b6SLuiz Otavio O Souza 	if (sc->mii->mii_media_active & IFM_FDX)
155923cd11b6SLuiz Otavio O Souza 		mac_control |= CPSW_SL_MACTL_FULLDUPLEX;
15607c909980SAndrew Turner 
156123cd11b6SLuiz Otavio O Souza 	cpsw_write_4(sc->swsc, reg, mac_control);
15627c909980SAndrew Turner }
15637c909980SAndrew Turner 
1564ae6aefafSTim Kientzle /*
1565ae6aefafSTim Kientzle  *
1566ae6aefafSTim Kientzle  * Transmit/Receive Packets.
1567ae6aefafSTim Kientzle  *
1568ae6aefafSTim Kientzle  */
1569ae6aefafSTim Kientzle static void
cpsw_intr_rx(void * arg)1570ae6aefafSTim Kientzle cpsw_intr_rx(void *arg)
1571ae6aefafSTim Kientzle {
1572ba14258fSLuiz Otavio O Souza 	struct cpsw_softc *sc;
15732c7bc0f5SJustin Hibbits 	if_t ifp;
1574ae6aefafSTim Kientzle 	struct mbuf *received, *next;
1575ae6aefafSTim Kientzle 
1576ba14258fSLuiz Otavio O Souza 	sc = (struct cpsw_softc *)arg;
1577ae6aefafSTim Kientzle 	CPSW_RX_LOCK(sc);
1578ba14258fSLuiz Otavio O Souza 	if (sc->rx.teardown) {
1579ba14258fSLuiz Otavio O Souza 		sc->rx.running = 0;
1580ba14258fSLuiz Otavio O Souza 		sc->rx.teardown = 0;
1581ba14258fSLuiz Otavio O Souza 		cpsw_write_cp(sc, &sc->rx, 0xfffffffc);
1582ba14258fSLuiz Otavio O Souza 	}
1583ae6aefafSTim Kientzle 	received = cpsw_rx_dequeue(sc);
1584ae6aefafSTim Kientzle 	cpsw_rx_enqueue(sc);
1585ae6aefafSTim Kientzle 	cpsw_write_4(sc, CPSW_CPDMA_CPDMA_EOI_VECTOR, 1);
1586ae6aefafSTim Kientzle 	CPSW_RX_UNLOCK(sc);
1587ae6aefafSTim Kientzle 
1588ae6aefafSTim Kientzle 	while (received != NULL) {
1589ae6aefafSTim Kientzle 		next = received->m_nextpkt;
1590ae6aefafSTim Kientzle 		received->m_nextpkt = NULL;
159123cd11b6SLuiz Otavio O Souza 		ifp = received->m_pkthdr.rcvif;
15922c7bc0f5SJustin Hibbits 		if_input(ifp, received);
15939c2deddeSLuiz Otavio O Souza 		if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1);
1594ae6aefafSTim Kientzle 		received = next;
1595ae6aefafSTim Kientzle 	}
1596ae6aefafSTim Kientzle }
1597ae6aefafSTim Kientzle 
1598ae6aefafSTim Kientzle static struct mbuf *
cpsw_rx_dequeue(struct cpsw_softc * sc)1599ae6aefafSTim Kientzle cpsw_rx_dequeue(struct cpsw_softc *sc)
1600ae6aefafSTim Kientzle {
16013a3d1c77SLuiz Otavio O Souza 	int nsegs, port, removed;
1602ae6aefafSTim Kientzle 	struct cpsw_cpdma_bd bd;
1603ba14258fSLuiz Otavio O Souza 	struct cpsw_slot *last, *slot;
160423cd11b6SLuiz Otavio O Souza 	struct cpswp_softc *psc;
16053a3d1c77SLuiz Otavio O Souza 	struct mbuf *m, *m0, *mb_head, *mb_tail;
16063a3d1c77SLuiz Otavio O Souza 	uint16_t m0_flags;
1607ae6aefafSTim Kientzle 
16083a3d1c77SLuiz Otavio O Souza 	nsegs = 0;
16093a3d1c77SLuiz Otavio O Souza 	m0 = NULL;
1610ba14258fSLuiz Otavio O Souza 	last = NULL;
16113a3d1c77SLuiz Otavio O Souza 	mb_head = NULL;
16123a3d1c77SLuiz Otavio O Souza 	mb_tail = NULL;
16133a3d1c77SLuiz Otavio O Souza 	removed = 0;
1614ae6aefafSTim Kientzle 
1615ae6aefafSTim Kientzle 	/* Pull completed packets off hardware RX queue. */
1616ae6aefafSTim Kientzle 	while ((slot = STAILQ_FIRST(&sc->rx.active)) != NULL) {
1617ae6aefafSTim Kientzle 		cpsw_cpdma_read_bd(sc, slot, &bd);
1618ae6aefafSTim Kientzle 
1619ba14258fSLuiz Otavio O Souza 		/*
1620ba14258fSLuiz Otavio O Souza 		 * Stop on packets still in use by hardware, but do not stop
1621ba14258fSLuiz Otavio O Souza 		 * on packets with the teardown complete flag, they will be
1622ba14258fSLuiz Otavio O Souza 		 * discarded later.
1623ba14258fSLuiz Otavio O Souza 		 */
1624ba14258fSLuiz Otavio O Souza 		if ((bd.flags & (CPDMA_BD_OWNER | CPDMA_BD_TDOWNCMPLT)) ==
1625ba14258fSLuiz Otavio O Souza 		    CPDMA_BD_OWNER)
1626ba14258fSLuiz Otavio O Souza 			break;
1627ba14258fSLuiz Otavio O Souza 
1628ba14258fSLuiz Otavio O Souza 		last = slot;
1629ae6aefafSTim Kientzle 		++removed;
1630ae6aefafSTim Kientzle 		STAILQ_REMOVE_HEAD(&sc->rx.active, next);
1631ae6aefafSTim Kientzle 		STAILQ_INSERT_TAIL(&sc->rx.avail, slot, next);
1632ae6aefafSTim Kientzle 
1633ae6aefafSTim Kientzle 		bus_dmamap_sync(sc->mbuf_dtag, slot->dmamap, BUS_DMASYNC_POSTREAD);
1634ae6aefafSTim Kientzle 		bus_dmamap_unload(sc->mbuf_dtag, slot->dmamap);
1635ae6aefafSTim Kientzle 
16363a3d1c77SLuiz Otavio O Souza 		m = slot->mbuf;
16373a3d1c77SLuiz Otavio O Souza 		slot->mbuf = NULL;
16383a3d1c77SLuiz Otavio O Souza 
1639ae6aefafSTim Kientzle 		if (bd.flags & CPDMA_BD_TDOWNCMPLT) {
1640ba14258fSLuiz Otavio O Souza 			CPSW_DEBUGF(sc, ("RX teardown is complete"));
16413a3d1c77SLuiz Otavio O Souza 			m_freem(m);
1642ae6aefafSTim Kientzle 			sc->rx.running = 0;
1643ba14258fSLuiz Otavio O Souza 			sc->rx.teardown = 0;
1644ae6aefafSTim Kientzle 			break;
1645ae6aefafSTim Kientzle 		}
1646ae6aefafSTim Kientzle 
164723cd11b6SLuiz Otavio O Souza 		port = (bd.flags & CPDMA_BD_PORT_MASK) - 1;
164823cd11b6SLuiz Otavio O Souza 		KASSERT(port >= 0 && port <= 1,
164923cd11b6SLuiz Otavio O Souza 		    ("patcket received with invalid port: %d", port));
165023cd11b6SLuiz Otavio O Souza 		psc = device_get_softc(sc->port[port].dev);
165123cd11b6SLuiz Otavio O Souza 
1652ae6aefafSTim Kientzle 		/* Set up mbuf */
16533a3d1c77SLuiz Otavio O Souza 		m->m_data += bd.bufoff;
16543a3d1c77SLuiz Otavio O Souza 		m->m_len = bd.buflen;
1655270da772SLuiz Otavio O Souza 		if (bd.flags & CPDMA_BD_SOP) {
16563a3d1c77SLuiz Otavio O Souza 			m->m_pkthdr.len = bd.pktlen;
16573a3d1c77SLuiz Otavio O Souza 			m->m_pkthdr.rcvif = psc->ifp;
16583a3d1c77SLuiz Otavio O Souza 			m->m_flags |= M_PKTHDR;
16593a3d1c77SLuiz Otavio O Souza 			m0_flags = bd.flags;
16603a3d1c77SLuiz Otavio O Souza 			m0 = m;
1661270da772SLuiz Otavio O Souza 		}
16623a3d1c77SLuiz Otavio O Souza 		nsegs++;
16633a3d1c77SLuiz Otavio O Souza 		m->m_next = NULL;
16643a3d1c77SLuiz Otavio O Souza 		m->m_nextpkt = NULL;
16653a3d1c77SLuiz Otavio O Souza 		if (bd.flags & CPDMA_BD_EOP && m0 != NULL) {
16663a3d1c77SLuiz Otavio O Souza 			if (m0_flags & CPDMA_BD_PASS_CRC)
16673a3d1c77SLuiz Otavio O Souza 				m_adj(m0, -ETHER_CRC_LEN);
16683a3d1c77SLuiz Otavio O Souza 			m0_flags = 0;
16693a3d1c77SLuiz Otavio O Souza 			m0 = NULL;
16703a3d1c77SLuiz Otavio O Souza 			if (nsegs > sc->rx.longest_chain)
16713a3d1c77SLuiz Otavio O Souza 				sc->rx.longest_chain = nsegs;
16723a3d1c77SLuiz Otavio O Souza 			nsegs = 0;
16733a3d1c77SLuiz Otavio O Souza 		}
1674ae6aefafSTim Kientzle 
16752c7bc0f5SJustin Hibbits 		if ((if_getcapenable(psc->ifp) & IFCAP_RXCSUM) != 0) {
1676ae6aefafSTim Kientzle 			/* check for valid CRC by looking into pkt_err[5:4] */
1677270da772SLuiz Otavio O Souza 			if ((bd.flags &
1678270da772SLuiz Otavio O Souza 			    (CPDMA_BD_SOP | CPDMA_BD_PKT_ERR_MASK)) ==
1679270da772SLuiz Otavio O Souza 			    CPDMA_BD_SOP) {
16803a3d1c77SLuiz Otavio O Souza 				m->m_pkthdr.csum_flags |= CSUM_IP_CHECKED;
16813a3d1c77SLuiz Otavio O Souza 				m->m_pkthdr.csum_flags |= CSUM_IP_VALID;
16823a3d1c77SLuiz Otavio O Souza 				m->m_pkthdr.csum_data = 0xffff;
1683ae6aefafSTim Kientzle 			}
1684ae6aefafSTim Kientzle 		}
1685ae6aefafSTim Kientzle 
16866d800e3cSLuiz Otavio O Souza 		if (STAILQ_FIRST(&sc->rx.active) != NULL &&
16876d800e3cSLuiz Otavio O Souza 		    (bd.flags & (CPDMA_BD_EOP | CPDMA_BD_EOQ)) ==
16886d800e3cSLuiz Otavio O Souza 		    (CPDMA_BD_EOP | CPDMA_BD_EOQ)) {
16896d800e3cSLuiz Otavio O Souza 			cpsw_write_hdp_slot(sc, &sc->rx,
16906d800e3cSLuiz Otavio O Souza 			    STAILQ_FIRST(&sc->rx.active));
16916d800e3cSLuiz Otavio O Souza 			sc->rx.queue_restart++;
16926d800e3cSLuiz Otavio O Souza 		}
16936d800e3cSLuiz Otavio O Souza 
1694ae6aefafSTim Kientzle 		/* Add mbuf to packet list to be returned. */
16953a3d1c77SLuiz Otavio O Souza 		if (mb_tail != NULL && (bd.flags & CPDMA_BD_SOP)) {
16963a3d1c77SLuiz Otavio O Souza 			mb_tail->m_nextpkt = m;
16973a3d1c77SLuiz Otavio O Souza 		} else if (mb_tail != NULL) {
16983a3d1c77SLuiz Otavio O Souza 			mb_tail->m_next = m;
16993a3d1c77SLuiz Otavio O Souza 		} else if (mb_tail == NULL && (bd.flags & CPDMA_BD_SOP) == 0) {
17003a3d1c77SLuiz Otavio O Souza 			if (bootverbose)
17013a3d1c77SLuiz Otavio O Souza 				printf(
17023a3d1c77SLuiz Otavio O Souza 				    "%s: %s: discanding fragment packet w/o header\n",
17032c7bc0f5SJustin Hibbits 				    __func__, if_name(psc->ifp));
17043a3d1c77SLuiz Otavio O Souza 			m_freem(m);
17053a3d1c77SLuiz Otavio O Souza 			continue;
1706ae6aefafSTim Kientzle 		} else {
17073a3d1c77SLuiz Otavio O Souza 			mb_head = m;
1708ae6aefafSTim Kientzle 		}
17093a3d1c77SLuiz Otavio O Souza 		mb_tail = m;
1710ae6aefafSTim Kientzle 	}
1711ae6aefafSTim Kientzle 
1712ae6aefafSTim Kientzle 	if (removed != 0) {
1713ba14258fSLuiz Otavio O Souza 		cpsw_write_cp_slot(sc, &sc->rx, last);
1714ae6aefafSTim Kientzle 		sc->rx.queue_removes += removed;
1715ae6aefafSTim Kientzle 		sc->rx.avail_queue_len += removed;
1716ba14258fSLuiz Otavio O Souza 		sc->rx.active_queue_len -= removed;
1717ae6aefafSTim Kientzle 		if (sc->rx.avail_queue_len > sc->rx.max_avail_queue_len)
1718ae6aefafSTim Kientzle 			sc->rx.max_avail_queue_len = sc->rx.avail_queue_len;
1719ba14258fSLuiz Otavio O Souza 		CPSW_DEBUGF(sc, ("Removed %d received packet(s) from RX queue", removed));
1720ae6aefafSTim Kientzle 	}
1721ba14258fSLuiz Otavio O Souza 
1722ae6aefafSTim Kientzle 	return (mb_head);
1723ae6aefafSTim Kientzle }
1724ae6aefafSTim Kientzle 
1725ae6aefafSTim Kientzle static void
cpsw_rx_enqueue(struct cpsw_softc * sc)1726ae6aefafSTim Kientzle cpsw_rx_enqueue(struct cpsw_softc *sc)
1727ae6aefafSTim Kientzle {
1728ae6aefafSTim Kientzle 	bus_dma_segment_t seg[1];
1729ae6aefafSTim Kientzle 	struct cpsw_cpdma_bd bd;
1730ba14258fSLuiz Otavio O Souza 	struct cpsw_slot *first_new_slot, *last_old_slot, *next, *slot;
1731ae6aefafSTim Kientzle 	int error, nsegs, added = 0;
1732ae6aefafSTim Kientzle 
1733ae6aefafSTim Kientzle 	/* Register new mbufs with hardware. */
1734ba14258fSLuiz Otavio O Souza 	first_new_slot = NULL;
1735ba14258fSLuiz Otavio O Souza 	last_old_slot = STAILQ_LAST(&sc->rx.active, cpsw_slot, next);
1736ae6aefafSTim Kientzle 	while ((slot = STAILQ_FIRST(&sc->rx.avail)) != NULL) {
1737ba14258fSLuiz Otavio O Souza 		if (first_new_slot == NULL)
1738ba14258fSLuiz Otavio O Souza 			first_new_slot = slot;
1739ae6aefafSTim Kientzle 		if (slot->mbuf == NULL) {
1740ae6aefafSTim Kientzle 			slot->mbuf = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR);
1741ae6aefafSTim Kientzle 			if (slot->mbuf == NULL) {
174223cd11b6SLuiz Otavio O Souza 				device_printf(sc->dev,
174323cd11b6SLuiz Otavio O Souza 				    "Unable to fill RX queue\n");
1744ae6aefafSTim Kientzle 				break;
1745ae6aefafSTim Kientzle 			}
1746ae6aefafSTim Kientzle 			slot->mbuf->m_len =
1747ae6aefafSTim Kientzle 			    slot->mbuf->m_pkthdr.len =
1748ae6aefafSTim Kientzle 			    slot->mbuf->m_ext.ext_size;
1749ae6aefafSTim Kientzle 		}
1750ae6aefafSTim Kientzle 
1751ae6aefafSTim Kientzle 		error = bus_dmamap_load_mbuf_sg(sc->mbuf_dtag, slot->dmamap,
1752ae6aefafSTim Kientzle 		    slot->mbuf, seg, &nsegs, BUS_DMA_NOWAIT);
1753ae6aefafSTim Kientzle 
1754ae6aefafSTim Kientzle 		KASSERT(nsegs == 1, ("More than one segment (nsegs=%d)", nsegs));
1755ae6aefafSTim Kientzle 		KASSERT(error == 0, ("DMA error (error=%d)", error));
1756ae6aefafSTim Kientzle 		if (error != 0 || nsegs != 1) {
175723cd11b6SLuiz Otavio O Souza 			device_printf(sc->dev,
1758ae6aefafSTim Kientzle 			    "%s: Can't prep RX buf for DMA (nsegs=%d, error=%d)\n",
1759ae6aefafSTim Kientzle 			    __func__, nsegs, error);
1760ae6aefafSTim Kientzle 			bus_dmamap_unload(sc->mbuf_dtag, slot->dmamap);
1761ae6aefafSTim Kientzle 			m_freem(slot->mbuf);
1762ae6aefafSTim Kientzle 			slot->mbuf = NULL;
1763ae6aefafSTim Kientzle 			break;
1764ae6aefafSTim Kientzle 		}
1765ae6aefafSTim Kientzle 
1766ae6aefafSTim Kientzle 		bus_dmamap_sync(sc->mbuf_dtag, slot->dmamap, BUS_DMASYNC_PREREAD);
1767ae6aefafSTim Kientzle 
1768ba14258fSLuiz Otavio O Souza 		/* Create and submit new rx descriptor. */
1769ba14258fSLuiz Otavio O Souza 		if ((next = STAILQ_NEXT(slot, next)) != NULL)
1770ba14258fSLuiz Otavio O Souza 			bd.next = cpsw_cpdma_bd_paddr(sc, next);
1771ba14258fSLuiz Otavio O Souza 		else
1772ae6aefafSTim Kientzle 			bd.next = 0;
1773ae6aefafSTim Kientzle 		bd.bufptr = seg->ds_addr;
1774ae6aefafSTim Kientzle 		bd.bufoff = 0;
1775ae6aefafSTim Kientzle 		bd.buflen = MCLBYTES - 1;
1776ae6aefafSTim Kientzle 		bd.pktlen = bd.buflen;
1777ae6aefafSTim Kientzle 		bd.flags = CPDMA_BD_OWNER;
1778ae6aefafSTim Kientzle 		cpsw_cpdma_write_bd(sc, slot, &bd);
1779ae6aefafSTim Kientzle 		++added;
1780ae6aefafSTim Kientzle 
1781ae6aefafSTim Kientzle 		STAILQ_REMOVE_HEAD(&sc->rx.avail, next);
1782ba14258fSLuiz Otavio O Souza 		STAILQ_INSERT_TAIL(&sc->rx.active, slot, next);
1783ae6aefafSTim Kientzle 	}
1784ae6aefafSTim Kientzle 
1785ba14258fSLuiz Otavio O Souza 	if (added == 0 || first_new_slot == NULL)
1786ae6aefafSTim Kientzle 		return;
1787ae6aefafSTim Kientzle 
178823cd11b6SLuiz Otavio O Souza 	CPSW_DEBUGF(sc, ("Adding %d buffers to RX queue", added));
1789ae6aefafSTim Kientzle 
1790ae6aefafSTim Kientzle 	/* Link new entries to hardware RX queue. */
1791ba14258fSLuiz Otavio O Souza 	if (last_old_slot == NULL) {
1792ae6aefafSTim Kientzle 		/* Start a fresh queue. */
1793ae6aefafSTim Kientzle 		cpsw_write_hdp_slot(sc, &sc->rx, first_new_slot);
1794ae6aefafSTim Kientzle 	} else {
1795ae6aefafSTim Kientzle 		/* Add buffers to end of current queue. */
1796ae6aefafSTim Kientzle 		cpsw_cpdma_write_bd_next(sc, last_old_slot, first_new_slot);
1797ae6aefafSTim Kientzle 	}
1798ae6aefafSTim Kientzle 	sc->rx.queue_adds += added;
1799ba14258fSLuiz Otavio O Souza 	sc->rx.avail_queue_len -= added;
1800ae6aefafSTim Kientzle 	sc->rx.active_queue_len += added;
180171462f56SLuiz Otavio O Souza 	cpsw_write_4(sc, CPSW_CPDMA_RX_FREEBUFFER(0), added);
18026d800e3cSLuiz Otavio O Souza 	if (sc->rx.active_queue_len > sc->rx.max_active_queue_len)
1803ae6aefafSTim Kientzle 		sc->rx.max_active_queue_len = sc->rx.active_queue_len;
1804ae6aefafSTim Kientzle }
1805ae6aefafSTim Kientzle 
1806ae6aefafSTim Kientzle static void
cpswp_start(if_t ifp)18072c7bc0f5SJustin Hibbits cpswp_start(if_t ifp)
1808ae6aefafSTim Kientzle {
1809ba14258fSLuiz Otavio O Souza 	struct cpswp_softc *sc;
1810ae6aefafSTim Kientzle 
18112c7bc0f5SJustin Hibbits 	sc = if_getsoftc(ifp);
18122c7bc0f5SJustin Hibbits 	if ((if_getdrvflags(ifp) & IFF_DRV_RUNNING) == 0 ||
1813ba14258fSLuiz Otavio O Souza 	    sc->swsc->tx.running == 0) {
1814ba14258fSLuiz Otavio O Souza 		return;
1815ba14258fSLuiz Otavio O Souza 	}
181623cd11b6SLuiz Otavio O Souza 	CPSW_TX_LOCK(sc->swsc);
181723cd11b6SLuiz Otavio O Souza 	cpswp_tx_enqueue(sc);
181823cd11b6SLuiz Otavio O Souza 	cpsw_tx_dequeue(sc->swsc);
181923cd11b6SLuiz Otavio O Souza 	CPSW_TX_UNLOCK(sc->swsc);
1820ae6aefafSTim Kientzle }
1821ae6aefafSTim Kientzle 
1822ae6aefafSTim Kientzle static void
cpsw_intr_tx(void * arg)1823430d5eb4SLuiz Otavio O Souza cpsw_intr_tx(void *arg)
1824430d5eb4SLuiz Otavio O Souza {
1825430d5eb4SLuiz Otavio O Souza 	struct cpsw_softc *sc;
1826430d5eb4SLuiz Otavio O Souza 
1827430d5eb4SLuiz Otavio O Souza 	sc = (struct cpsw_softc *)arg;
1828430d5eb4SLuiz Otavio O Souza 	CPSW_TX_LOCK(sc);
1829ba14258fSLuiz Otavio O Souza 	if (cpsw_read_4(sc, CPSW_CPDMA_TX_CP(0)) == 0xfffffffc)
1830ba14258fSLuiz Otavio O Souza 		cpsw_write_cp(sc, &sc->tx, 0xfffffffc);
1831430d5eb4SLuiz Otavio O Souza 	cpsw_tx_dequeue(sc);
1832430d5eb4SLuiz Otavio O Souza 	cpsw_write_4(sc, CPSW_CPDMA_CPDMA_EOI_VECTOR, 2);
1833430d5eb4SLuiz Otavio O Souza 	CPSW_TX_UNLOCK(sc);
1834430d5eb4SLuiz Otavio O Souza }
1835430d5eb4SLuiz Otavio O Souza 
1836430d5eb4SLuiz Otavio O Souza static void
cpswp_tx_enqueue(struct cpswp_softc * sc)183723cd11b6SLuiz Otavio O Souza cpswp_tx_enqueue(struct cpswp_softc *sc)
1838ae6aefafSTim Kientzle {
1839ae6aefafSTim Kientzle 	bus_dma_segment_t segs[CPSW_TXFRAGS];
1840ae6aefafSTim Kientzle 	struct cpsw_cpdma_bd bd;
1841ba14258fSLuiz Otavio O Souza 	struct cpsw_slot *first_new_slot, *last, *last_old_slot, *next, *slot;
1842ae6aefafSTim Kientzle 	struct mbuf *m0;
1843b328ce00SLuiz Otavio O Souza 	int error, nsegs, seg, added = 0, padlen;
1844ae6aefafSTim Kientzle 
1845ae6aefafSTim Kientzle 	/* Pull pending packets from IF queue and prep them for DMA. */
1846ba14258fSLuiz Otavio O Souza 	last = NULL;
1847ba14258fSLuiz Otavio O Souza 	first_new_slot = NULL;
1848ba14258fSLuiz Otavio O Souza 	last_old_slot = STAILQ_LAST(&sc->swsc->tx.active, cpsw_slot, next);
184923cd11b6SLuiz Otavio O Souza 	while ((slot = STAILQ_FIRST(&sc->swsc->tx.avail)) != NULL) {
18502c7bc0f5SJustin Hibbits 		m0 = if_dequeue(sc->ifp);
1851ae6aefafSTim Kientzle 		if (m0 == NULL)
1852ae6aefafSTim Kientzle 			break;
1853ae6aefafSTim Kientzle 
1854ae6aefafSTim Kientzle 		slot->mbuf = m0;
1855904e8e89SLuiz Otavio O Souza 		padlen = ETHER_MIN_LEN - ETHER_CRC_LEN - m0->m_pkthdr.len;
1856ae6aefafSTim Kientzle 		if (padlen < 0)
1857ae6aefafSTim Kientzle 			padlen = 0;
1858904e8e89SLuiz Otavio O Souza 		else if (padlen > 0)
1859904e8e89SLuiz Otavio O Souza 			m_append(slot->mbuf, padlen, sc->swsc->nullpad);
1860ae6aefafSTim Kientzle 
1861ae6aefafSTim Kientzle 		/* Create mapping in DMA memory */
186223cd11b6SLuiz Otavio O Souza 		error = bus_dmamap_load_mbuf_sg(sc->swsc->mbuf_dtag,
186323cd11b6SLuiz Otavio O Souza 		    slot->dmamap, slot->mbuf, segs, &nsegs, BUS_DMA_NOWAIT);
1864ae6aefafSTim Kientzle 		/* If the packet is too fragmented, try to simplify. */
1865ae6aefafSTim Kientzle 		if (error == EFBIG ||
1866904e8e89SLuiz Otavio O Souza 		    (error == 0 && nsegs > sc->swsc->tx.avail_queue_len)) {
186723cd11b6SLuiz Otavio O Souza 			bus_dmamap_unload(sc->swsc->mbuf_dtag, slot->dmamap);
1868ae6aefafSTim Kientzle 			m0 = m_defrag(slot->mbuf, M_NOWAIT);
1869ae6aefafSTim Kientzle 			if (m0 == NULL) {
187023cd11b6SLuiz Otavio O Souza 				device_printf(sc->dev,
1871ae6aefafSTim Kientzle 				    "Can't defragment packet; dropping\n");
1872ae6aefafSTim Kientzle 				m_freem(slot->mbuf);
1873ae6aefafSTim Kientzle 			} else {
18745a9c270aSLuiz Otavio O Souza 				CPSW_DEBUGF(sc->swsc,
187523cd11b6SLuiz Otavio O Souza 				    ("Requeueing defragmented packet"));
18762c7bc0f5SJustin Hibbits 				if_sendq_prepend(sc->ifp, m0);
1877ae6aefafSTim Kientzle 			}
1878ae6aefafSTim Kientzle 			slot->mbuf = NULL;
1879ae6aefafSTim Kientzle 			continue;
1880ae6aefafSTim Kientzle 		}
1881ae6aefafSTim Kientzle 		if (error != 0) {
188223cd11b6SLuiz Otavio O Souza 			device_printf(sc->dev,
1883ae6aefafSTim Kientzle 			    "%s: Can't setup DMA (error=%d), dropping packet\n",
1884ae6aefafSTim Kientzle 			    __func__, error);
188523cd11b6SLuiz Otavio O Souza 			bus_dmamap_unload(sc->swsc->mbuf_dtag, slot->dmamap);
1886ae6aefafSTim Kientzle 			m_freem(slot->mbuf);
1887ae6aefafSTim Kientzle 			slot->mbuf = NULL;
1888ae6aefafSTim Kientzle 			break;
1889ae6aefafSTim Kientzle 		}
1890ae6aefafSTim Kientzle 
189123cd11b6SLuiz Otavio O Souza 		bus_dmamap_sync(sc->swsc->mbuf_dtag, slot->dmamap,
1892ae6aefafSTim Kientzle 				BUS_DMASYNC_PREWRITE);
1893ae6aefafSTim Kientzle 
18945a9c270aSLuiz Otavio O Souza 		CPSW_DEBUGF(sc->swsc,
189523cd11b6SLuiz Otavio O Souza 		    ("Queueing TX packet: %d segments + %d pad bytes",
1896ae6aefafSTim Kientzle 		    nsegs, padlen));
1897ae6aefafSTim Kientzle 
1898ba14258fSLuiz Otavio O Souza 		if (first_new_slot == NULL)
1899ba14258fSLuiz Otavio O Souza 			first_new_slot = slot;
1900ba14258fSLuiz Otavio O Souza 
1901ba14258fSLuiz Otavio O Souza 		/* Link from the previous descriptor. */
1902ba14258fSLuiz Otavio O Souza 		if (last != NULL)
1903ba14258fSLuiz Otavio O Souza 			cpsw_cpdma_write_bd_next(sc->swsc, last, slot);
1904ba14258fSLuiz Otavio O Souza 
19059c2deddeSLuiz Otavio O Souza 		slot->ifp = sc->ifp;
1906ba14258fSLuiz Otavio O Souza 
1907ae6aefafSTim Kientzle 		/* If there is only one segment, the for() loop
1908ae6aefafSTim Kientzle 		 * gets skipped and the single buffer gets set up
1909ae6aefafSTim Kientzle 		 * as both SOP and EOP. */
1910ba14258fSLuiz Otavio O Souza 		if (nsegs > 1) {
1911ba14258fSLuiz Otavio O Souza 			next = STAILQ_NEXT(slot, next);
1912ba14258fSLuiz Otavio O Souza 			bd.next = cpsw_cpdma_bd_paddr(sc->swsc, next);
1913ba14258fSLuiz Otavio O Souza 		} else
1914ae6aefafSTim Kientzle 			bd.next = 0;
1915ba14258fSLuiz Otavio O Souza 		/* Start by setting up the first buffer. */
1916ae6aefafSTim Kientzle 		bd.bufptr = segs[0].ds_addr;
1917ae6aefafSTim Kientzle 		bd.bufoff = 0;
1918ae6aefafSTim Kientzle 		bd.buflen = segs[0].ds_len;
1919904e8e89SLuiz Otavio O Souza 		bd.pktlen = m_length(slot->mbuf, NULL);
1920b328ce00SLuiz Otavio O Souza 		bd.flags =  CPDMA_BD_SOP | CPDMA_BD_OWNER;
1921b328ce00SLuiz Otavio O Souza 		if (sc->swsc->dualemac) {
1922b328ce00SLuiz Otavio O Souza 			bd.flags |= CPDMA_BD_TO_PORT;
1923b328ce00SLuiz Otavio O Souza 			bd.flags |= ((sc->unit + 1) & CPDMA_BD_PORT_MASK);
1924b328ce00SLuiz Otavio O Souza 		}
1925ae6aefafSTim Kientzle 		for (seg = 1; seg < nsegs; ++seg) {
1926ae6aefafSTim Kientzle 			/* Save the previous buffer (which isn't EOP) */
192723cd11b6SLuiz Otavio O Souza 			cpsw_cpdma_write_bd(sc->swsc, slot, &bd);
192823cd11b6SLuiz Otavio O Souza 			STAILQ_REMOVE_HEAD(&sc->swsc->tx.avail, next);
1929ba14258fSLuiz Otavio O Souza 			STAILQ_INSERT_TAIL(&sc->swsc->tx.active, slot, next);
193023cd11b6SLuiz Otavio O Souza 			slot = STAILQ_FIRST(&sc->swsc->tx.avail);
1931ae6aefafSTim Kientzle 
1932ae6aefafSTim Kientzle 			/* Setup next buffer (which isn't SOP) */
1933ba14258fSLuiz Otavio O Souza 			if (nsegs > seg + 1) {
1934ba14258fSLuiz Otavio O Souza 				next = STAILQ_NEXT(slot, next);
1935ba14258fSLuiz Otavio O Souza 				bd.next = cpsw_cpdma_bd_paddr(sc->swsc, next);
1936ba14258fSLuiz Otavio O Souza 			} else
1937ae6aefafSTim Kientzle 				bd.next = 0;
1938ae6aefafSTim Kientzle 			bd.bufptr = segs[seg].ds_addr;
1939ae6aefafSTim Kientzle 			bd.bufoff = 0;
1940ae6aefafSTim Kientzle 			bd.buflen = segs[seg].ds_len;
1941ae6aefafSTim Kientzle 			bd.pktlen = 0;
1942b328ce00SLuiz Otavio O Souza 			bd.flags = CPDMA_BD_OWNER;
1943ae6aefafSTim Kientzle 		}
1944904e8e89SLuiz Otavio O Souza 
1945ae6aefafSTim Kientzle 		/* Save the final buffer. */
1946ae6aefafSTim Kientzle 		bd.flags |= CPDMA_BD_EOP;
194723cd11b6SLuiz Otavio O Souza 		cpsw_cpdma_write_bd(sc->swsc, slot, &bd);
194823cd11b6SLuiz Otavio O Souza 		STAILQ_REMOVE_HEAD(&sc->swsc->tx.avail, next);
1949ba14258fSLuiz Otavio O Souza 		STAILQ_INSERT_TAIL(&sc->swsc->tx.active, slot, next);
1950ae6aefafSTim Kientzle 
1951ba14258fSLuiz Otavio O Souza 		last = slot;
1952ba14258fSLuiz Otavio O Souza 		added += nsegs;
195323cd11b6SLuiz Otavio O Souza 		if (nsegs > sc->swsc->tx.longest_chain)
195423cd11b6SLuiz Otavio O Souza 			sc->swsc->tx.longest_chain = nsegs;
1955ae6aefafSTim Kientzle 
1956ae6aefafSTim Kientzle 		BPF_MTAP(sc->ifp, m0);
1957ae6aefafSTim Kientzle 	}
1958ae6aefafSTim Kientzle 
1959ba14258fSLuiz Otavio O Souza 	if (first_new_slot == NULL)
1960ae6aefafSTim Kientzle 		return;
1961ba14258fSLuiz Otavio O Souza 
1962ba14258fSLuiz Otavio O Souza 	/* Attach the list of new buffers to the hardware TX queue. */
1963ba14258fSLuiz Otavio O Souza 	if (last_old_slot != NULL &&
1964ba14258fSLuiz Otavio O Souza 	    (cpsw_cpdma_read_bd_flags(sc->swsc, last_old_slot) &
1965ba14258fSLuiz Otavio O Souza 	     CPDMA_BD_EOQ) == 0) {
1966ae6aefafSTim Kientzle 		/* Add buffers to end of current queue. */
196723cd11b6SLuiz Otavio O Souza 		cpsw_cpdma_write_bd_next(sc->swsc, last_old_slot,
196823cd11b6SLuiz Otavio O Souza 		    first_new_slot);
1969ba14258fSLuiz Otavio O Souza 	} else {
1970ba14258fSLuiz Otavio O Souza 		/* Start a fresh queue. */
1971ba14258fSLuiz Otavio O Souza 		cpsw_write_hdp_slot(sc->swsc, &sc->swsc->tx, first_new_slot);
1972ae6aefafSTim Kientzle 	}
197323cd11b6SLuiz Otavio O Souza 	sc->swsc->tx.queue_adds += added;
1974ba14258fSLuiz Otavio O Souza 	sc->swsc->tx.avail_queue_len -= added;
197523cd11b6SLuiz Otavio O Souza 	sc->swsc->tx.active_queue_len += added;
197623cd11b6SLuiz Otavio O Souza 	if (sc->swsc->tx.active_queue_len > sc->swsc->tx.max_active_queue_len) {
197723cd11b6SLuiz Otavio O Souza 		sc->swsc->tx.max_active_queue_len = sc->swsc->tx.active_queue_len;
1978ae6aefafSTim Kientzle 	}
1979ba14258fSLuiz Otavio O Souza 	CPSW_DEBUGF(sc->swsc, ("Queued %d TX packet(s)", added));
1980ae6aefafSTim Kientzle }
1981ae6aefafSTim Kientzle 
1982ae6aefafSTim Kientzle static int
cpsw_tx_dequeue(struct cpsw_softc * sc)1983ae6aefafSTim Kientzle cpsw_tx_dequeue(struct cpsw_softc *sc)
1984ae6aefafSTim Kientzle {
1985ae6aefafSTim Kientzle 	struct cpsw_slot *slot, *last_removed_slot = NULL;
1986cde7231aSLuiz Otavio O Souza 	struct cpsw_cpdma_bd bd;
1987ae6aefafSTim Kientzle 	uint32_t flags, removed = 0;
1988ae6aefafSTim Kientzle 
1989ae6aefafSTim Kientzle 	/* Pull completed buffers off the hardware TX queue. */
1990ba14258fSLuiz Otavio O Souza 	slot = STAILQ_FIRST(&sc->tx.active);
1991ae6aefafSTim Kientzle 	while (slot != NULL) {
1992ae6aefafSTim Kientzle 		flags = cpsw_cpdma_read_bd_flags(sc, slot);
1993ba14258fSLuiz Otavio O Souza 
1994ba14258fSLuiz Otavio O Souza 		/* TearDown complete is only marked on the SOP for the packet. */
1995ba14258fSLuiz Otavio O Souza 		if ((flags & (CPDMA_BD_SOP | CPDMA_BD_TDOWNCMPLT)) ==
1996ba14258fSLuiz Otavio O Souza 		    (CPDMA_BD_SOP | CPDMA_BD_TDOWNCMPLT)) {
1997ba14258fSLuiz Otavio O Souza 			sc->tx.teardown = 1;
1998ba14258fSLuiz Otavio O Souza 		}
1999ba14258fSLuiz Otavio O Souza 
20006386d003SLuiz Otavio O Souza 		if ((flags & (CPDMA_BD_SOP | CPDMA_BD_OWNER)) ==
20016386d003SLuiz Otavio O Souza 		    (CPDMA_BD_SOP | CPDMA_BD_OWNER) && sc->tx.teardown == 0)
2002ae6aefafSTim Kientzle 			break; /* Hardware is still using this packet. */
2003ae6aefafSTim Kientzle 
2004ae6aefafSTim Kientzle 		bus_dmamap_sync(sc->mbuf_dtag, slot->dmamap, BUS_DMASYNC_POSTWRITE);
2005ae6aefafSTim Kientzle 		bus_dmamap_unload(sc->mbuf_dtag, slot->dmamap);
2006ae6aefafSTim Kientzle 		m_freem(slot->mbuf);
2007ae6aefafSTim Kientzle 		slot->mbuf = NULL;
2008ba14258fSLuiz Otavio O Souza 
2009ba14258fSLuiz Otavio O Souza 		if (slot->ifp) {
2010ba14258fSLuiz Otavio O Souza 			if (sc->tx.teardown == 0)
20119c2deddeSLuiz Otavio O Souza 				if_inc_counter(slot->ifp, IFCOUNTER_OPACKETS, 1);
2012ba14258fSLuiz Otavio O Souza 			else
2013ba14258fSLuiz Otavio O Souza 				if_inc_counter(slot->ifp, IFCOUNTER_OQDROPS, 1);
2014ba14258fSLuiz Otavio O Souza 		}
2015ae6aefafSTim Kientzle 
2016ae6aefafSTim Kientzle 		/* Dequeue any additional buffers used by this packet. */
2017ae6aefafSTim Kientzle 		while (slot != NULL && slot->mbuf == NULL) {
2018ae6aefafSTim Kientzle 			STAILQ_REMOVE_HEAD(&sc->tx.active, next);
2019ae6aefafSTim Kientzle 			STAILQ_INSERT_TAIL(&sc->tx.avail, slot, next);
2020ae6aefafSTim Kientzle 			++removed;
2021ae6aefafSTim Kientzle 			last_removed_slot = slot;
2022ae6aefafSTim Kientzle 			slot = STAILQ_FIRST(&sc->tx.active);
2023ae6aefafSTim Kientzle 		}
2024ae6aefafSTim Kientzle 
2025ba14258fSLuiz Otavio O Souza 		cpsw_write_cp_slot(sc, &sc->tx, last_removed_slot);
2026cde7231aSLuiz Otavio O Souza 
2027ba14258fSLuiz Otavio O Souza 		/* Restart the TX queue if necessary. */
2028cde7231aSLuiz Otavio O Souza 		cpsw_cpdma_read_bd(sc, last_removed_slot, &bd);
2029ba14258fSLuiz Otavio O Souza 		if (slot != NULL && bd.next != 0 && (bd.flags &
2030ba14258fSLuiz Otavio O Souza 		    (CPDMA_BD_EOP | CPDMA_BD_OWNER | CPDMA_BD_EOQ)) ==
2031ba14258fSLuiz Otavio O Souza 		    (CPDMA_BD_EOP | CPDMA_BD_EOQ)) {
2032ba14258fSLuiz Otavio O Souza 			cpsw_write_hdp_slot(sc, &sc->tx, slot);
2033ba14258fSLuiz Otavio O Souza 			sc->tx.queue_restart++;
2034ba14258fSLuiz Otavio O Souza 			break;
2035cde7231aSLuiz Otavio O Souza 		}
2036ae6aefafSTim Kientzle 	}
2037ae6aefafSTim Kientzle 
2038ae6aefafSTim Kientzle 	if (removed != 0) {
2039ae6aefafSTim Kientzle 		sc->tx.queue_removes += removed;
2040ae6aefafSTim Kientzle 		sc->tx.active_queue_len -= removed;
2041ae6aefafSTim Kientzle 		sc->tx.avail_queue_len += removed;
2042ae6aefafSTim Kientzle 		if (sc->tx.avail_queue_len > sc->tx.max_avail_queue_len)
2043ae6aefafSTim Kientzle 			sc->tx.max_avail_queue_len = sc->tx.avail_queue_len;
2044ba14258fSLuiz Otavio O Souza 		CPSW_DEBUGF(sc, ("TX removed %d completed packet(s)", removed));
2045ae6aefafSTim Kientzle 	}
2046ba14258fSLuiz Otavio O Souza 
2047ba14258fSLuiz Otavio O Souza 	if (sc->tx.teardown && STAILQ_EMPTY(&sc->tx.active)) {
2048ba14258fSLuiz Otavio O Souza 		CPSW_DEBUGF(sc, ("TX teardown is complete"));
2049ba14258fSLuiz Otavio O Souza 		sc->tx.teardown = 0;
2050ba14258fSLuiz Otavio O Souza 		sc->tx.running = 0;
2051ba14258fSLuiz Otavio O Souza 	}
2052ba14258fSLuiz Otavio O Souza 
2053ae6aefafSTim Kientzle 	return (removed);
2054ae6aefafSTim Kientzle }
2055ae6aefafSTim Kientzle 
2056ae6aefafSTim Kientzle /*
2057ae6aefafSTim Kientzle  *
2058ae6aefafSTim Kientzle  * Miscellaneous interrupts.
2059ae6aefafSTim Kientzle  *
2060ae6aefafSTim Kientzle  */
2061ae6aefafSTim Kientzle 
2062ae6aefafSTim Kientzle static void
cpsw_intr_rx_thresh(void * arg)2063ae6aefafSTim Kientzle cpsw_intr_rx_thresh(void *arg)
2064ae6aefafSTim Kientzle {
2065ba14258fSLuiz Otavio O Souza 	struct cpsw_softc *sc;
20662c7bc0f5SJustin Hibbits 	if_t ifp;
2067ba14258fSLuiz Otavio O Souza 	struct mbuf *received, *next;
2068ae6aefafSTim Kientzle 
2069ba14258fSLuiz Otavio O Souza 	sc = (struct cpsw_softc *)arg;
2070ba14258fSLuiz Otavio O Souza 	CPSW_RX_LOCK(sc);
2071ba14258fSLuiz Otavio O Souza 	received = cpsw_rx_dequeue(sc);
2072ba14258fSLuiz Otavio O Souza 	cpsw_rx_enqueue(sc);
2073ae6aefafSTim Kientzle 	cpsw_write_4(sc, CPSW_CPDMA_CPDMA_EOI_VECTOR, 0);
2074ba14258fSLuiz Otavio O Souza 	CPSW_RX_UNLOCK(sc);
2075ba14258fSLuiz Otavio O Souza 
2076ba14258fSLuiz Otavio O Souza 	while (received != NULL) {
2077ba14258fSLuiz Otavio O Souza 		next = received->m_nextpkt;
2078ba14258fSLuiz Otavio O Souza 		received->m_nextpkt = NULL;
2079ba14258fSLuiz Otavio O Souza 		ifp = received->m_pkthdr.rcvif;
20802c7bc0f5SJustin Hibbits 		if_input(ifp, received);
2081ba14258fSLuiz Otavio O Souza 		if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1);
2082ba14258fSLuiz Otavio O Souza 		received = next;
2083ba14258fSLuiz Otavio O Souza 	}
2084ae6aefafSTim Kientzle }
2085ae6aefafSTim Kientzle 
2086ae6aefafSTim Kientzle static void
cpsw_intr_misc_host_error(struct cpsw_softc * sc)2087ae6aefafSTim Kientzle cpsw_intr_misc_host_error(struct cpsw_softc *sc)
2088ae6aefafSTim Kientzle {
2089ae6aefafSTim Kientzle 	uint32_t intstat;
2090ae6aefafSTim Kientzle 	uint32_t dmastat;
2091ae6aefafSTim Kientzle 	int txerr, rxerr, txchan, rxchan;
2092ae6aefafSTim Kientzle 
2093ae6aefafSTim Kientzle 	printf("\n\n");
2094ae6aefafSTim Kientzle 	device_printf(sc->dev,
2095ae6aefafSTim Kientzle 	    "HOST ERROR:  PROGRAMMING ERROR DETECTED BY HARDWARE\n");
2096ae6aefafSTim Kientzle 	printf("\n\n");
2097ae6aefafSTim Kientzle 	intstat = cpsw_read_4(sc, CPSW_CPDMA_DMA_INTSTAT_MASKED);
2098ae6aefafSTim Kientzle 	device_printf(sc->dev, "CPSW_CPDMA_DMA_INTSTAT_MASKED=0x%x\n", intstat);
2099ae6aefafSTim Kientzle 	dmastat = cpsw_read_4(sc, CPSW_CPDMA_DMASTATUS);
2100ae6aefafSTim Kientzle 	device_printf(sc->dev, "CPSW_CPDMA_DMASTATUS=0x%x\n", dmastat);
2101ae6aefafSTim Kientzle 
2102ae6aefafSTim Kientzle 	txerr = (dmastat >> 20) & 15;
2103ae6aefafSTim Kientzle 	txchan = (dmastat >> 16) & 7;
2104ae6aefafSTim Kientzle 	rxerr = (dmastat >> 12) & 15;
2105ae6aefafSTim Kientzle 	rxchan = (dmastat >> 8) & 7;
2106ae6aefafSTim Kientzle 
2107ae6aefafSTim Kientzle 	switch (txerr) {
2108ae6aefafSTim Kientzle 	case 0: break;
2109ae6aefafSTim Kientzle 	case 1:	printf("SOP error on TX channel %d\n", txchan);
2110ae6aefafSTim Kientzle 		break;
2111ae6aefafSTim Kientzle 	case 2:	printf("Ownership bit not set on SOP buffer on TX channel %d\n", txchan);
2112ae6aefafSTim Kientzle 		break;
2113ae6aefafSTim Kientzle 	case 3:	printf("Zero Next Buffer but not EOP on TX channel %d\n", txchan);
2114ae6aefafSTim Kientzle 		break;
2115ae6aefafSTim Kientzle 	case 4:	printf("Zero Buffer Pointer on TX channel %d\n", txchan);
2116ae6aefafSTim Kientzle 		break;
2117ae6aefafSTim Kientzle 	case 5:	printf("Zero Buffer Length on TX channel %d\n", txchan);
2118ae6aefafSTim Kientzle 		break;
2119ae6aefafSTim Kientzle 	case 6:	printf("Packet length error on TX channel %d\n", txchan);
2120ae6aefafSTim Kientzle 		break;
2121ae6aefafSTim Kientzle 	default: printf("Unknown error on TX channel %d\n", txchan);
2122ae6aefafSTim Kientzle 		break;
2123ae6aefafSTim Kientzle 	}
2124ae6aefafSTim Kientzle 
2125ae6aefafSTim Kientzle 	if (txerr != 0) {
2126ae6aefafSTim Kientzle 		printf("CPSW_CPDMA_TX%d_HDP=0x%x\n",
2127ae6aefafSTim Kientzle 		    txchan, cpsw_read_4(sc, CPSW_CPDMA_TX_HDP(txchan)));
2128ae6aefafSTim Kientzle 		printf("CPSW_CPDMA_TX%d_CP=0x%x\n",
2129ae6aefafSTim Kientzle 		    txchan, cpsw_read_4(sc, CPSW_CPDMA_TX_CP(txchan)));
2130ae6aefafSTim Kientzle 		cpsw_dump_queue(sc, &sc->tx.active);
2131ae6aefafSTim Kientzle 	}
2132ae6aefafSTim Kientzle 
2133ae6aefafSTim Kientzle 	switch (rxerr) {
2134ae6aefafSTim Kientzle 	case 0: break;
2135ae6aefafSTim Kientzle 	case 2:	printf("Ownership bit not set on RX channel %d\n", rxchan);
2136ae6aefafSTim Kientzle 		break;
2137ae6aefafSTim Kientzle 	case 4:	printf("Zero Buffer Pointer on RX channel %d\n", rxchan);
2138ae6aefafSTim Kientzle 		break;
2139ae6aefafSTim Kientzle 	case 5:	printf("Zero Buffer Length on RX channel %d\n", rxchan);
2140ae6aefafSTim Kientzle 		break;
2141ae6aefafSTim Kientzle 	case 6:	printf("Buffer offset too big on RX channel %d\n", rxchan);
2142ae6aefafSTim Kientzle 		break;
2143ae6aefafSTim Kientzle 	default: printf("Unknown RX error on RX channel %d\n", rxchan);
2144ae6aefafSTim Kientzle 		break;
2145ae6aefafSTim Kientzle 	}
2146ae6aefafSTim Kientzle 
2147ae6aefafSTim Kientzle 	if (rxerr != 0) {
2148ae6aefafSTim Kientzle 		printf("CPSW_CPDMA_RX%d_HDP=0x%x\n",
2149ae6aefafSTim Kientzle 		    rxchan, cpsw_read_4(sc,CPSW_CPDMA_RX_HDP(rxchan)));
2150ae6aefafSTim Kientzle 		printf("CPSW_CPDMA_RX%d_CP=0x%x\n",
2151ae6aefafSTim Kientzle 		    rxchan, cpsw_read_4(sc, CPSW_CPDMA_RX_CP(rxchan)));
2152ae6aefafSTim Kientzle 		cpsw_dump_queue(sc, &sc->rx.active);
2153ae6aefafSTim Kientzle 	}
2154ae6aefafSTim Kientzle 
2155ae6aefafSTim Kientzle 	printf("\nALE Table\n");
2156ae6aefafSTim Kientzle 	cpsw_ale_dump_table(sc);
2157ae6aefafSTim Kientzle 
2158ae6aefafSTim Kientzle 	// XXX do something useful here??
2159ae6aefafSTim Kientzle 	panic("CPSW HOST ERROR INTERRUPT");
2160ae6aefafSTim Kientzle 
2161ae6aefafSTim Kientzle 	// Suppress this interrupt in the future.
2162ae6aefafSTim Kientzle 	cpsw_write_4(sc, CPSW_CPDMA_DMA_INTMASK_CLEAR, intstat);
2163ae6aefafSTim Kientzle 	printf("XXX HOST ERROR INTERRUPT SUPPRESSED\n");
2164ae6aefafSTim Kientzle 	// The watchdog will probably reset the controller
2165ae6aefafSTim Kientzle 	// in a little while.  It will probably fail again.
2166ae6aefafSTim Kientzle }
2167ae6aefafSTim Kientzle 
2168ae6aefafSTim Kientzle static void
cpsw_intr_misc(void * arg)2169ae6aefafSTim Kientzle cpsw_intr_misc(void *arg)
2170ae6aefafSTim Kientzle {
2171ae6aefafSTim Kientzle 	struct cpsw_softc *sc = arg;
2172ae6aefafSTim Kientzle 	uint32_t stat = cpsw_read_4(sc, CPSW_WR_C_MISC_STAT(0));
2173ae6aefafSTim Kientzle 
217423cd11b6SLuiz Otavio O Souza 	if (stat & CPSW_WR_C_MISC_EVNT_PEND)
217523cd11b6SLuiz Otavio O Souza 		CPSW_DEBUGF(sc, ("Time sync event interrupt unimplemented"));
217623cd11b6SLuiz Otavio O Souza 	if (stat & CPSW_WR_C_MISC_STAT_PEND)
2177ae6aefafSTim Kientzle 		cpsw_stats_collect(sc);
217823cd11b6SLuiz Otavio O Souza 	if (stat & CPSW_WR_C_MISC_HOST_PEND)
2179ae6aefafSTim Kientzle 		cpsw_intr_misc_host_error(sc);
218023cd11b6SLuiz Otavio O Souza 	if (stat & CPSW_WR_C_MISC_MDIOLINK) {
218123cd11b6SLuiz Otavio O Souza 		cpsw_write_4(sc, MDIOLINKINTMASKED,
218223cd11b6SLuiz Otavio O Souza 		    cpsw_read_4(sc, MDIOLINKINTMASKED));
218323cd11b6SLuiz Otavio O Souza 	}
218423cd11b6SLuiz Otavio O Souza 	if (stat & CPSW_WR_C_MISC_MDIOUSER) {
218523cd11b6SLuiz Otavio O Souza 		CPSW_DEBUGF(sc,
218623cd11b6SLuiz Otavio O Souza 		    ("MDIO operation completed interrupt unimplemented"));
218723cd11b6SLuiz Otavio O Souza 	}
2188ae6aefafSTim Kientzle 	cpsw_write_4(sc, CPSW_CPDMA_CPDMA_EOI_VECTOR, 3);
2189ae6aefafSTim Kientzle }
2190ae6aefafSTim Kientzle 
2191ae6aefafSTim Kientzle /*
2192ae6aefafSTim Kientzle  *
2193ae6aefafSTim Kientzle  * Periodic Checks and Watchdog.
2194ae6aefafSTim Kientzle  *
2195ae6aefafSTim Kientzle  */
2196ae6aefafSTim Kientzle 
2197ae6aefafSTim Kientzle static void
cpswp_tick(void * msc)219823cd11b6SLuiz Otavio O Souza cpswp_tick(void *msc)
2199ae6aefafSTim Kientzle {
220023cd11b6SLuiz Otavio O Souza 	struct cpswp_softc *sc = msc;
2201ae6aefafSTim Kientzle 
2202ae6aefafSTim Kientzle 	/* Check for media type change */
2203ae6aefafSTim Kientzle 	mii_tick(sc->mii);
220423cd11b6SLuiz Otavio O Souza 	if (sc->media_status != sc->mii->mii_media.ifm_media) {
2205ae6aefafSTim Kientzle 		printf("%s: media type changed (ifm_media=%x)\n", __func__,
2206ae6aefafSTim Kientzle 			sc->mii->mii_media.ifm_media);
220723cd11b6SLuiz Otavio O Souza 		cpswp_ifmedia_upd(sc->ifp);
2208ae6aefafSTim Kientzle 	}
2209ae6aefafSTim Kientzle 
2210ae6aefafSTim Kientzle 	/* Schedule another timeout one second from now */
221123cd11b6SLuiz Otavio O Souza 	callout_reset(&sc->mii_callout, hz, cpswp_tick, sc);
2212ae6aefafSTim Kientzle }
2213ae6aefafSTim Kientzle 
2214e53470feSOleksandr Tymoshenko static void
cpswp_ifmedia_sts(if_t ifp,struct ifmediareq * ifmr)22152c7bc0f5SJustin Hibbits cpswp_ifmedia_sts(if_t ifp, struct ifmediareq *ifmr)
2216e53470feSOleksandr Tymoshenko {
221723cd11b6SLuiz Otavio O Souza 	struct cpswp_softc *sc;
2218e53470feSOleksandr Tymoshenko 	struct mii_data *mii;
2219e53470feSOleksandr Tymoshenko 
22202c7bc0f5SJustin Hibbits 	sc = if_getsoftc(ifp);
22215a9c270aSLuiz Otavio O Souza 	CPSW_DEBUGF(sc->swsc, (""));
222223cd11b6SLuiz Otavio O Souza 	CPSW_PORT_LOCK(sc);
2223e53470feSOleksandr Tymoshenko 
2224e53470feSOleksandr Tymoshenko 	mii = sc->mii;
2225e53470feSOleksandr Tymoshenko 	mii_pollstat(mii);
2226e53470feSOleksandr Tymoshenko 
2227e53470feSOleksandr Tymoshenko 	ifmr->ifm_active = mii->mii_media_active;
2228e53470feSOleksandr Tymoshenko 	ifmr->ifm_status = mii->mii_media_status;
222923cd11b6SLuiz Otavio O Souza 	CPSW_PORT_UNLOCK(sc);
2230e53470feSOleksandr Tymoshenko }
2231e53470feSOleksandr Tymoshenko 
2232e53470feSOleksandr Tymoshenko static int
cpswp_ifmedia_upd(if_t ifp)22332c7bc0f5SJustin Hibbits cpswp_ifmedia_upd(if_t ifp)
2234e53470feSOleksandr Tymoshenko {
223523cd11b6SLuiz Otavio O Souza 	struct cpswp_softc *sc;
2236e53470feSOleksandr Tymoshenko 
22372c7bc0f5SJustin Hibbits 	sc = if_getsoftc(ifp);
22385a9c270aSLuiz Otavio O Souza 	CPSW_DEBUGF(sc->swsc, (""));
223923cd11b6SLuiz Otavio O Souza 	CPSW_PORT_LOCK(sc);
2240e53470feSOleksandr Tymoshenko 	mii_mediachg(sc->mii);
224123cd11b6SLuiz Otavio O Souza 	sc->media_status = sc->mii->mii_media.ifm_media;
224223cd11b6SLuiz Otavio O Souza 	CPSW_PORT_UNLOCK(sc);
2243e53470feSOleksandr Tymoshenko 
2244e53470feSOleksandr Tymoshenko 	return (0);
2245e53470feSOleksandr Tymoshenko }
2246e53470feSOleksandr Tymoshenko 
2247e53470feSOleksandr Tymoshenko static void
cpsw_tx_watchdog_full_reset(struct cpsw_softc * sc)2248ae6aefafSTim Kientzle cpsw_tx_watchdog_full_reset(struct cpsw_softc *sc)
2249e53470feSOleksandr Tymoshenko {
225023cd11b6SLuiz Otavio O Souza 	struct cpswp_softc *psc;
225123cd11b6SLuiz Otavio O Souza 	int i;
225223cd11b6SLuiz Otavio O Souza 
2253ae6aefafSTim Kientzle 	cpsw_debugf_head("CPSW watchdog");
225423cd11b6SLuiz Otavio O Souza 	device_printf(sc->dev, "watchdog timeout\n");
2255ba14258fSLuiz Otavio O Souza 	printf("CPSW_CPDMA_TX%d_HDP=0x%x\n", 0,
2256ba14258fSLuiz Otavio O Souza 	    cpsw_read_4(sc, CPSW_CPDMA_TX_HDP(0)));
2257ba14258fSLuiz Otavio O Souza 	printf("CPSW_CPDMA_TX%d_CP=0x%x\n", 0,
2258ba14258fSLuiz Otavio O Souza 	    cpsw_read_4(sc, CPSW_CPDMA_TX_CP(0)));
2259ba14258fSLuiz Otavio O Souza 	cpsw_dump_queue(sc, &sc->tx.active);
226023cd11b6SLuiz Otavio O Souza 	for (i = 0; i < CPSW_PORTS; i++) {
226123cd11b6SLuiz Otavio O Souza 		if (!sc->dualemac && i != sc->active_slave)
226223cd11b6SLuiz Otavio O Souza 			continue;
226323cd11b6SLuiz Otavio O Souza 		psc = device_get_softc(sc->port[i].dev);
226423cd11b6SLuiz Otavio O Souza 		CPSW_PORT_LOCK(psc);
226523cd11b6SLuiz Otavio O Souza 		cpswp_stop_locked(psc);
226623cd11b6SLuiz Otavio O Souza 		CPSW_PORT_UNLOCK(psc);
226723cd11b6SLuiz Otavio O Souza 	}
2268e53470feSOleksandr Tymoshenko }
2269e53470feSOleksandr Tymoshenko 
2270e53470feSOleksandr Tymoshenko static void
cpsw_tx_watchdog(void * msc)227123cd11b6SLuiz Otavio O Souza cpsw_tx_watchdog(void *msc)
2272e53470feSOleksandr Tymoshenko {
227323cd11b6SLuiz Otavio O Souza 	struct cpsw_softc *sc;
2274e53470feSOleksandr Tymoshenko 
227523cd11b6SLuiz Otavio O Souza 	sc = msc;
2276848a049bSLuiz Otavio O Souza 	CPSW_TX_LOCK(sc);
227723cd11b6SLuiz Otavio O Souza 	if (sc->tx.active_queue_len == 0 || !sc->tx.running) {
2278ae6aefafSTim Kientzle 		sc->watchdog.timer = 0; /* Nothing to do. */
2279ae6aefafSTim Kientzle 	} else if (sc->tx.queue_removes > sc->tx.queue_removes_at_last_tick) {
2280ae6aefafSTim Kientzle 		sc->watchdog.timer = 0;  /* Stuff done while we weren't looking. */
2281ae6aefafSTim Kientzle 	} else if (cpsw_tx_dequeue(sc) > 0) {
2282ae6aefafSTim Kientzle 		sc->watchdog.timer = 0;  /* We just did something. */
2283ebc4238eSTim Kientzle 	} else {
2284ae6aefafSTim Kientzle 		/* There was something to do but it didn't get done. */
2285ae6aefafSTim Kientzle 		++sc->watchdog.timer;
228623cd11b6SLuiz Otavio O Souza 		if (sc->watchdog.timer > 5) {
2287ae6aefafSTim Kientzle 			sc->watchdog.timer = 0;
2288ae6aefafSTim Kientzle 			++sc->watchdog.resets;
2289ae6aefafSTim Kientzle 			cpsw_tx_watchdog_full_reset(sc);
2290ebc4238eSTim Kientzle 		}
2291ebc4238eSTim Kientzle 	}
2292ae6aefafSTim Kientzle 	sc->tx.queue_removes_at_last_tick = sc->tx.queue_removes;
2293848a049bSLuiz Otavio O Souza 	CPSW_TX_UNLOCK(sc);
229423cd11b6SLuiz Otavio O Souza 
229523cd11b6SLuiz Otavio O Souza 	/* Schedule another timeout one second from now */
229623cd11b6SLuiz Otavio O Souza 	callout_reset(&sc->watchdog.callout, hz, cpsw_tx_watchdog, sc);
2297e53470feSOleksandr Tymoshenko }
2298e53470feSOleksandr Tymoshenko 
2299ae6aefafSTim Kientzle /*
2300ae6aefafSTim Kientzle  *
2301ae6aefafSTim Kientzle  * ALE support routines.
2302ae6aefafSTim Kientzle  *
2303ae6aefafSTim Kientzle  */
2304e53470feSOleksandr Tymoshenko 
2305e53470feSOleksandr Tymoshenko static void
cpsw_ale_read_entry(struct cpsw_softc * sc,uint16_t idx,uint32_t * ale_entry)2306e53470feSOleksandr Tymoshenko cpsw_ale_read_entry(struct cpsw_softc *sc, uint16_t idx, uint32_t *ale_entry)
2307e53470feSOleksandr Tymoshenko {
2308ae6aefafSTim Kientzle 	cpsw_write_4(sc, CPSW_ALE_TBLCTL, idx & 1023);
2309ae6aefafSTim Kientzle 	ale_entry[0] = cpsw_read_4(sc, CPSW_ALE_TBLW0);
2310ae6aefafSTim Kientzle 	ale_entry[1] = cpsw_read_4(sc, CPSW_ALE_TBLW1);
2311ae6aefafSTim Kientzle 	ale_entry[2] = cpsw_read_4(sc, CPSW_ALE_TBLW2);
2312e53470feSOleksandr Tymoshenko }
2313e53470feSOleksandr Tymoshenko 
2314e53470feSOleksandr Tymoshenko static void
cpsw_ale_write_entry(struct cpsw_softc * sc,uint16_t idx,uint32_t * ale_entry)2315e53470feSOleksandr Tymoshenko cpsw_ale_write_entry(struct cpsw_softc *sc, uint16_t idx, uint32_t *ale_entry)
2316e53470feSOleksandr Tymoshenko {
2317ae6aefafSTim Kientzle 	cpsw_write_4(sc, CPSW_ALE_TBLW0, ale_entry[0]);
2318ae6aefafSTim Kientzle 	cpsw_write_4(sc, CPSW_ALE_TBLW1, ale_entry[1]);
2319ae6aefafSTim Kientzle 	cpsw_write_4(sc, CPSW_ALE_TBLW2, ale_entry[2]);
2320ae6aefafSTim Kientzle 	cpsw_write_4(sc, CPSW_ALE_TBLCTL, 1 << 31 | (idx & 1023));
2321e53470feSOleksandr Tymoshenko }
2322e53470feSOleksandr Tymoshenko 
232323cd11b6SLuiz Otavio O Souza static void
cpsw_ale_remove_all_mc_entries(struct cpsw_softc * sc)2324ae6aefafSTim Kientzle cpsw_ale_remove_all_mc_entries(struct cpsw_softc *sc)
2325e53470feSOleksandr Tymoshenko {
2326e53470feSOleksandr Tymoshenko 	int i;
2327e53470feSOleksandr Tymoshenko 	uint32_t ale_entry[3];
2328ae6aefafSTim Kientzle 
232923cd11b6SLuiz Otavio O Souza 	/* First four entries are link address and broadcast. */
233023cd11b6SLuiz Otavio O Souza 	for (i = 10; i < CPSW_MAX_ALE_ENTRIES; i++) {
2331ae6aefafSTim Kientzle 		cpsw_ale_read_entry(sc, i, ale_entry);
233223cd11b6SLuiz Otavio O Souza 		if ((ALE_TYPE(ale_entry) == ALE_TYPE_ADDR ||
233323cd11b6SLuiz Otavio O Souza 		    ALE_TYPE(ale_entry) == ALE_TYPE_VLAN_ADDR) &&
233423cd11b6SLuiz Otavio O Souza 		    ALE_MCAST(ale_entry)  == 1) { /* MCast link addr */
2335ae6aefafSTim Kientzle 			ale_entry[0] = ale_entry[1] = ale_entry[2] = 0;
2336ae6aefafSTim Kientzle 			cpsw_ale_write_entry(sc, i, ale_entry);
2337ae6aefafSTim Kientzle 		}
2338ae6aefafSTim Kientzle 	}
2339ae6aefafSTim Kientzle }
2340ae6aefafSTim Kientzle 
2341ae6aefafSTim Kientzle static int
cpsw_ale_mc_entry_set(struct cpsw_softc * sc,uint8_t portmap,int vlan,uint8_t * mac)234223cd11b6SLuiz Otavio O Souza cpsw_ale_mc_entry_set(struct cpsw_softc *sc, uint8_t portmap, int vlan,
234323cd11b6SLuiz Otavio O Souza 	uint8_t *mac)
2344ae6aefafSTim Kientzle {
2345ae6aefafSTim Kientzle 	int free_index = -1, matching_index = -1, i;
234623cd11b6SLuiz Otavio O Souza 	uint32_t ale_entry[3], ale_type;
2347ae6aefafSTim Kientzle 
2348ae6aefafSTim Kientzle 	/* Find a matching entry or a free entry. */
234923cd11b6SLuiz Otavio O Souza 	for (i = 10; i < CPSW_MAX_ALE_ENTRIES; i++) {
2350e53470feSOleksandr Tymoshenko 		cpsw_ale_read_entry(sc, i, ale_entry);
2351ae6aefafSTim Kientzle 
2352ae6aefafSTim Kientzle 		/* Entry Type[61:60] is 0 for free entry */
235323cd11b6SLuiz Otavio O Souza 		if (free_index < 0 && ALE_TYPE(ale_entry) == 0)
2354ae6aefafSTim Kientzle 			free_index = i;
2355ae6aefafSTim Kientzle 
2356e53470feSOleksandr Tymoshenko 		if ((((ale_entry[1] >> 8) & 0xFF) == mac[0]) &&
2357e53470feSOleksandr Tymoshenko 		    (((ale_entry[1] >> 0) & 0xFF) == mac[1]) &&
2358e53470feSOleksandr Tymoshenko 		    (((ale_entry[0] >>24) & 0xFF) == mac[2]) &&
2359e53470feSOleksandr Tymoshenko 		    (((ale_entry[0] >>16) & 0xFF) == mac[3]) &&
2360e53470feSOleksandr Tymoshenko 		    (((ale_entry[0] >> 8) & 0xFF) == mac[4]) &&
2361e53470feSOleksandr Tymoshenko 		    (((ale_entry[0] >> 0) & 0xFF) == mac[5])) {
2362ae6aefafSTim Kientzle 			matching_index = i;
2363ae6aefafSTim Kientzle 			break;
2364e53470feSOleksandr Tymoshenko 		}
2365e53470feSOleksandr Tymoshenko 	}
2366e53470feSOleksandr Tymoshenko 
2367ae6aefafSTim Kientzle 	if (matching_index < 0) {
2368ae6aefafSTim Kientzle 		if (free_index < 0)
2369e53470feSOleksandr Tymoshenko 			return (ENOMEM);
2370ae6aefafSTim Kientzle 		i = free_index;
2371e53470feSOleksandr Tymoshenko 	}
2372e53470feSOleksandr Tymoshenko 
237323cd11b6SLuiz Otavio O Souza 	if (vlan != -1)
237423cd11b6SLuiz Otavio O Souza 		ale_type = ALE_TYPE_VLAN_ADDR << 28 | vlan << 16;
237523cd11b6SLuiz Otavio O Souza 	else
237623cd11b6SLuiz Otavio O Souza 		ale_type = ALE_TYPE_ADDR << 28;
237723cd11b6SLuiz Otavio O Souza 
2378e53470feSOleksandr Tymoshenko 	/* Set MAC address */
2379e53470feSOleksandr Tymoshenko 	ale_entry[0] = mac[2] << 24 | mac[3] << 16 | mac[4] << 8 | mac[5];
2380e53470feSOleksandr Tymoshenko 	ale_entry[1] = mac[0] << 8 | mac[1];
2381e53470feSOleksandr Tymoshenko 
238223cd11b6SLuiz Otavio O Souza 	/* Entry type[61:60] and Mcast fwd state[63:62] is fw(3). */
238323cd11b6SLuiz Otavio O Souza 	ale_entry[1] |= ALE_MCAST_FWD | ale_type;
2384e53470feSOleksandr Tymoshenko 
2385e53470feSOleksandr Tymoshenko 	/* Set portmask [68:66] */
2386e53470feSOleksandr Tymoshenko 	ale_entry[2] = (portmap & 7) << 2;
2387e53470feSOleksandr Tymoshenko 
2388e53470feSOleksandr Tymoshenko 	cpsw_ale_write_entry(sc, i, ale_entry);
2389e53470feSOleksandr Tymoshenko 
2390e53470feSOleksandr Tymoshenko 	return 0;
2391e53470feSOleksandr Tymoshenko }
2392e53470feSOleksandr Tymoshenko 
2393e53470feSOleksandr Tymoshenko static void
cpsw_ale_dump_table(struct cpsw_softc * sc)2394e53470feSOleksandr Tymoshenko cpsw_ale_dump_table(struct cpsw_softc *sc) {
2395e53470feSOleksandr Tymoshenko 	int i;
2396e53470feSOleksandr Tymoshenko 	uint32_t ale_entry[3];
2397e53470feSOleksandr Tymoshenko 	for (i = 0; i < CPSW_MAX_ALE_ENTRIES; i++) {
2398e53470feSOleksandr Tymoshenko 		cpsw_ale_read_entry(sc, i, ale_entry);
239923cd11b6SLuiz Otavio O Souza 		switch (ALE_TYPE(ale_entry)) {
240023cd11b6SLuiz Otavio O Souza 		case ALE_TYPE_VLAN:
240123cd11b6SLuiz Otavio O Souza 			printf("ALE[%4u] %08x %08x %08x ", i, ale_entry[2],
240223cd11b6SLuiz Otavio O Souza 				ale_entry[1], ale_entry[0]);
240323cd11b6SLuiz Otavio O Souza 			printf("type: %u ", ALE_TYPE(ale_entry));
240423cd11b6SLuiz Otavio O Souza 			printf("vlan: %u ", ALE_VLAN(ale_entry));
240523cd11b6SLuiz Otavio O Souza 			printf("untag: %u ", ALE_VLAN_UNTAG(ale_entry));
240623cd11b6SLuiz Otavio O Souza 			printf("reg flood: %u ", ALE_VLAN_REGFLOOD(ale_entry));
240723cd11b6SLuiz Otavio O Souza 			printf("unreg flood: %u ", ALE_VLAN_UNREGFLOOD(ale_entry));
240823cd11b6SLuiz Otavio O Souza 			printf("members: %u ", ALE_VLAN_MEMBERS(ale_entry));
240923cd11b6SLuiz Otavio O Souza 			printf("\n");
241023cd11b6SLuiz Otavio O Souza 			break;
241123cd11b6SLuiz Otavio O Souza 		case ALE_TYPE_ADDR:
241223cd11b6SLuiz Otavio O Souza 		case ALE_TYPE_VLAN_ADDR:
241323cd11b6SLuiz Otavio O Souza 			printf("ALE[%4u] %08x %08x %08x ", i, ale_entry[2],
241423cd11b6SLuiz Otavio O Souza 				ale_entry[1], ale_entry[0]);
241523cd11b6SLuiz Otavio O Souza 			printf("type: %u ", ALE_TYPE(ale_entry));
2416e53470feSOleksandr Tymoshenko 			printf("mac: %02x:%02x:%02x:%02x:%02x:%02x ",
2417e53470feSOleksandr Tymoshenko 				(ale_entry[1] >> 8) & 0xFF,
2418e53470feSOleksandr Tymoshenko 				(ale_entry[1] >> 0) & 0xFF,
2419e53470feSOleksandr Tymoshenko 				(ale_entry[0] >>24) & 0xFF,
2420e53470feSOleksandr Tymoshenko 				(ale_entry[0] >>16) & 0xFF,
2421e53470feSOleksandr Tymoshenko 				(ale_entry[0] >> 8) & 0xFF,
2422e53470feSOleksandr Tymoshenko 				(ale_entry[0] >> 0) & 0xFF);
242323cd11b6SLuiz Otavio O Souza 			printf(ALE_MCAST(ale_entry) ? "mcast " : "ucast ");
242423cd11b6SLuiz Otavio O Souza 			if (ALE_TYPE(ale_entry) == ALE_TYPE_VLAN_ADDR)
242523cd11b6SLuiz Otavio O Souza 				printf("vlan: %u ", ALE_VLAN(ale_entry));
242623cd11b6SLuiz Otavio O Souza 			printf("port: %u ", ALE_PORTS(ale_entry));
2427e53470feSOleksandr Tymoshenko 			printf("\n");
242823cd11b6SLuiz Otavio O Souza 			break;
2429e53470feSOleksandr Tymoshenko 		}
2430e53470feSOleksandr Tymoshenko 	}
2431ae6aefafSTim Kientzle 	printf("\n");
2432ae6aefafSTim Kientzle }
2433ae6aefafSTim Kientzle 
24349321bbc5SGleb Smirnoff static u_int
cpswp_set_maddr(void * arg,struct sockaddr_dl * sdl,u_int cnt)24359321bbc5SGleb Smirnoff cpswp_set_maddr(void *arg, struct sockaddr_dl *sdl, u_int cnt)
24369321bbc5SGleb Smirnoff {
24379321bbc5SGleb Smirnoff 	struct cpswp_softc *sc = arg;
24389321bbc5SGleb Smirnoff 	uint32_t portmask;
24399321bbc5SGleb Smirnoff 
24409321bbc5SGleb Smirnoff 	if (sc->swsc->dualemac)
24419321bbc5SGleb Smirnoff 		portmask = 1 << (sc->unit + 1) | 1 << 0;
24429321bbc5SGleb Smirnoff 	else
24439321bbc5SGleb Smirnoff 		portmask = 7;
24449321bbc5SGleb Smirnoff 
24459321bbc5SGleb Smirnoff 	cpsw_ale_mc_entry_set(sc->swsc, portmask, sc->vlan, LLADDR(sdl));
24469321bbc5SGleb Smirnoff 
24479321bbc5SGleb Smirnoff 	return (1);
24489321bbc5SGleb Smirnoff }
24499321bbc5SGleb Smirnoff 
2450ae6aefafSTim Kientzle static int
cpswp_ale_update_addresses(struct cpswp_softc * sc,int purge)245123cd11b6SLuiz Otavio O Souza cpswp_ale_update_addresses(struct cpswp_softc *sc, int purge)
2452ae6aefafSTim Kientzle {
2453ae6aefafSTim Kientzle 	uint8_t *mac;
245423cd11b6SLuiz Otavio O Souza 	uint32_t ale_entry[3], ale_type, portmask;
2455ae6aefafSTim Kientzle 
245623cd11b6SLuiz Otavio O Souza 	if (sc->swsc->dualemac) {
245723cd11b6SLuiz Otavio O Souza 		ale_type = ALE_TYPE_VLAN_ADDR << 28 | sc->vlan << 16;
245823cd11b6SLuiz Otavio O Souza 		portmask = 1 << (sc->unit + 1) | 1 << 0;
245923cd11b6SLuiz Otavio O Souza 	} else {
246023cd11b6SLuiz Otavio O Souza 		ale_type = ALE_TYPE_ADDR << 28;
246123cd11b6SLuiz Otavio O Souza 		portmask = 7;
2462ae6aefafSTim Kientzle 	}
2463ae6aefafSTim Kientzle 
246423cd11b6SLuiz Otavio O Souza 	/*
246523cd11b6SLuiz Otavio O Souza 	 * Route incoming packets for our MAC address to Port 0 (host).
246623cd11b6SLuiz Otavio O Souza 	 * For simplicity, keep this entry at table index 0 for port 1 and
246723cd11b6SLuiz Otavio O Souza 	 * at index 2 for port 2 in the ALE.
246823cd11b6SLuiz Otavio O Souza 	 */
24692c7bc0f5SJustin Hibbits 	mac = LLADDR((struct sockaddr_dl *)if_getifaddr(sc->ifp)->ifa_addr);
247023cd11b6SLuiz Otavio O Souza 	ale_entry[0] = mac[2] << 24 | mac[3] << 16 | mac[4] << 8 | mac[5];
247123cd11b6SLuiz Otavio O Souza 	ale_entry[1] = ale_type | mac[0] << 8 | mac[1]; /* addr entry + mac */
247223cd11b6SLuiz Otavio O Souza 	ale_entry[2] = 0; /* port = 0 */
247323cd11b6SLuiz Otavio O Souza 	cpsw_ale_write_entry(sc->swsc, 0 + 2 * sc->unit, ale_entry);
247423cd11b6SLuiz Otavio O Souza 
247523cd11b6SLuiz Otavio O Souza 	/* Set outgoing MAC Address for slave port. */
247623cd11b6SLuiz Otavio O Souza 	cpsw_write_4(sc->swsc, CPSW_PORT_P_SA_HI(sc->unit + 1),
247723cd11b6SLuiz Otavio O Souza 	    mac[3] << 24 | mac[2] << 16 | mac[1] << 8 | mac[0]);
247823cd11b6SLuiz Otavio O Souza 	cpsw_write_4(sc->swsc, CPSW_PORT_P_SA_LO(sc->unit + 1),
247923cd11b6SLuiz Otavio O Souza 	    mac[5] << 8 | mac[4]);
248023cd11b6SLuiz Otavio O Souza 
248123cd11b6SLuiz Otavio O Souza 	/* Keep the broadcast address at table entry 1 (or 3). */
2482ae6aefafSTim Kientzle 	ale_entry[0] = 0xffffffff; /* Lower 32 bits of MAC */
248323cd11b6SLuiz Otavio O Souza 	/* ALE_MCAST_FWD, Addr type, upper 16 bits of Mac */
248423cd11b6SLuiz Otavio O Souza 	ale_entry[1] = ALE_MCAST_FWD | ale_type | 0xffff;
248523cd11b6SLuiz Otavio O Souza 	ale_entry[2] = portmask << 2;
248623cd11b6SLuiz Otavio O Souza 	cpsw_ale_write_entry(sc->swsc, 1 + 2 * sc->unit, ale_entry);
2487ae6aefafSTim Kientzle 
2488ae6aefafSTim Kientzle 	/* SIOCDELMULTI doesn't specify the particular address
2489ae6aefafSTim Kientzle 	   being removed, so we have to remove all and rebuild. */
2490ae6aefafSTim Kientzle 	if (purge)
249123cd11b6SLuiz Otavio O Souza 		cpsw_ale_remove_all_mc_entries(sc->swsc);
2492ae6aefafSTim Kientzle 
2493ae6aefafSTim Kientzle         /* Set other multicast addrs desired. */
24949321bbc5SGleb Smirnoff 	if_foreach_llmaddr(sc->ifp, cpswp_set_maddr, sc);
249523cd11b6SLuiz Otavio O Souza 
249623cd11b6SLuiz Otavio O Souza 	return (0);
249723cd11b6SLuiz Otavio O Souza }
249823cd11b6SLuiz Otavio O Souza 
249923cd11b6SLuiz Otavio O Souza static int
cpsw_ale_update_vlan_table(struct cpsw_softc * sc,int vlan,int ports,int untag,int mcregflood,int mcunregflood)250051f8a15cSLuiz Otavio O Souza cpsw_ale_update_vlan_table(struct cpsw_softc *sc, int vlan, int ports,
250151f8a15cSLuiz Otavio O Souza 	int untag, int mcregflood, int mcunregflood)
250223cd11b6SLuiz Otavio O Souza {
250323cd11b6SLuiz Otavio O Souza 	int free_index, i, matching_index;
250423cd11b6SLuiz Otavio O Souza 	uint32_t ale_entry[3];
250523cd11b6SLuiz Otavio O Souza 
250623cd11b6SLuiz Otavio O Souza 	free_index = matching_index = -1;
250723cd11b6SLuiz Otavio O Souza 	/* Find a matching entry or a free entry. */
250823cd11b6SLuiz Otavio O Souza 	for (i = 5; i < CPSW_MAX_ALE_ENTRIES; i++) {
250923cd11b6SLuiz Otavio O Souza 		cpsw_ale_read_entry(sc, i, ale_entry);
251023cd11b6SLuiz Otavio O Souza 
251123cd11b6SLuiz Otavio O Souza 		/* Entry Type[61:60] is 0 for free entry */
251223cd11b6SLuiz Otavio O Souza 		if (free_index < 0 && ALE_TYPE(ale_entry) == 0)
251323cd11b6SLuiz Otavio O Souza 			free_index = i;
251423cd11b6SLuiz Otavio O Souza 
251523cd11b6SLuiz Otavio O Souza 		if (ALE_VLAN(ale_entry) == vlan) {
251623cd11b6SLuiz Otavio O Souza 			matching_index = i;
251723cd11b6SLuiz Otavio O Souza 			break;
251823cd11b6SLuiz Otavio O Souza 		}
251923cd11b6SLuiz Otavio O Souza 	}
252023cd11b6SLuiz Otavio O Souza 
252123cd11b6SLuiz Otavio O Souza 	if (matching_index < 0) {
252223cd11b6SLuiz Otavio O Souza 		if (free_index < 0)
252323cd11b6SLuiz Otavio O Souza 			return (-1);
252423cd11b6SLuiz Otavio O Souza 		i = free_index;
252523cd11b6SLuiz Otavio O Souza 	}
252623cd11b6SLuiz Otavio O Souza 
252751f8a15cSLuiz Otavio O Souza 	ale_entry[0] = (untag & 7) << 24 | (mcregflood & 7) << 16 |
252851f8a15cSLuiz Otavio O Souza 	    (mcunregflood & 7) << 8 | (ports & 7);
252923cd11b6SLuiz Otavio O Souza 	ale_entry[1] = ALE_TYPE_VLAN << 28 | vlan << 16;
253023cd11b6SLuiz Otavio O Souza 	ale_entry[2] = 0;
253123cd11b6SLuiz Otavio O Souza 	cpsw_ale_write_entry(sc, i, ale_entry);
2532ae6aefafSTim Kientzle 
2533ae6aefafSTim Kientzle 	return (0);
2534ae6aefafSTim Kientzle }
2535ae6aefafSTim Kientzle 
2536ae6aefafSTim Kientzle /*
2537ae6aefafSTim Kientzle  *
2538ae6aefafSTim Kientzle  * Statistics and Sysctls.
2539ae6aefafSTim Kientzle  *
2540ae6aefafSTim Kientzle  */
2541ae6aefafSTim Kientzle 
2542ae6aefafSTim Kientzle #if 0
2543ae6aefafSTim Kientzle static void
2544ae6aefafSTim Kientzle cpsw_stats_dump(struct cpsw_softc *sc)
2545ae6aefafSTim Kientzle {
2546ae6aefafSTim Kientzle 	int i;
2547ae6aefafSTim Kientzle 	uint32_t r;
2548ae6aefafSTim Kientzle 
2549ae6aefafSTim Kientzle 	for (i = 0; i < CPSW_SYSCTL_COUNT; ++i) {
2550ae6aefafSTim Kientzle 		r = cpsw_read_4(sc, CPSW_STATS_OFFSET +
2551ae6aefafSTim Kientzle 		    cpsw_stat_sysctls[i].reg);
255223cd11b6SLuiz Otavio O Souza 		CPSW_DEBUGF(sc, ("%s: %ju + %u = %ju", cpsw_stat_sysctls[i].oid,
2553ae6aefafSTim Kientzle 		    (intmax_t)sc->shadow_stats[i], r,
2554ae6aefafSTim Kientzle 		    (intmax_t)sc->shadow_stats[i] + r));
2555ae6aefafSTim Kientzle 	}
2556e53470feSOleksandr Tymoshenko }
2557e53470feSOleksandr Tymoshenko #endif
2558ae6aefafSTim Kientzle 
2559ae6aefafSTim Kientzle static void
cpsw_stats_collect(struct cpsw_softc * sc)2560ae6aefafSTim Kientzle cpsw_stats_collect(struct cpsw_softc *sc)
2561ae6aefafSTim Kientzle {
2562ae6aefafSTim Kientzle 	int i;
2563ae6aefafSTim Kientzle 	uint32_t r;
2564ae6aefafSTim Kientzle 
256523cd11b6SLuiz Otavio O Souza 	CPSW_DEBUGF(sc, ("Controller shadow statistics updated."));
2566ae6aefafSTim Kientzle 
2567ae6aefafSTim Kientzle 	for (i = 0; i < CPSW_SYSCTL_COUNT; ++i) {
2568ae6aefafSTim Kientzle 		r = cpsw_read_4(sc, CPSW_STATS_OFFSET +
2569ae6aefafSTim Kientzle 		    cpsw_stat_sysctls[i].reg);
2570ae6aefafSTim Kientzle 		sc->shadow_stats[i] += r;
257123cd11b6SLuiz Otavio O Souza 		cpsw_write_4(sc, CPSW_STATS_OFFSET + cpsw_stat_sysctls[i].reg,
257223cd11b6SLuiz Otavio O Souza 		    r);
2573ae6aefafSTim Kientzle 	}
2574ae6aefafSTim Kientzle }
2575ae6aefafSTim Kientzle 
2576ae6aefafSTim Kientzle static int
cpsw_stats_sysctl(SYSCTL_HANDLER_ARGS)2577ae6aefafSTim Kientzle cpsw_stats_sysctl(SYSCTL_HANDLER_ARGS)
2578ae6aefafSTim Kientzle {
2579ae6aefafSTim Kientzle 	struct cpsw_softc *sc;
2580ae6aefafSTim Kientzle 	struct cpsw_stat *stat;
2581ae6aefafSTim Kientzle 	uint64_t result;
2582ae6aefafSTim Kientzle 
2583ae6aefafSTim Kientzle 	sc = (struct cpsw_softc *)arg1;
2584ae6aefafSTim Kientzle 	stat = &cpsw_stat_sysctls[oidp->oid_number];
2585ae6aefafSTim Kientzle 	result = sc->shadow_stats[oidp->oid_number];
2586ae6aefafSTim Kientzle 	result += cpsw_read_4(sc, CPSW_STATS_OFFSET + stat->reg);
2587ae6aefafSTim Kientzle 	return (sysctl_handle_64(oidp, &result, 0, req));
2588ae6aefafSTim Kientzle }
2589ae6aefafSTim Kientzle 
2590ae6aefafSTim Kientzle static int
cpsw_stat_attached(SYSCTL_HANDLER_ARGS)2591ae6aefafSTim Kientzle cpsw_stat_attached(SYSCTL_HANDLER_ARGS)
2592ae6aefafSTim Kientzle {
2593ae6aefafSTim Kientzle 	struct cpsw_softc *sc;
2594ae6aefafSTim Kientzle 	struct bintime t;
2595ae6aefafSTim Kientzle 	unsigned result;
2596ae6aefafSTim Kientzle 
2597ae6aefafSTim Kientzle 	sc = (struct cpsw_softc *)arg1;
2598ae6aefafSTim Kientzle 	getbinuptime(&t);
2599ae6aefafSTim Kientzle 	bintime_sub(&t, &sc->attach_uptime);
2600ae6aefafSTim Kientzle 	result = t.sec;
2601ae6aefafSTim Kientzle 	return (sysctl_handle_int(oidp, &result, 0, req));
2602ae6aefafSTim Kientzle }
2603ae6aefafSTim Kientzle 
2604ae6aefafSTim Kientzle static int
cpsw_intr_coalesce(SYSCTL_HANDLER_ARGS)2605feeb22f3SLuiz Otavio O Souza cpsw_intr_coalesce(SYSCTL_HANDLER_ARGS)
2606feeb22f3SLuiz Otavio O Souza {
2607feeb22f3SLuiz Otavio O Souza 	int error;
2608feeb22f3SLuiz Otavio O Souza 	struct cpsw_softc *sc;
2609feeb22f3SLuiz Otavio O Souza 	uint32_t ctrl, intr_per_ms;
2610feeb22f3SLuiz Otavio O Souza 
2611feeb22f3SLuiz Otavio O Souza 	sc = (struct cpsw_softc *)arg1;
2612feeb22f3SLuiz Otavio O Souza 	error = sysctl_handle_int(oidp, &sc->coal_us, 0, req);
2613feeb22f3SLuiz Otavio O Souza 	if (error != 0 || req->newptr == NULL)
2614feeb22f3SLuiz Otavio O Souza 		return (error);
2615feeb22f3SLuiz Otavio O Souza 
2616feeb22f3SLuiz Otavio O Souza 	ctrl = cpsw_read_4(sc, CPSW_WR_INT_CONTROL);
2617feeb22f3SLuiz Otavio O Souza 	ctrl &= ~(CPSW_WR_INT_PACE_EN | CPSW_WR_INT_PRESCALE_MASK);
2618feeb22f3SLuiz Otavio O Souza 	if (sc->coal_us == 0) {
2619feeb22f3SLuiz Otavio O Souza 		/* Disable the interrupt pace hardware. */
2620feeb22f3SLuiz Otavio O Souza 		cpsw_write_4(sc, CPSW_WR_INT_CONTROL, ctrl);
2621feeb22f3SLuiz Otavio O Souza 		cpsw_write_4(sc, CPSW_WR_C_RX_IMAX(0), 0);
2622feeb22f3SLuiz Otavio O Souza 		cpsw_write_4(sc, CPSW_WR_C_TX_IMAX(0), 0);
2623feeb22f3SLuiz Otavio O Souza 		return (0);
2624feeb22f3SLuiz Otavio O Souza 	}
2625feeb22f3SLuiz Otavio O Souza 
2626feeb22f3SLuiz Otavio O Souza 	if (sc->coal_us > CPSW_WR_C_IMAX_US_MAX)
2627feeb22f3SLuiz Otavio O Souza 		sc->coal_us = CPSW_WR_C_IMAX_US_MAX;
2628feeb22f3SLuiz Otavio O Souza 	if (sc->coal_us < CPSW_WR_C_IMAX_US_MIN)
2629feeb22f3SLuiz Otavio O Souza 		sc->coal_us = CPSW_WR_C_IMAX_US_MIN;
2630feeb22f3SLuiz Otavio O Souza 	intr_per_ms = 1000 / sc->coal_us;
2631feeb22f3SLuiz Otavio O Souza 	/* Just to make sure... */
2632feeb22f3SLuiz Otavio O Souza 	if (intr_per_ms > CPSW_WR_C_IMAX_MAX)
2633feeb22f3SLuiz Otavio O Souza 		intr_per_ms = CPSW_WR_C_IMAX_MAX;
2634feeb22f3SLuiz Otavio O Souza 	if (intr_per_ms < CPSW_WR_C_IMAX_MIN)
2635feeb22f3SLuiz Otavio O Souza 		intr_per_ms = CPSW_WR_C_IMAX_MIN;
2636feeb22f3SLuiz Otavio O Souza 
2637feeb22f3SLuiz Otavio O Souza 	/* Set the prescale to produce 4us pulses from the 125 Mhz clock. */
2638feeb22f3SLuiz Otavio O Souza 	ctrl |= (125 * 4) & CPSW_WR_INT_PRESCALE_MASK;
2639feeb22f3SLuiz Otavio O Souza 
2640feeb22f3SLuiz Otavio O Souza 	/* Enable the interrupt pace hardware. */
2641feeb22f3SLuiz Otavio O Souza 	cpsw_write_4(sc, CPSW_WR_C_RX_IMAX(0), intr_per_ms);
2642feeb22f3SLuiz Otavio O Souza 	cpsw_write_4(sc, CPSW_WR_C_TX_IMAX(0), intr_per_ms);
2643feeb22f3SLuiz Otavio O Souza 	ctrl |= CPSW_WR_INT_C0_RX_PULSE | CPSW_WR_INT_C0_TX_PULSE;
2644feeb22f3SLuiz Otavio O Souza 	cpsw_write_4(sc, CPSW_WR_INT_CONTROL, ctrl);
2645feeb22f3SLuiz Otavio O Souza 
2646feeb22f3SLuiz Otavio O Souza 	return (0);
2647feeb22f3SLuiz Otavio O Souza }
2648feeb22f3SLuiz Otavio O Souza 
2649feeb22f3SLuiz Otavio O Souza static int
cpsw_stat_uptime(SYSCTL_HANDLER_ARGS)2650ae6aefafSTim Kientzle cpsw_stat_uptime(SYSCTL_HANDLER_ARGS)
2651ae6aefafSTim Kientzle {
265223cd11b6SLuiz Otavio O Souza 	struct cpsw_softc *swsc;
265323cd11b6SLuiz Otavio O Souza 	struct cpswp_softc *sc;
2654ae6aefafSTim Kientzle 	struct bintime t;
2655ae6aefafSTim Kientzle 	unsigned result;
2656ae6aefafSTim Kientzle 
265723cd11b6SLuiz Otavio O Souza 	swsc = arg1;
265823cd11b6SLuiz Otavio O Souza 	sc = device_get_softc(swsc->port[arg2].dev);
26592c7bc0f5SJustin Hibbits 	if (if_getdrvflags(sc->ifp) & IFF_DRV_RUNNING) {
2660ae6aefafSTim Kientzle 		getbinuptime(&t);
2661ae6aefafSTim Kientzle 		bintime_sub(&t, &sc->init_uptime);
2662ae6aefafSTim Kientzle 		result = t.sec;
2663ae6aefafSTim Kientzle 	} else
2664ae6aefafSTim Kientzle 		result = 0;
2665ae6aefafSTim Kientzle 	return (sysctl_handle_int(oidp, &result, 0, req));
2666ae6aefafSTim Kientzle }
2667ae6aefafSTim Kientzle 
2668ae6aefafSTim Kientzle static void
cpsw_add_queue_sysctls(struct sysctl_ctx_list * ctx,struct sysctl_oid * node,struct cpsw_queue * queue)266923cd11b6SLuiz Otavio O Souza cpsw_add_queue_sysctls(struct sysctl_ctx_list *ctx, struct sysctl_oid *node,
267023cd11b6SLuiz Otavio O Souza 	struct cpsw_queue *queue)
2671ae6aefafSTim Kientzle {
2672ae6aefafSTim Kientzle 	struct sysctl_oid_list *parent;
2673ae6aefafSTim Kientzle 
2674ae6aefafSTim Kientzle 	parent = SYSCTL_CHILDREN(node);
2675ae6aefafSTim Kientzle 	SYSCTL_ADD_INT(ctx, parent, OID_AUTO, "totalBuffers",
2676ae6aefafSTim Kientzle 	    CTLFLAG_RD, &queue->queue_slots, 0,
2677ae6aefafSTim Kientzle 	    "Total buffers currently assigned to this queue");
2678ae6aefafSTim Kientzle 	SYSCTL_ADD_INT(ctx, parent, OID_AUTO, "activeBuffers",
2679ae6aefafSTim Kientzle 	    CTLFLAG_RD, &queue->active_queue_len, 0,
2680ae6aefafSTim Kientzle 	    "Buffers currently registered with hardware controller");
2681ae6aefafSTim Kientzle 	SYSCTL_ADD_INT(ctx, parent, OID_AUTO, "maxActiveBuffers",
2682ae6aefafSTim Kientzle 	    CTLFLAG_RD, &queue->max_active_queue_len, 0,
2683ae6aefafSTim Kientzle 	    "Max value of activeBuffers since last driver reset");
2684ae6aefafSTim Kientzle 	SYSCTL_ADD_INT(ctx, parent, OID_AUTO, "availBuffers",
2685ae6aefafSTim Kientzle 	    CTLFLAG_RD, &queue->avail_queue_len, 0,
2686ae6aefafSTim Kientzle 	    "Buffers allocated to this queue but not currently "
2687ae6aefafSTim Kientzle 	    "registered with hardware controller");
2688ae6aefafSTim Kientzle 	SYSCTL_ADD_INT(ctx, parent, OID_AUTO, "maxAvailBuffers",
2689ae6aefafSTim Kientzle 	    CTLFLAG_RD, &queue->max_avail_queue_len, 0,
2690ae6aefafSTim Kientzle 	    "Max value of availBuffers since last driver reset");
2691ae6aefafSTim Kientzle 	SYSCTL_ADD_UINT(ctx, parent, OID_AUTO, "totalEnqueued",
2692ae6aefafSTim Kientzle 	    CTLFLAG_RD, &queue->queue_adds, 0,
2693ae6aefafSTim Kientzle 	    "Total buffers added to queue");
2694ae6aefafSTim Kientzle 	SYSCTL_ADD_UINT(ctx, parent, OID_AUTO, "totalDequeued",
2695ae6aefafSTim Kientzle 	    CTLFLAG_RD, &queue->queue_removes, 0,
2696ae6aefafSTim Kientzle 	    "Total buffers removed from queue");
2697ba14258fSLuiz Otavio O Souza 	SYSCTL_ADD_UINT(ctx, parent, OID_AUTO, "queueRestart",
2698ba14258fSLuiz Otavio O Souza 	    CTLFLAG_RD, &queue->queue_restart, 0,
2699ba14258fSLuiz Otavio O Souza 	    "Total times the queue has been restarted");
2700ae6aefafSTim Kientzle 	SYSCTL_ADD_UINT(ctx, parent, OID_AUTO, "longestChain",
2701ae6aefafSTim Kientzle 	    CTLFLAG_RD, &queue->longest_chain, 0,
2702ae6aefafSTim Kientzle 	    "Max buffers used for a single packet");
2703ae6aefafSTim Kientzle }
2704ae6aefafSTim Kientzle 
2705ae6aefafSTim Kientzle static void
cpsw_add_watchdog_sysctls(struct sysctl_ctx_list * ctx,struct sysctl_oid * node,struct cpsw_softc * sc)270623cd11b6SLuiz Otavio O Souza cpsw_add_watchdog_sysctls(struct sysctl_ctx_list *ctx, struct sysctl_oid *node,
270723cd11b6SLuiz Otavio O Souza 	struct cpsw_softc *sc)
2708ae6aefafSTim Kientzle {
2709ae6aefafSTim Kientzle 	struct sysctl_oid_list *parent;
2710ae6aefafSTim Kientzle 
2711ae6aefafSTim Kientzle 	parent = SYSCTL_CHILDREN(node);
2712ae6aefafSTim Kientzle 	SYSCTL_ADD_INT(ctx, parent, OID_AUTO, "resets",
2713ae6aefafSTim Kientzle 	    CTLFLAG_RD, &sc->watchdog.resets, 0,
2714ae6aefafSTim Kientzle 	    "Total number of watchdog resets");
2715ae6aefafSTim Kientzle }
2716ae6aefafSTim Kientzle 
2717ae6aefafSTim Kientzle static void
cpsw_add_sysctls(struct cpsw_softc * sc)2718ae6aefafSTim Kientzle cpsw_add_sysctls(struct cpsw_softc *sc)
2719ae6aefafSTim Kientzle {
2720ae6aefafSTim Kientzle 	struct sysctl_ctx_list *ctx;
2721ae6aefafSTim Kientzle 	struct sysctl_oid *stats_node, *queue_node, *node;
2722ae6aefafSTim Kientzle 	struct sysctl_oid_list *parent, *stats_parent, *queue_parent;
272323cd11b6SLuiz Otavio O Souza 	struct sysctl_oid_list *ports_parent, *port_parent;
272423cd11b6SLuiz Otavio O Souza 	char port[16];
2725ae6aefafSTim Kientzle 	int i;
2726ae6aefafSTim Kientzle 
2727ae6aefafSTim Kientzle 	ctx = device_get_sysctl_ctx(sc->dev);
2728ae6aefafSTim Kientzle 	parent = SYSCTL_CHILDREN(device_get_sysctl_tree(sc->dev));
2729ae6aefafSTim Kientzle 
273023cd11b6SLuiz Otavio O Souza 	SYSCTL_ADD_INT(ctx, parent, OID_AUTO, "debug",
273123cd11b6SLuiz Otavio O Souza 	    CTLFLAG_RW, &sc->debug, 0, "Enable switch debug messages");
273223cd11b6SLuiz Otavio O Souza 
2733ae6aefafSTim Kientzle 	SYSCTL_ADD_PROC(ctx, parent, OID_AUTO, "attachedSecs",
27347029da5cSPawel Biernacki 	    CTLTYPE_UINT | CTLFLAG_RD | CTLFLAG_NEEDGIANT,
27357029da5cSPawel Biernacki 	    sc, 0, cpsw_stat_attached, "IU",
2736ae6aefafSTim Kientzle 	    "Time since driver attach");
2737ae6aefafSTim Kientzle 
2738feeb22f3SLuiz Otavio O Souza 	SYSCTL_ADD_PROC(ctx, parent, OID_AUTO, "intr_coalesce_us",
27397029da5cSPawel Biernacki 	    CTLTYPE_UINT | CTLFLAG_RW | CTLFLAG_NEEDGIANT,
27407029da5cSPawel Biernacki 	    sc, 0, cpsw_intr_coalesce, "IU",
2741feeb22f3SLuiz Otavio O Souza 	    "minimum time between interrupts");
2742feeb22f3SLuiz Otavio O Souza 
274323cd11b6SLuiz Otavio O Souza 	node = SYSCTL_ADD_NODE(ctx, parent, OID_AUTO, "ports",
27447029da5cSPawel Biernacki 	    CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, "CPSW Ports Statistics");
274523cd11b6SLuiz Otavio O Souza 	ports_parent = SYSCTL_CHILDREN(node);
274623cd11b6SLuiz Otavio O Souza 	for (i = 0; i < CPSW_PORTS; i++) {
274723cd11b6SLuiz Otavio O Souza 		if (!sc->dualemac && i != sc->active_slave)
274823cd11b6SLuiz Otavio O Souza 			continue;
274923cd11b6SLuiz Otavio O Souza 		port[0] = '0' + i;
275023cd11b6SLuiz Otavio O Souza 		port[1] = '\0';
275123cd11b6SLuiz Otavio O Souza 		node = SYSCTL_ADD_NODE(ctx, ports_parent, OID_AUTO,
27527029da5cSPawel Biernacki 		    port, CTLFLAG_RD | CTLFLAG_MPSAFE, NULL,
27537029da5cSPawel Biernacki 		    "CPSW Port Statistics");
275423cd11b6SLuiz Otavio O Souza 		port_parent = SYSCTL_CHILDREN(node);
275523cd11b6SLuiz Otavio O Souza 		SYSCTL_ADD_PROC(ctx, port_parent, OID_AUTO, "uptime",
27567029da5cSPawel Biernacki 		    CTLTYPE_UINT | CTLFLAG_RD | CTLFLAG_NEEDGIANT, sc, i,
275723cd11b6SLuiz Otavio O Souza 		    cpsw_stat_uptime, "IU", "Seconds since driver init");
275823cd11b6SLuiz Otavio O Souza 	}
2759ae6aefafSTim Kientzle 
2760ae6aefafSTim Kientzle 	stats_node = SYSCTL_ADD_NODE(ctx, parent, OID_AUTO, "stats",
27617029da5cSPawel Biernacki 	    CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, "CPSW Statistics");
2762ae6aefafSTim Kientzle 	stats_parent = SYSCTL_CHILDREN(stats_node);
2763ae6aefafSTim Kientzle 	for (i = 0; i < CPSW_SYSCTL_COUNT; ++i) {
2764ae6aefafSTim Kientzle 		SYSCTL_ADD_PROC(ctx, stats_parent, i,
2765ae6aefafSTim Kientzle 				cpsw_stat_sysctls[i].oid,
27667029da5cSPawel Biernacki 				CTLTYPE_U64 | CTLFLAG_RD | CTLFLAG_NEEDGIANT,
27677029da5cSPawel Biernacki 				sc, 0, cpsw_stats_sysctl, "IU",
2768ae6aefafSTim Kientzle 				cpsw_stat_sysctls[i].oid);
2769ae6aefafSTim Kientzle 	}
2770ae6aefafSTim Kientzle 
2771ae6aefafSTim Kientzle 	queue_node = SYSCTL_ADD_NODE(ctx, parent, OID_AUTO, "queue",
27727029da5cSPawel Biernacki 	    CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, "CPSW Queue Statistics");
2773ae6aefafSTim Kientzle 	queue_parent = SYSCTL_CHILDREN(queue_node);
2774ae6aefafSTim Kientzle 
2775ae6aefafSTim Kientzle 	node = SYSCTL_ADD_NODE(ctx, queue_parent, OID_AUTO, "tx",
27767029da5cSPawel Biernacki 	    CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, "TX Queue Statistics");
2777ae6aefafSTim Kientzle 	cpsw_add_queue_sysctls(ctx, node, &sc->tx);
2778ae6aefafSTim Kientzle 
2779ae6aefafSTim Kientzle 	node = SYSCTL_ADD_NODE(ctx, queue_parent, OID_AUTO, "rx",
27807029da5cSPawel Biernacki 	    CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, "RX Queue Statistics");
2781ae6aefafSTim Kientzle 	cpsw_add_queue_sysctls(ctx, node, &sc->rx);
2782ae6aefafSTim Kientzle 
2783ae6aefafSTim Kientzle 	node = SYSCTL_ADD_NODE(ctx, parent, OID_AUTO, "watchdog",
27847029da5cSPawel Biernacki 	    CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, "Watchdog Statistics");
2785ae6aefafSTim Kientzle 	cpsw_add_watchdog_sysctls(ctx, node, sc);
2786ae6aefafSTim Kientzle }
2787a2c46b94SLuiz Otavio O Souza 
2788a2c46b94SLuiz Otavio O Souza #ifdef CPSW_ETHERSWITCH
2789a2c46b94SLuiz Otavio O Souza static etherswitch_info_t etherswitch_info = {
2790a2c46b94SLuiz Otavio O Souza 	.es_nports =		CPSW_PORTS + 1,
2791a2c46b94SLuiz Otavio O Souza 	.es_nvlangroups =	CPSW_VLANS,
2792a2c46b94SLuiz Otavio O Souza 	.es_name =		"TI Common Platform Ethernet Switch (CPSW)",
2793a2c46b94SLuiz Otavio O Souza 	.es_vlan_caps =		ETHERSWITCH_VLAN_DOT1Q,
2794a2c46b94SLuiz Otavio O Souza };
2795a2c46b94SLuiz Otavio O Souza 
2796a2c46b94SLuiz Otavio O Souza static etherswitch_info_t *
cpsw_getinfo(device_t dev)2797a2c46b94SLuiz Otavio O Souza cpsw_getinfo(device_t dev)
2798a2c46b94SLuiz Otavio O Souza {
2799a2c46b94SLuiz Otavio O Souza 	return (&etherswitch_info);
2800a2c46b94SLuiz Otavio O Souza }
2801a2c46b94SLuiz Otavio O Souza 
2802a2c46b94SLuiz Otavio O Souza static int
cpsw_getport(device_t dev,etherswitch_port_t * p)2803a2c46b94SLuiz Otavio O Souza cpsw_getport(device_t dev, etherswitch_port_t *p)
2804a2c46b94SLuiz Otavio O Souza {
2805a2c46b94SLuiz Otavio O Souza 	int err;
2806a2c46b94SLuiz Otavio O Souza 	struct cpsw_softc *sc;
2807a2c46b94SLuiz Otavio O Souza 	struct cpswp_softc *psc;
2808a2c46b94SLuiz Otavio O Souza 	struct ifmediareq *ifmr;
2809a2c46b94SLuiz Otavio O Souza 	uint32_t reg;
2810a2c46b94SLuiz Otavio O Souza 
2811a2c46b94SLuiz Otavio O Souza 	if (p->es_port < 0 || p->es_port > CPSW_PORTS)
2812a2c46b94SLuiz Otavio O Souza 		return (ENXIO);
2813a2c46b94SLuiz Otavio O Souza 
2814a2c46b94SLuiz Otavio O Souza 	err = 0;
2815a2c46b94SLuiz Otavio O Souza 	sc = device_get_softc(dev);
2816a2c46b94SLuiz Otavio O Souza 	if (p->es_port == CPSW_CPU_PORT) {
2817a2c46b94SLuiz Otavio O Souza 		p->es_flags |= ETHERSWITCH_PORT_CPU;
2818a2c46b94SLuiz Otavio O Souza  		ifmr = &p->es_ifmr;
2819a2c46b94SLuiz Otavio O Souza 		ifmr->ifm_current = ifmr->ifm_active =
2820a2c46b94SLuiz Otavio O Souza 		    IFM_ETHER | IFM_1000_T | IFM_FDX;
2821a2c46b94SLuiz Otavio O Souza 		ifmr->ifm_mask = 0;
2822a2c46b94SLuiz Otavio O Souza 		ifmr->ifm_status = IFM_ACTIVE | IFM_AVALID;
2823a2c46b94SLuiz Otavio O Souza 		ifmr->ifm_count = 0;
2824a2c46b94SLuiz Otavio O Souza 	} else {
2825a2c46b94SLuiz Otavio O Souza 		psc = device_get_softc(sc->port[p->es_port - 1].dev);
2826a2c46b94SLuiz Otavio O Souza 		err = ifmedia_ioctl(psc->ifp, &p->es_ifr,
2827a2c46b94SLuiz Otavio O Souza 		    &psc->mii->mii_media, SIOCGIFMEDIA);
2828a2c46b94SLuiz Otavio O Souza 	}
2829a2c46b94SLuiz Otavio O Souza 	reg = cpsw_read_4(sc, CPSW_PORT_P_VLAN(p->es_port));
2830a2c46b94SLuiz Otavio O Souza 	p->es_pvid = reg & ETHERSWITCH_VID_MASK;
2831a2c46b94SLuiz Otavio O Souza 
2832a2c46b94SLuiz Otavio O Souza 	reg = cpsw_read_4(sc, CPSW_ALE_PORTCTL(p->es_port));
2833a2c46b94SLuiz Otavio O Souza 	if (reg & ALE_PORTCTL_DROP_UNTAGGED)
2834a2c46b94SLuiz Otavio O Souza 		p->es_flags |= ETHERSWITCH_PORT_DROPUNTAGGED;
2835a2c46b94SLuiz Otavio O Souza 	if (reg & ALE_PORTCTL_INGRESS)
2836a2c46b94SLuiz Otavio O Souza 		p->es_flags |= ETHERSWITCH_PORT_INGRESS;
2837a2c46b94SLuiz Otavio O Souza 
2838a2c46b94SLuiz Otavio O Souza 	return (err);
2839a2c46b94SLuiz Otavio O Souza }
2840a2c46b94SLuiz Otavio O Souza 
2841a2c46b94SLuiz Otavio O Souza static int
cpsw_setport(device_t dev,etherswitch_port_t * p)2842a2c46b94SLuiz Otavio O Souza cpsw_setport(device_t dev, etherswitch_port_t *p)
2843a2c46b94SLuiz Otavio O Souza {
2844a2c46b94SLuiz Otavio O Souza 	struct cpsw_softc *sc;
2845a2c46b94SLuiz Otavio O Souza 	struct cpswp_softc *psc;
2846a2c46b94SLuiz Otavio O Souza 	struct ifmedia *ifm;
2847a2c46b94SLuiz Otavio O Souza 	uint32_t reg;
2848a2c46b94SLuiz Otavio O Souza 
2849a2c46b94SLuiz Otavio O Souza 	if (p->es_port < 0 || p->es_port > CPSW_PORTS)
2850a2c46b94SLuiz Otavio O Souza 		return (ENXIO);
2851a2c46b94SLuiz Otavio O Souza 
2852a2c46b94SLuiz Otavio O Souza 	sc = device_get_softc(dev);
2853a2c46b94SLuiz Otavio O Souza 	if (p->es_pvid != 0) {
2854a2c46b94SLuiz Otavio O Souza 		cpsw_write_4(sc, CPSW_PORT_P_VLAN(p->es_port),
2855a2c46b94SLuiz Otavio O Souza 		    p->es_pvid & ETHERSWITCH_VID_MASK);
2856a2c46b94SLuiz Otavio O Souza 	}
2857a2c46b94SLuiz Otavio O Souza 
2858a2c46b94SLuiz Otavio O Souza 	reg = cpsw_read_4(sc, CPSW_ALE_PORTCTL(p->es_port));
2859a2c46b94SLuiz Otavio O Souza 	if (p->es_flags & ETHERSWITCH_PORT_DROPUNTAGGED)
2860a2c46b94SLuiz Otavio O Souza 		reg |= ALE_PORTCTL_DROP_UNTAGGED;
2861a2c46b94SLuiz Otavio O Souza 	else
2862a2c46b94SLuiz Otavio O Souza 		reg &= ~ALE_PORTCTL_DROP_UNTAGGED;
2863a2c46b94SLuiz Otavio O Souza 	if (p->es_flags & ETHERSWITCH_PORT_INGRESS)
2864a2c46b94SLuiz Otavio O Souza 		reg |= ALE_PORTCTL_INGRESS;
2865a2c46b94SLuiz Otavio O Souza 	else
2866a2c46b94SLuiz Otavio O Souza 		reg &= ~ALE_PORTCTL_INGRESS;
2867a2c46b94SLuiz Otavio O Souza 	cpsw_write_4(sc, CPSW_ALE_PORTCTL(p->es_port), reg);
2868a2c46b94SLuiz Otavio O Souza 
2869a2c46b94SLuiz Otavio O Souza 	/* CPU port does not allow media settings. */
2870a2c46b94SLuiz Otavio O Souza 	if (p->es_port == CPSW_CPU_PORT)
2871a2c46b94SLuiz Otavio O Souza 		return (0);
2872a2c46b94SLuiz Otavio O Souza 
2873a2c46b94SLuiz Otavio O Souza 	psc = device_get_softc(sc->port[p->es_port - 1].dev);
2874a2c46b94SLuiz Otavio O Souza 	ifm = &psc->mii->mii_media;
2875a2c46b94SLuiz Otavio O Souza 
2876a2c46b94SLuiz Otavio O Souza 	return (ifmedia_ioctl(psc->ifp, &p->es_ifr, ifm, SIOCSIFMEDIA));
2877a2c46b94SLuiz Otavio O Souza }
2878a2c46b94SLuiz Otavio O Souza 
2879a2c46b94SLuiz Otavio O Souza static int
cpsw_getconf(device_t dev,etherswitch_conf_t * conf)2880a2c46b94SLuiz Otavio O Souza cpsw_getconf(device_t dev, etherswitch_conf_t *conf)
2881a2c46b94SLuiz Otavio O Souza {
2882a2c46b94SLuiz Otavio O Souza 
2883a2c46b94SLuiz Otavio O Souza 	/* Return the VLAN mode. */
2884a2c46b94SLuiz Otavio O Souza 	conf->cmd = ETHERSWITCH_CONF_VLAN_MODE;
2885a2c46b94SLuiz Otavio O Souza 	conf->vlan_mode = ETHERSWITCH_VLAN_DOT1Q;
2886a2c46b94SLuiz Otavio O Souza 
2887a2c46b94SLuiz Otavio O Souza 	return (0);
2888a2c46b94SLuiz Otavio O Souza }
2889a2c46b94SLuiz Otavio O Souza 
2890a2c46b94SLuiz Otavio O Souza static int
cpsw_getvgroup(device_t dev,etherswitch_vlangroup_t * vg)2891a2c46b94SLuiz Otavio O Souza cpsw_getvgroup(device_t dev, etherswitch_vlangroup_t *vg)
2892a2c46b94SLuiz Otavio O Souza {
2893a2c46b94SLuiz Otavio O Souza 	int i, vid;
2894a2c46b94SLuiz Otavio O Souza 	uint32_t ale_entry[3];
2895a2c46b94SLuiz Otavio O Souza 	struct cpsw_softc *sc;
2896a2c46b94SLuiz Otavio O Souza 
2897a2c46b94SLuiz Otavio O Souza 	sc = device_get_softc(dev);
2898a2c46b94SLuiz Otavio O Souza 
2899a2c46b94SLuiz Otavio O Souza 	if (vg->es_vlangroup >= CPSW_VLANS)
2900a2c46b94SLuiz Otavio O Souza 		return (EINVAL);
2901a2c46b94SLuiz Otavio O Souza 
2902a2c46b94SLuiz Otavio O Souza 	vg->es_vid = 0;
2903a2c46b94SLuiz Otavio O Souza 	vid = cpsw_vgroups[vg->es_vlangroup].vid;
2904a2c46b94SLuiz Otavio O Souza 	if (vid == -1)
2905a2c46b94SLuiz Otavio O Souza 		return (0);
2906a2c46b94SLuiz Otavio O Souza 
2907a2c46b94SLuiz Otavio O Souza 	for (i = 0; i < CPSW_MAX_ALE_ENTRIES; i++) {
2908a2c46b94SLuiz Otavio O Souza 		cpsw_ale_read_entry(sc, i, ale_entry);
2909a2c46b94SLuiz Otavio O Souza 		if (ALE_TYPE(ale_entry) != ALE_TYPE_VLAN)
2910a2c46b94SLuiz Otavio O Souza 			continue;
2911a2c46b94SLuiz Otavio O Souza 		if (vid != ALE_VLAN(ale_entry))
2912a2c46b94SLuiz Otavio O Souza 			continue;
2913a2c46b94SLuiz Otavio O Souza 
2914a2c46b94SLuiz Otavio O Souza 		vg->es_fid = 0;
2915a2c46b94SLuiz Otavio O Souza 		vg->es_vid = ALE_VLAN(ale_entry) | ETHERSWITCH_VID_VALID;
2916a2c46b94SLuiz Otavio O Souza 		vg->es_member_ports = ALE_VLAN_MEMBERS(ale_entry);
2917a2c46b94SLuiz Otavio O Souza 		vg->es_untagged_ports = ALE_VLAN_UNTAG(ale_entry);
2918a2c46b94SLuiz Otavio O Souza 	}
2919a2c46b94SLuiz Otavio O Souza 
2920a2c46b94SLuiz Otavio O Souza 	return (0);
2921a2c46b94SLuiz Otavio O Souza }
2922a2c46b94SLuiz Otavio O Souza 
2923a2c46b94SLuiz Otavio O Souza static void
cpsw_remove_vlan(struct cpsw_softc * sc,int vlan)2924a2c46b94SLuiz Otavio O Souza cpsw_remove_vlan(struct cpsw_softc *sc, int vlan)
2925a2c46b94SLuiz Otavio O Souza {
2926a2c46b94SLuiz Otavio O Souza 	int i;
2927a2c46b94SLuiz Otavio O Souza 	uint32_t ale_entry[3];
2928a2c46b94SLuiz Otavio O Souza 
2929a2c46b94SLuiz Otavio O Souza 	for (i = 0; i < CPSW_MAX_ALE_ENTRIES; i++) {
2930a2c46b94SLuiz Otavio O Souza 		cpsw_ale_read_entry(sc, i, ale_entry);
2931a2c46b94SLuiz Otavio O Souza 		if (ALE_TYPE(ale_entry) != ALE_TYPE_VLAN)
2932a2c46b94SLuiz Otavio O Souza 			continue;
2933a2c46b94SLuiz Otavio O Souza 		if (vlan != ALE_VLAN(ale_entry))
2934a2c46b94SLuiz Otavio O Souza 			continue;
2935a2c46b94SLuiz Otavio O Souza 		ale_entry[0] = ale_entry[1] = ale_entry[2] = 0;
2936a2c46b94SLuiz Otavio O Souza 		cpsw_ale_write_entry(sc, i, ale_entry);
2937a2c46b94SLuiz Otavio O Souza 		break;
2938a2c46b94SLuiz Otavio O Souza 	}
2939a2c46b94SLuiz Otavio O Souza }
2940a2c46b94SLuiz Otavio O Souza 
2941a2c46b94SLuiz Otavio O Souza static int
cpsw_setvgroup(device_t dev,etherswitch_vlangroup_t * vg)2942a2c46b94SLuiz Otavio O Souza cpsw_setvgroup(device_t dev, etherswitch_vlangroup_t *vg)
2943a2c46b94SLuiz Otavio O Souza {
2944a2c46b94SLuiz Otavio O Souza 	int i;
2945a2c46b94SLuiz Otavio O Souza 	struct cpsw_softc *sc;
2946a2c46b94SLuiz Otavio O Souza 
2947a2c46b94SLuiz Otavio O Souza 	sc = device_get_softc(dev);
2948a2c46b94SLuiz Otavio O Souza 
2949a2c46b94SLuiz Otavio O Souza 	for (i = 0; i < CPSW_VLANS; i++) {
2950a2c46b94SLuiz Otavio O Souza 		/* Is this Vlan ID in use by another vlangroup ? */
2951a2c46b94SLuiz Otavio O Souza 		if (vg->es_vlangroup != i && cpsw_vgroups[i].vid == vg->es_vid)
2952a2c46b94SLuiz Otavio O Souza 			return (EINVAL);
2953a2c46b94SLuiz Otavio O Souza 	}
2954a2c46b94SLuiz Otavio O Souza 
2955a2c46b94SLuiz Otavio O Souza 	if (vg->es_vid == 0) {
2956a2c46b94SLuiz Otavio O Souza 		if (cpsw_vgroups[vg->es_vlangroup].vid == -1)
2957a2c46b94SLuiz Otavio O Souza 			return (0);
2958a2c46b94SLuiz Otavio O Souza 		cpsw_remove_vlan(sc, cpsw_vgroups[vg->es_vlangroup].vid);
2959a2c46b94SLuiz Otavio O Souza 		cpsw_vgroups[vg->es_vlangroup].vid = -1;
2960a2c46b94SLuiz Otavio O Souza 		vg->es_untagged_ports = 0;
2961a2c46b94SLuiz Otavio O Souza 		vg->es_member_ports = 0;
2962a2c46b94SLuiz Otavio O Souza 		vg->es_vid = 0;
2963a2c46b94SLuiz Otavio O Souza 		return (0);
2964a2c46b94SLuiz Otavio O Souza 	}
2965a2c46b94SLuiz Otavio O Souza 
2966a2c46b94SLuiz Otavio O Souza 	vg->es_vid &= ETHERSWITCH_VID_MASK;
2967a2c46b94SLuiz Otavio O Souza 	vg->es_member_ports &= CPSW_PORTS_MASK;
2968a2c46b94SLuiz Otavio O Souza 	vg->es_untagged_ports &= CPSW_PORTS_MASK;
2969a2c46b94SLuiz Otavio O Souza 
2970a2c46b94SLuiz Otavio O Souza 	if (cpsw_vgroups[vg->es_vlangroup].vid != -1 &&
2971a2c46b94SLuiz Otavio O Souza 	    cpsw_vgroups[vg->es_vlangroup].vid != vg->es_vid)
2972a2c46b94SLuiz Otavio O Souza 		return (EINVAL);
2973a2c46b94SLuiz Otavio O Souza 
2974a2c46b94SLuiz Otavio O Souza 	cpsw_vgroups[vg->es_vlangroup].vid = vg->es_vid;
2975a2c46b94SLuiz Otavio O Souza 	cpsw_ale_update_vlan_table(sc, vg->es_vid, vg->es_member_ports,
2976a2c46b94SLuiz Otavio O Souza 	    vg->es_untagged_ports, vg->es_member_ports, 0);
2977a2c46b94SLuiz Otavio O Souza 
2978a2c46b94SLuiz Otavio O Souza 	return (0);
2979a2c46b94SLuiz Otavio O Souza }
2980a2c46b94SLuiz Otavio O Souza 
2981a2c46b94SLuiz Otavio O Souza static int
cpsw_readreg(device_t dev,int addr)2982a2c46b94SLuiz Otavio O Souza cpsw_readreg(device_t dev, int addr)
2983a2c46b94SLuiz Otavio O Souza {
2984a2c46b94SLuiz Otavio O Souza 
2985a2c46b94SLuiz Otavio O Souza 	/* Not supported. */
2986a2c46b94SLuiz Otavio O Souza 	return (0);
2987a2c46b94SLuiz Otavio O Souza }
2988a2c46b94SLuiz Otavio O Souza 
2989a2c46b94SLuiz Otavio O Souza static int
cpsw_writereg(device_t dev,int addr,int value)2990a2c46b94SLuiz Otavio O Souza cpsw_writereg(device_t dev, int addr, int value)
2991a2c46b94SLuiz Otavio O Souza {
2992a2c46b94SLuiz Otavio O Souza 
2993a2c46b94SLuiz Otavio O Souza 	/* Not supported. */
2994a2c46b94SLuiz Otavio O Souza 	return (0);
2995a2c46b94SLuiz Otavio O Souza }
2996a2c46b94SLuiz Otavio O Souza 
2997a2c46b94SLuiz Otavio O Souza static int
cpsw_readphy(device_t dev,int phy,int reg)2998a2c46b94SLuiz Otavio O Souza cpsw_readphy(device_t dev, int phy, int reg)
2999a2c46b94SLuiz Otavio O Souza {
3000a2c46b94SLuiz Otavio O Souza 
3001a2c46b94SLuiz Otavio O Souza 	/* Not supported. */
3002a2c46b94SLuiz Otavio O Souza 	return (0);
3003a2c46b94SLuiz Otavio O Souza }
3004a2c46b94SLuiz Otavio O Souza 
3005a2c46b94SLuiz Otavio O Souza static int
cpsw_writephy(device_t dev,int phy,int reg,int data)3006a2c46b94SLuiz Otavio O Souza cpsw_writephy(device_t dev, int phy, int reg, int data)
3007a2c46b94SLuiz Otavio O Souza {
3008a2c46b94SLuiz Otavio O Souza 
3009a2c46b94SLuiz Otavio O Souza 	/* Not supported. */
3010a2c46b94SLuiz Otavio O Souza 	return (0);
3011a2c46b94SLuiz Otavio O Souza }
3012a2c46b94SLuiz Otavio O Souza #endif
3013