xref: /freebsd/sys/dev/etherswitch/felix/felix.c (revision b29549c7f939ec93cae1bac115b0010e07538573)
1451bcf1bSMarcin Wojtas /*-
2451bcf1bSMarcin Wojtas  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3451bcf1bSMarcin Wojtas  *
4451bcf1bSMarcin Wojtas  * Copyright (c) 2021 Alstom Group.
5451bcf1bSMarcin Wojtas  * Copyright (c) 2021 Semihalf.
6451bcf1bSMarcin Wojtas  *
7451bcf1bSMarcin Wojtas  * Redistribution and use in source and binary forms, with or without
8451bcf1bSMarcin Wojtas  * modification, are permitted provided that the following conditions
9451bcf1bSMarcin Wojtas  * are met:
10451bcf1bSMarcin Wojtas  * 1. Redistributions of source code must retain the above copyright
11451bcf1bSMarcin Wojtas  *    notice, this list of conditions and the following disclaimer.
12451bcf1bSMarcin Wojtas  * 2. Redistributions in binary form must reproduce the above copyright
13451bcf1bSMarcin Wojtas  *    notice, this list of conditions and the following disclaimer in the
14451bcf1bSMarcin Wojtas  *    documentation and/or other materials provided with the distribution.
15451bcf1bSMarcin Wojtas  *
16451bcf1bSMarcin Wojtas  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17451bcf1bSMarcin Wojtas  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18451bcf1bSMarcin Wojtas  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19451bcf1bSMarcin Wojtas  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20451bcf1bSMarcin Wojtas  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21451bcf1bSMarcin Wojtas  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22451bcf1bSMarcin Wojtas  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23451bcf1bSMarcin Wojtas  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24451bcf1bSMarcin Wojtas  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25451bcf1bSMarcin Wojtas  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26451bcf1bSMarcin Wojtas  * SUCH DAMAGE.
27451bcf1bSMarcin Wojtas  */
28451bcf1bSMarcin Wojtas 
29451bcf1bSMarcin Wojtas #include <sys/cdefs.h>
30451bcf1bSMarcin Wojtas __FBSDID("$FreeBSD$");
31451bcf1bSMarcin Wojtas 
32451bcf1bSMarcin Wojtas #include <sys/param.h>
33451bcf1bSMarcin Wojtas #include <sys/bus.h>
34451bcf1bSMarcin Wojtas #include <sys/kernel.h>
35451bcf1bSMarcin Wojtas #include <sys/module.h>
36451bcf1bSMarcin Wojtas #include <sys/socket.h>
37451bcf1bSMarcin Wojtas #include <sys/sockio.h>
38d88aecceSKornel Duleba #include <sys/sysctl.h>
39451bcf1bSMarcin Wojtas #include <sys/rman.h>
40451bcf1bSMarcin Wojtas 
41451bcf1bSMarcin Wojtas #include <machine/bus.h>
42451bcf1bSMarcin Wojtas #include <machine/resource.h>
43451bcf1bSMarcin Wojtas 
44451bcf1bSMarcin Wojtas #include <net/if.h>
45451bcf1bSMarcin Wojtas #include <net/if_media.h>
46451bcf1bSMarcin Wojtas #include <net/if_types.h>
47451bcf1bSMarcin Wojtas 
4829cf6a79SKornel Duleba #include <dev/enetc/enetc_mdio.h>
4929cf6a79SKornel Duleba 
50451bcf1bSMarcin Wojtas #include <dev/etherswitch/etherswitch.h>
51451bcf1bSMarcin Wojtas #include <dev/mii/mii.h>
52451bcf1bSMarcin Wojtas #include <dev/mii/miivar.h>
53451bcf1bSMarcin Wojtas #include <dev/pci/pcireg.h>
54451bcf1bSMarcin Wojtas #include <dev/pci/pcivar.h>
55451bcf1bSMarcin Wojtas 
56451bcf1bSMarcin Wojtas #include <dev/ofw/ofw_bus.h>
57451bcf1bSMarcin Wojtas #include <dev/ofw/ofw_bus_subr.h>
58451bcf1bSMarcin Wojtas 
59451bcf1bSMarcin Wojtas #include <dev/etherswitch/felix/felix_var.h>
60451bcf1bSMarcin Wojtas #include <dev/etherswitch/felix/felix_reg.h>
61451bcf1bSMarcin Wojtas 
62451bcf1bSMarcin Wojtas #include "etherswitch_if.h"
63451bcf1bSMarcin Wojtas #include "miibus_if.h"
64451bcf1bSMarcin Wojtas 
65451bcf1bSMarcin Wojtas MALLOC_DECLARE(M_FELIX);
66451bcf1bSMarcin Wojtas MALLOC_DEFINE(M_FELIX, "felix", "felix switch");
67451bcf1bSMarcin Wojtas 
68451bcf1bSMarcin Wojtas static device_probe_t felix_probe;
69451bcf1bSMarcin Wojtas static device_attach_t felix_attach;
70451bcf1bSMarcin Wojtas static device_detach_t felix_detach;
71451bcf1bSMarcin Wojtas 
72451bcf1bSMarcin Wojtas static etherswitch_info_t* felix_getinfo(device_t);
73451bcf1bSMarcin Wojtas static int felix_getconf(device_t, etherswitch_conf_t *);
74451bcf1bSMarcin Wojtas static int felix_setconf(device_t, etherswitch_conf_t *);
75451bcf1bSMarcin Wojtas static void felix_lock(device_t);
76451bcf1bSMarcin Wojtas static void felix_unlock(device_t);
77451bcf1bSMarcin Wojtas static int felix_getport(device_t, etherswitch_port_t *);
78451bcf1bSMarcin Wojtas static int felix_setport(device_t, etherswitch_port_t *);
79451bcf1bSMarcin Wojtas static int felix_readreg_wrapper(device_t, int);
80451bcf1bSMarcin Wojtas static int felix_writereg_wrapper(device_t, int, int);
81451bcf1bSMarcin Wojtas static int felix_readphy(device_t, int, int);
82451bcf1bSMarcin Wojtas static int felix_writephy(device_t, int, int, int);
83451bcf1bSMarcin Wojtas static int felix_setvgroup(device_t, etherswitch_vlangroup_t *);
84451bcf1bSMarcin Wojtas static int felix_getvgroup(device_t, etherswitch_vlangroup_t *);
85451bcf1bSMarcin Wojtas 
86451bcf1bSMarcin Wojtas static int felix_parse_port_fdt(felix_softc_t, phandle_t, int *);
87451bcf1bSMarcin Wojtas static int felix_setup(felix_softc_t);
88451bcf1bSMarcin Wojtas static void felix_setup_port(felix_softc_t, int);
89451bcf1bSMarcin Wojtas 
90451bcf1bSMarcin Wojtas static void felix_tick(void *);
912e6a8c1aSJustin Hibbits static int felix_ifmedia_upd(if_t);
922e6a8c1aSJustin Hibbits static void felix_ifmedia_sts(if_t, struct ifmediareq *);
93451bcf1bSMarcin Wojtas 
94451bcf1bSMarcin Wojtas static void felix_get_port_cfg(felix_softc_t, etherswitch_port_t *);
95451bcf1bSMarcin Wojtas static void felix_set_port_cfg(felix_softc_t, etherswitch_port_t *);
96451bcf1bSMarcin Wojtas 
97451bcf1bSMarcin Wojtas static bool felix_is_phyport(felix_softc_t, int);
98451bcf1bSMarcin Wojtas static struct mii_data *felix_miiforport(felix_softc_t, unsigned int);
99451bcf1bSMarcin Wojtas 
100451bcf1bSMarcin Wojtas static struct felix_pci_id felix_pci_ids[] = {
101451bcf1bSMarcin Wojtas 	{PCI_VENDOR_FREESCALE, FELIX_DEV_ID, FELIX_DEV_NAME},
102451bcf1bSMarcin Wojtas 	{0, 0, NULL}
103451bcf1bSMarcin Wojtas };
104451bcf1bSMarcin Wojtas 
105451bcf1bSMarcin Wojtas static device_method_t felix_methods[] = {
106451bcf1bSMarcin Wojtas 	/* device interface */
107451bcf1bSMarcin Wojtas 	DEVMETHOD(device_probe,			felix_probe),
108451bcf1bSMarcin Wojtas 	DEVMETHOD(device_attach,		felix_attach),
109451bcf1bSMarcin Wojtas 	DEVMETHOD(device_detach,		felix_detach),
110451bcf1bSMarcin Wojtas 
111451bcf1bSMarcin Wojtas 	/* bus interface */
112451bcf1bSMarcin Wojtas 	DEVMETHOD(bus_add_child,		device_add_child_ordered),
1130f84feb9SKornel Duleba 	DEVMETHOD(bus_setup_intr,		bus_generic_setup_intr),
1140f84feb9SKornel Duleba 	DEVMETHOD(bus_teardown_intr,		bus_generic_teardown_intr),
1150f84feb9SKornel Duleba 	DEVMETHOD(bus_release_resource,		bus_generic_release_resource),
1160f84feb9SKornel Duleba 	DEVMETHOD(bus_activate_resource,	bus_generic_activate_resource),
1170f84feb9SKornel Duleba 	DEVMETHOD(bus_deactivate_resource,	bus_generic_deactivate_resource),
1180f84feb9SKornel Duleba 	DEVMETHOD(bus_adjust_resource,		bus_generic_adjust_resource),
1190f84feb9SKornel Duleba 	DEVMETHOD(bus_alloc_resource,		bus_generic_alloc_resource),
120451bcf1bSMarcin Wojtas 
121451bcf1bSMarcin Wojtas 	/* etherswitch interface */
122451bcf1bSMarcin Wojtas 	DEVMETHOD(etherswitch_getinfo,		felix_getinfo),
123451bcf1bSMarcin Wojtas 	DEVMETHOD(etherswitch_getconf,		felix_getconf),
124451bcf1bSMarcin Wojtas 	DEVMETHOD(etherswitch_setconf,		felix_setconf),
125451bcf1bSMarcin Wojtas 	DEVMETHOD(etherswitch_lock,		felix_lock),
126451bcf1bSMarcin Wojtas 	DEVMETHOD(etherswitch_unlock,		felix_unlock),
127451bcf1bSMarcin Wojtas 	DEVMETHOD(etherswitch_getport,		felix_getport),
128451bcf1bSMarcin Wojtas 	DEVMETHOD(etherswitch_setport,		felix_setport),
129451bcf1bSMarcin Wojtas 	DEVMETHOD(etherswitch_readreg,		felix_readreg_wrapper),
130451bcf1bSMarcin Wojtas 	DEVMETHOD(etherswitch_writereg,		felix_writereg_wrapper),
131451bcf1bSMarcin Wojtas 	DEVMETHOD(etherswitch_readphyreg,	felix_readphy),
132451bcf1bSMarcin Wojtas 	DEVMETHOD(etherswitch_writephyreg,	felix_writephy),
133451bcf1bSMarcin Wojtas 	DEVMETHOD(etherswitch_setvgroup,	felix_setvgroup),
134451bcf1bSMarcin Wojtas 	DEVMETHOD(etherswitch_getvgroup,	felix_getvgroup),
135451bcf1bSMarcin Wojtas 
13629cf6a79SKornel Duleba 	/* miibus interface */
13729cf6a79SKornel Duleba 	DEVMETHOD(miibus_readreg,		felix_readphy),
13829cf6a79SKornel Duleba 	DEVMETHOD(miibus_writereg,		felix_writephy),
13929cf6a79SKornel Duleba 
140451bcf1bSMarcin Wojtas 	DEVMETHOD_END
141451bcf1bSMarcin Wojtas };
142451bcf1bSMarcin Wojtas 
143451bcf1bSMarcin Wojtas DEFINE_CLASS_0(felix, felix_driver, felix_methods,
144451bcf1bSMarcin Wojtas     sizeof(struct felix_softc));
145451bcf1bSMarcin Wojtas 
14642726c2fSJohn Baldwin DRIVER_MODULE_ORDERED(felix, pci, felix_driver, NULL, NULL, SI_ORDER_ANY);
1473e38757dSJohn Baldwin DRIVER_MODULE(miibus, felix, miibus_fdt_driver, NULL, NULL);
148829a13faSJohn Baldwin DRIVER_MODULE(etherswitch, felix, etherswitch_driver, NULL, NULL);
149451bcf1bSMarcin Wojtas MODULE_VERSION(felix, 1);
150451bcf1bSMarcin Wojtas MODULE_PNP_INFO("U16:vendor;U16:device;D:#", pci, felix,
151451bcf1bSMarcin Wojtas     felix_pci_ids, nitems(felix_pci_ids) - 1);
152451bcf1bSMarcin Wojtas 
153451bcf1bSMarcin Wojtas static int
154451bcf1bSMarcin Wojtas felix_probe(device_t dev)
155451bcf1bSMarcin Wojtas {
156451bcf1bSMarcin Wojtas 	struct felix_pci_id *id;
157451bcf1bSMarcin Wojtas 	felix_softc_t sc;
158451bcf1bSMarcin Wojtas 
159451bcf1bSMarcin Wojtas 	sc = device_get_softc(dev);
160451bcf1bSMarcin Wojtas 	sc->dev = dev;
161451bcf1bSMarcin Wojtas 
162451bcf1bSMarcin Wojtas 	for (id = felix_pci_ids; id->vendor != 0; ++id) {
163451bcf1bSMarcin Wojtas 		if (pci_get_device(dev) != id->device ||
164451bcf1bSMarcin Wojtas 		    pci_get_vendor(dev) != id->vendor)
165451bcf1bSMarcin Wojtas 			continue;
166451bcf1bSMarcin Wojtas 
167451bcf1bSMarcin Wojtas 		device_set_desc(dev, id->desc);
168451bcf1bSMarcin Wojtas 
169451bcf1bSMarcin Wojtas 		return (BUS_PROBE_DEFAULT);
170451bcf1bSMarcin Wojtas 	}
171451bcf1bSMarcin Wojtas 
172451bcf1bSMarcin Wojtas 	return (ENXIO);
173451bcf1bSMarcin Wojtas }
174451bcf1bSMarcin Wojtas 
175451bcf1bSMarcin Wojtas static int
176451bcf1bSMarcin Wojtas felix_parse_port_fdt(felix_softc_t sc, phandle_t child, int *pport)
177451bcf1bSMarcin Wojtas {
178451bcf1bSMarcin Wojtas 	uint32_t port, status;
179451bcf1bSMarcin Wojtas 	phandle_t node;
180451bcf1bSMarcin Wojtas 
181451bcf1bSMarcin Wojtas 	if (OF_getencprop(child, "reg", (void *)&port, sizeof(port)) < 0) {
182451bcf1bSMarcin Wojtas 		device_printf(sc->dev, "Port node doesn't have reg property\n");
183451bcf1bSMarcin Wojtas 		return (ENXIO);
184451bcf1bSMarcin Wojtas 	}
185451bcf1bSMarcin Wojtas 
186451bcf1bSMarcin Wojtas 	*pport = port;
187451bcf1bSMarcin Wojtas 
188451bcf1bSMarcin Wojtas 	node = OF_getproplen(child, "ethernet");
189451bcf1bSMarcin Wojtas 	if (node <= 0)
190451bcf1bSMarcin Wojtas 		sc->ports[port].cpu_port = false;
191451bcf1bSMarcin Wojtas 	else
192451bcf1bSMarcin Wojtas 		sc->ports[port].cpu_port = true;
193451bcf1bSMarcin Wojtas 
194451bcf1bSMarcin Wojtas 	node = ofw_bus_find_child(child, "fixed-link");
195451bcf1bSMarcin Wojtas 	if (node <= 0) {
196451bcf1bSMarcin Wojtas 		sc->ports[port].fixed_port = false;
197451bcf1bSMarcin Wojtas 		return (0);
198451bcf1bSMarcin Wojtas 	}
199451bcf1bSMarcin Wojtas 
20021cc0918SElliott Mitchell 	sc->ports[port].fixed_port = true;
201451bcf1bSMarcin Wojtas 
202451bcf1bSMarcin Wojtas 	if (OF_getencprop(node, "speed", &status, sizeof(status)) <= 0) {
203451bcf1bSMarcin Wojtas 		device_printf(sc->dev,
204451bcf1bSMarcin Wojtas 		    "Port has fixed-link node without link speed specified\n");
205451bcf1bSMarcin Wojtas 		return (ENXIO);
206451bcf1bSMarcin Wojtas         }
207451bcf1bSMarcin Wojtas 
208451bcf1bSMarcin Wojtas 	switch (status) {
209451bcf1bSMarcin Wojtas 	case 2500:
210451bcf1bSMarcin Wojtas 		status = IFM_2500_T;
211451bcf1bSMarcin Wojtas 		break;
212451bcf1bSMarcin Wojtas 	case 1000:
213451bcf1bSMarcin Wojtas 		status = IFM_1000_T;
214451bcf1bSMarcin Wojtas 		break;
215451bcf1bSMarcin Wojtas 	case 100:
216451bcf1bSMarcin Wojtas 		status = IFM_100_T;
217451bcf1bSMarcin Wojtas 		break;
218451bcf1bSMarcin Wojtas 	case 10:
219451bcf1bSMarcin Wojtas 		status = IFM_10_T;
220451bcf1bSMarcin Wojtas 		break;
221451bcf1bSMarcin Wojtas 	default:
222451bcf1bSMarcin Wojtas 		device_printf(sc->dev,
223451bcf1bSMarcin Wojtas 		    "Unsupported link speed value of %d\n",
224451bcf1bSMarcin Wojtas 		    status);
225451bcf1bSMarcin Wojtas 		return (ENXIO);
226451bcf1bSMarcin Wojtas 	}
227451bcf1bSMarcin Wojtas 
228451bcf1bSMarcin Wojtas 	if (OF_hasprop(node, "full-duplex"))
229451bcf1bSMarcin Wojtas 		status |= IFM_FDX;
230451bcf1bSMarcin Wojtas 	else
231451bcf1bSMarcin Wojtas 		status |= IFM_HDX;
232451bcf1bSMarcin Wojtas 
233451bcf1bSMarcin Wojtas 	status |= IFM_ETHER;
234451bcf1bSMarcin Wojtas 	sc->ports[port].fixed_link_status = status;
235451bcf1bSMarcin Wojtas 	return (0);
236451bcf1bSMarcin Wojtas }
237451bcf1bSMarcin Wojtas 
238451bcf1bSMarcin Wojtas static int
239451bcf1bSMarcin Wojtas felix_init_interface(felix_softc_t sc, int port)
240451bcf1bSMarcin Wojtas {
241451bcf1bSMarcin Wojtas 	char name[IFNAMSIZ];
242451bcf1bSMarcin Wojtas 
243451bcf1bSMarcin Wojtas 	snprintf(name, IFNAMSIZ, "%sport", device_get_nameunit(sc->dev));
244451bcf1bSMarcin Wojtas 
245451bcf1bSMarcin Wojtas 	sc->ports[port].ifp = if_alloc(IFT_ETHER);
246451bcf1bSMarcin Wojtas 	if (sc->ports[port].ifp == NULL)
247451bcf1bSMarcin Wojtas 		return (ENOMEM);
248451bcf1bSMarcin Wojtas 
2492e6a8c1aSJustin Hibbits 	if_setsoftc(sc->ports[port].ifp, sc);
2502e6a8c1aSJustin Hibbits 	if_setflags(sc->ports[port].ifp, IFF_UP | IFF_BROADCAST | IFF_MULTICAST |
2512e6a8c1aSJustin Hibbits 	    IFF_DRV_RUNNING | IFF_SIMPLEX);
252451bcf1bSMarcin Wojtas 	sc->ports[port].ifname = malloc(strlen(name) + 1, M_FELIX, M_NOWAIT);
253451bcf1bSMarcin Wojtas 	if (sc->ports[port].ifname == NULL) {
254451bcf1bSMarcin Wojtas 		if_free(sc->ports[port].ifp);
255451bcf1bSMarcin Wojtas 		return (ENOMEM);
256451bcf1bSMarcin Wojtas 	}
257451bcf1bSMarcin Wojtas 
258451bcf1bSMarcin Wojtas 	memcpy(sc->ports[port].ifname, name, strlen(name) + 1);
259451bcf1bSMarcin Wojtas 	if_initname(sc->ports[port].ifp, sc->ports[port].ifname, port);
260451bcf1bSMarcin Wojtas 	return (0);
261451bcf1bSMarcin Wojtas }
262451bcf1bSMarcin Wojtas 
263451bcf1bSMarcin Wojtas static void
264451bcf1bSMarcin Wojtas felix_setup_port(felix_softc_t sc, int port)
265451bcf1bSMarcin Wojtas {
266451bcf1bSMarcin Wojtas 
267451bcf1bSMarcin Wojtas 	/* Link speed has to be always set to 1000 in the clock register. */
268451bcf1bSMarcin Wojtas 	FELIX_DEVGMII_PORT_WR4(sc, port, FELIX_DEVGMII_CLK_CFG,
269451bcf1bSMarcin Wojtas 	    FELIX_DEVGMII_CLK_CFG_SPEED_1000);
270451bcf1bSMarcin Wojtas 	FELIX_DEVGMII_PORT_WR4(sc, port, FELIX_DEVGMII_MAC_CFG,
271451bcf1bSMarcin Wojtas 	    FELIX_DEVGMII_MAC_CFG_TX_ENA | FELIX_DEVGMII_MAC_CFG_RX_ENA);
272451bcf1bSMarcin Wojtas 	FELIX_WR4(sc, FELIX_QSYS_PORT_MODE(port),
273451bcf1bSMarcin Wojtas 	    FELIX_QSYS_PORT_MODE_PORT_ENA);
274451bcf1bSMarcin Wojtas 
275451bcf1bSMarcin Wojtas 	/*
276451bcf1bSMarcin Wojtas 	 * Enable "VLANMTU". Each port has a configurable MTU.
277451bcf1bSMarcin Wojtas 	 * Accept frames that are 8 and 4 bytes longer than it
278451bcf1bSMarcin Wojtas 	 * for double and single tagged frames respectively.
279451bcf1bSMarcin Wojtas 	 * Since etherswitch API doesn't provide an option to change
280451bcf1bSMarcin Wojtas 	 * MTU don't touch it for now.
281451bcf1bSMarcin Wojtas 	 */
282451bcf1bSMarcin Wojtas 	FELIX_DEVGMII_PORT_WR4(sc, port, FELIX_DEVGMII_VLAN_CFG,
283451bcf1bSMarcin Wojtas 	    FELIX_DEVGMII_VLAN_CFG_ENA |
284451bcf1bSMarcin Wojtas 	    FELIX_DEVGMII_VLAN_CFG_LEN_ENA |
285451bcf1bSMarcin Wojtas 	    FELIX_DEVGMII_VLAN_CFG_DOUBLE_ENA);
286451bcf1bSMarcin Wojtas }
287451bcf1bSMarcin Wojtas 
288451bcf1bSMarcin Wojtas static int
289451bcf1bSMarcin Wojtas felix_setup(felix_softc_t sc)
290451bcf1bSMarcin Wojtas {
291451bcf1bSMarcin Wojtas 	int timeout, i;
292451bcf1bSMarcin Wojtas 	uint32_t reg;
293451bcf1bSMarcin Wojtas 
294451bcf1bSMarcin Wojtas 	/* Trigger soft reset, bit is self-clearing, with 5s timeout. */
295451bcf1bSMarcin Wojtas 	FELIX_WR4(sc, FELIX_DEVCPU_GCB_RST, FELIX_DEVCPU_GCB_RST_EN);
296451bcf1bSMarcin Wojtas 	timeout = FELIX_INIT_TIMEOUT;
297451bcf1bSMarcin Wojtas 	do {
298451bcf1bSMarcin Wojtas 		DELAY(1000);
299451bcf1bSMarcin Wojtas 		reg = FELIX_RD4(sc, FELIX_DEVCPU_GCB_RST);
300451bcf1bSMarcin Wojtas 		if ((reg & FELIX_DEVCPU_GCB_RST_EN) == 0)
301451bcf1bSMarcin Wojtas 			break;
302451bcf1bSMarcin Wojtas 	} while (timeout-- > 0);
303451bcf1bSMarcin Wojtas 	if (timeout == 0) {
304451bcf1bSMarcin Wojtas 		device_printf(sc->dev,
305451bcf1bSMarcin Wojtas 		    "Timeout while waiting for switch to reset\n");
306451bcf1bSMarcin Wojtas 		return (ETIMEDOUT);
307451bcf1bSMarcin Wojtas 	}
308451bcf1bSMarcin Wojtas 
309451bcf1bSMarcin Wojtas 	FELIX_WR4(sc, FELIX_SYS_RAM_CTRL, FELIX_SYS_RAM_CTRL_INIT);
310451bcf1bSMarcin Wojtas 	timeout = FELIX_INIT_TIMEOUT;
311451bcf1bSMarcin Wojtas 	do {
312451bcf1bSMarcin Wojtas 		DELAY(1000);
313451bcf1bSMarcin Wojtas 		reg = FELIX_RD4(sc, FELIX_SYS_RAM_CTRL);
314451bcf1bSMarcin Wojtas 		if ((reg & FELIX_SYS_RAM_CTRL_INIT) == 0)
315451bcf1bSMarcin Wojtas 			break;
316451bcf1bSMarcin Wojtas 	} while (timeout-- > 0);
317451bcf1bSMarcin Wojtas 	if (timeout == 0) {
318451bcf1bSMarcin Wojtas 		device_printf(sc->dev,
319451bcf1bSMarcin Wojtas 		    "Timeout while waiting for switch RAM init.\n");
320451bcf1bSMarcin Wojtas 		return (ETIMEDOUT);
321451bcf1bSMarcin Wojtas 	}
322451bcf1bSMarcin Wojtas 
323451bcf1bSMarcin Wojtas 	FELIX_WR4(sc, FELIX_SYS_CFG, FELIX_SYS_CFG_CORE_EN);
324451bcf1bSMarcin Wojtas 
325451bcf1bSMarcin Wojtas 	for (i = 0; i < sc->info.es_nports; i++)
326451bcf1bSMarcin Wojtas 		felix_setup_port(sc, i);
327451bcf1bSMarcin Wojtas 
328451bcf1bSMarcin Wojtas 	return (0);
329451bcf1bSMarcin Wojtas }
330451bcf1bSMarcin Wojtas 
331451bcf1bSMarcin Wojtas static int
332d88aecceSKornel Duleba felix_timer_rate(SYSCTL_HANDLER_ARGS)
333d88aecceSKornel Duleba {
334d88aecceSKornel Duleba 	felix_softc_t sc;
335d88aecceSKornel Duleba 	int error, value, old;
336d88aecceSKornel Duleba 
337d88aecceSKornel Duleba 	sc = arg1;
338d88aecceSKornel Duleba 
339d88aecceSKornel Duleba 	old = value = sc->timer_ticks;
340d88aecceSKornel Duleba 	error = sysctl_handle_int(oidp, &value, 0, req);
341d88aecceSKornel Duleba 	if (error != 0 || req->newptr == NULL)
342d88aecceSKornel Duleba 		return (error);
343d88aecceSKornel Duleba 
344d88aecceSKornel Duleba 	if (value < 0)
345d88aecceSKornel Duleba 		return (EINVAL);
346d88aecceSKornel Duleba 
347d88aecceSKornel Duleba 	if (value == old)
348d88aecceSKornel Duleba 		return (0);
349d88aecceSKornel Duleba 
350d88aecceSKornel Duleba 	FELIX_LOCK(sc);
351d88aecceSKornel Duleba 	sc->timer_ticks = value;
352d88aecceSKornel Duleba 	callout_reset(&sc->tick_callout, sc->timer_ticks, felix_tick, sc);
353d88aecceSKornel Duleba 	FELIX_UNLOCK(sc);
354d88aecceSKornel Duleba 
355d88aecceSKornel Duleba 	return (0);
356d88aecceSKornel Duleba }
357d88aecceSKornel Duleba 
358d88aecceSKornel Duleba static int
359451bcf1bSMarcin Wojtas felix_attach(device_t dev)
360451bcf1bSMarcin Wojtas {
361451bcf1bSMarcin Wojtas 	phandle_t child, ports, node;
362451bcf1bSMarcin Wojtas 	int error, port, rid;
363451bcf1bSMarcin Wojtas 	felix_softc_t sc;
364451bcf1bSMarcin Wojtas 	uint32_t phy_addr;
365451bcf1bSMarcin Wojtas 	ssize_t size;
366451bcf1bSMarcin Wojtas 
367451bcf1bSMarcin Wojtas 	sc = device_get_softc(dev);
368451bcf1bSMarcin Wojtas 	sc->info.es_nports = 0;
369451bcf1bSMarcin Wojtas 	sc->info.es_vlan_caps = ETHERSWITCH_VLAN_DOT1Q;
370451bcf1bSMarcin Wojtas 	strlcpy(sc->info.es_name, "Felix TSN Switch", sizeof(sc->info.es_name));
371451bcf1bSMarcin Wojtas 
37229cf6a79SKornel Duleba 	rid = PCIR_BAR(FELIX_BAR_MDIO);
37329cf6a79SKornel Duleba 	sc->mdio = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
37429cf6a79SKornel Duleba 	    RF_ACTIVE);
37529cf6a79SKornel Duleba 	if (sc->mdio == NULL) {
37629cf6a79SKornel Duleba 		device_printf(dev, "Failed to allocate MDIO registers.\n");
37729cf6a79SKornel Duleba 		return (ENXIO);
37829cf6a79SKornel Duleba 	}
37929cf6a79SKornel Duleba 
380451bcf1bSMarcin Wojtas 	rid = PCIR_BAR(FELIX_BAR_REGS);
381451bcf1bSMarcin Wojtas 	sc->regs = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
382451bcf1bSMarcin Wojtas 	    RF_ACTIVE);
383451bcf1bSMarcin Wojtas 	if (sc->regs == NULL) {
384451bcf1bSMarcin Wojtas 		device_printf(dev, "Failed to allocate registers BAR.\n");
38529cf6a79SKornel Duleba 		error = ENXIO;
38629cf6a79SKornel Duleba 		goto out_fail;
387451bcf1bSMarcin Wojtas 	}
388451bcf1bSMarcin Wojtas 
389451bcf1bSMarcin Wojtas 	mtx_init(&sc->mtx, "felix lock",  NULL, MTX_DEF);
390451bcf1bSMarcin Wojtas 	callout_init_mtx(&sc->tick_callout, &sc->mtx, 0);
391451bcf1bSMarcin Wojtas 
392451bcf1bSMarcin Wojtas 	node = ofw_bus_get_node(dev);
393451bcf1bSMarcin Wojtas 	if (node <= 0) {
394451bcf1bSMarcin Wojtas 		error = ENXIO;
395451bcf1bSMarcin Wojtas 		goto out_fail;
396451bcf1bSMarcin Wojtas 	}
397451bcf1bSMarcin Wojtas 
398451bcf1bSMarcin Wojtas 	ports = ofw_bus_find_child(node, "ports");
399451bcf1bSMarcin Wojtas 	if (ports == 0) {
400451bcf1bSMarcin Wojtas 		device_printf(dev,
401451bcf1bSMarcin Wojtas 		    "Failed to find \"ports\" property in DTS.\n");
402451bcf1bSMarcin Wojtas 		error = ENXIO;
403451bcf1bSMarcin Wojtas 		goto out_fail;
404451bcf1bSMarcin Wojtas 	}
405451bcf1bSMarcin Wojtas 
406451bcf1bSMarcin Wojtas 	for (child = OF_child(ports); child != 0; child = OF_peer(child)) {
407451bcf1bSMarcin Wojtas 		/* Do not parse disabled ports. */
408451bcf1bSMarcin Wojtas 		if (ofw_bus_node_status_okay(child) == 0)
409451bcf1bSMarcin Wojtas 			continue;
410451bcf1bSMarcin Wojtas 
411451bcf1bSMarcin Wojtas 		error = felix_parse_port_fdt(sc, child, &port);
412451bcf1bSMarcin Wojtas 		if (error != 0)
413451bcf1bSMarcin Wojtas 			goto out_fail;
414451bcf1bSMarcin Wojtas 
415451bcf1bSMarcin Wojtas 		error = felix_init_interface(sc, port);
416451bcf1bSMarcin Wojtas 		if (error != 0) {
417451bcf1bSMarcin Wojtas 			device_printf(sc->dev,
418451bcf1bSMarcin Wojtas 			    "Failed to initialize interface.\n");
419451bcf1bSMarcin Wojtas 			goto out_fail;
420451bcf1bSMarcin Wojtas 		}
421451bcf1bSMarcin Wojtas 
422451bcf1bSMarcin Wojtas 		if (sc->ports[port].fixed_port) {
423451bcf1bSMarcin Wojtas 			sc->info.es_nports++;
424451bcf1bSMarcin Wojtas 			continue;
425451bcf1bSMarcin Wojtas 		}
426451bcf1bSMarcin Wojtas 
427451bcf1bSMarcin Wojtas 		size = OF_getencprop(child, "phy-handle", &node, sizeof(node));
428451bcf1bSMarcin Wojtas 		if (size <= 0) {
429451bcf1bSMarcin Wojtas 			device_printf(sc->dev,
430451bcf1bSMarcin Wojtas 			    "Failed to acquire PHY handle from FDT.\n");
431451bcf1bSMarcin Wojtas 			error = ENXIO;
432451bcf1bSMarcin Wojtas 			goto out_fail;
433451bcf1bSMarcin Wojtas 		}
434451bcf1bSMarcin Wojtas 
435451bcf1bSMarcin Wojtas 		node = OF_node_from_xref(node);
436451bcf1bSMarcin Wojtas 		size = OF_getencprop(node, "reg", &phy_addr, sizeof(phy_addr));
437451bcf1bSMarcin Wojtas 		if (size <= 0) {
438451bcf1bSMarcin Wojtas 			device_printf(sc->dev,
439451bcf1bSMarcin Wojtas 			    "Failed to obtain PHY address.\n");
440451bcf1bSMarcin Wojtas 			error = ENXIO;
441451bcf1bSMarcin Wojtas 			goto out_fail;
442451bcf1bSMarcin Wojtas 		}
443451bcf1bSMarcin Wojtas 
444451bcf1bSMarcin Wojtas 		sc->ports[port].phyaddr = phy_addr;
445451bcf1bSMarcin Wojtas 		sc->ports[port].miibus = NULL;
44629cf6a79SKornel Duleba 		error = mii_attach(dev, &sc->ports[port].miibus, sc->ports[port].ifp,
447451bcf1bSMarcin Wojtas 		    felix_ifmedia_upd, felix_ifmedia_sts, BMSR_DEFCAPMASK,
448451bcf1bSMarcin Wojtas 		    phy_addr, MII_OFFSET_ANY, 0);
449451bcf1bSMarcin Wojtas 		if (error != 0)
450451bcf1bSMarcin Wojtas 			goto out_fail;
451451bcf1bSMarcin Wojtas 
452451bcf1bSMarcin Wojtas 		sc->info.es_nports++;
453451bcf1bSMarcin Wojtas 	}
454451bcf1bSMarcin Wojtas 
455451bcf1bSMarcin Wojtas 	error = felix_setup(sc);
456451bcf1bSMarcin Wojtas 	if (error != 0)
457451bcf1bSMarcin Wojtas 		goto out_fail;
458451bcf1bSMarcin Wojtas 
459d88aecceSKornel Duleba 	sc->timer_ticks = hz;	/* Default to 1s. */
460d88aecceSKornel Duleba 	SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
461d88aecceSKornel Duleba 	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
462d88aecceSKornel Duleba 	    OID_AUTO, "timer_ticks", CTLTYPE_INT | CTLFLAG_RW,
463d88aecceSKornel Duleba 	    sc, 0, felix_timer_rate, "I",
464d88aecceSKornel Duleba 	    "Number of ticks between timer invocations");
465d88aecceSKornel Duleba 
466451bcf1bSMarcin Wojtas 	/* The tick routine has to be called with the lock held. */
467451bcf1bSMarcin Wojtas 	FELIX_LOCK(sc);
468451bcf1bSMarcin Wojtas 	felix_tick(sc);
469451bcf1bSMarcin Wojtas 	FELIX_UNLOCK(sc);
470451bcf1bSMarcin Wojtas 
471451bcf1bSMarcin Wojtas 	/* Allow etherswitch to attach as our child. */
472451bcf1bSMarcin Wojtas 	bus_generic_probe(dev);
473451bcf1bSMarcin Wojtas 	bus_generic_attach(dev);
474451bcf1bSMarcin Wojtas 
475451bcf1bSMarcin Wojtas 	return (0);
476451bcf1bSMarcin Wojtas 
477451bcf1bSMarcin Wojtas out_fail:
478451bcf1bSMarcin Wojtas 	felix_detach(dev);
479451bcf1bSMarcin Wojtas 	return (error);
480451bcf1bSMarcin Wojtas }
481451bcf1bSMarcin Wojtas 
482451bcf1bSMarcin Wojtas static int
483451bcf1bSMarcin Wojtas felix_detach(device_t dev)
484451bcf1bSMarcin Wojtas {
485451bcf1bSMarcin Wojtas 	felix_softc_t sc;
486451bcf1bSMarcin Wojtas 	int error;
487451bcf1bSMarcin Wojtas 	int i;
488451bcf1bSMarcin Wojtas 
489451bcf1bSMarcin Wojtas 	error = 0;
490451bcf1bSMarcin Wojtas 	sc = device_get_softc(dev);
491451bcf1bSMarcin Wojtas 	bus_generic_detach(dev);
492451bcf1bSMarcin Wojtas 
493451bcf1bSMarcin Wojtas 	mtx_lock(&sc->mtx);
494451bcf1bSMarcin Wojtas 	callout_stop(&sc->tick_callout);
495451bcf1bSMarcin Wojtas 	mtx_unlock(&sc->mtx);
496451bcf1bSMarcin Wojtas 	mtx_destroy(&sc->mtx);
497451bcf1bSMarcin Wojtas 
498451bcf1bSMarcin Wojtas 	/*
499451bcf1bSMarcin Wojtas 	 * If we have been fully attached do a soft reset.
500451bcf1bSMarcin Wojtas 	 * This way after when driver is unloaded switch is left in unmanaged mode.
501451bcf1bSMarcin Wojtas 	 */
502451bcf1bSMarcin Wojtas 	if (device_is_attached(dev))
503451bcf1bSMarcin Wojtas 		felix_setup(sc);
504451bcf1bSMarcin Wojtas 
505451bcf1bSMarcin Wojtas 	for (i = 0; i < sc->info.es_nports; i++) {
50629cf6a79SKornel Duleba 		if (sc->ports[i].miibus != NULL)
50729cf6a79SKornel Duleba 			device_delete_child(dev, sc->ports[i].miibus);
508451bcf1bSMarcin Wojtas 		if (sc->ports[i].ifp != NULL)
509451bcf1bSMarcin Wojtas 			if_free(sc->ports[i].ifp);
510451bcf1bSMarcin Wojtas 		if (sc->ports[i].ifname != NULL)
511451bcf1bSMarcin Wojtas 			free(sc->ports[i].ifname, M_FELIX);
512451bcf1bSMarcin Wojtas 	}
513451bcf1bSMarcin Wojtas 
514451bcf1bSMarcin Wojtas 	if (sc->regs != NULL)
515451bcf1bSMarcin Wojtas 		error = bus_release_resource(sc->dev, SYS_RES_MEMORY,
516451bcf1bSMarcin Wojtas 		    rman_get_rid(sc->regs), sc->regs);
517451bcf1bSMarcin Wojtas 
51829cf6a79SKornel Duleba 	if (sc->mdio != NULL)
51929cf6a79SKornel Duleba 		error = bus_release_resource(sc->dev, SYS_RES_MEMORY,
52029cf6a79SKornel Duleba 		    rman_get_rid(sc->mdio), sc->mdio);
52129cf6a79SKornel Duleba 
522451bcf1bSMarcin Wojtas 	return (error);
523451bcf1bSMarcin Wojtas }
524451bcf1bSMarcin Wojtas 
525451bcf1bSMarcin Wojtas static etherswitch_info_t*
526451bcf1bSMarcin Wojtas felix_getinfo(device_t dev)
527451bcf1bSMarcin Wojtas {
528451bcf1bSMarcin Wojtas 	felix_softc_t sc;
529451bcf1bSMarcin Wojtas 
530451bcf1bSMarcin Wojtas 	sc = device_get_softc(dev);
531451bcf1bSMarcin Wojtas 	return (&sc->info);
532451bcf1bSMarcin Wojtas }
533451bcf1bSMarcin Wojtas 
534451bcf1bSMarcin Wojtas static int
535451bcf1bSMarcin Wojtas felix_getconf(device_t dev, etherswitch_conf_t *conf)
536451bcf1bSMarcin Wojtas {
537451bcf1bSMarcin Wojtas 	felix_softc_t sc;
538451bcf1bSMarcin Wojtas 
539451bcf1bSMarcin Wojtas 	sc = device_get_softc(dev);
540451bcf1bSMarcin Wojtas 
541451bcf1bSMarcin Wojtas 	/* Return the VLAN mode. */
542451bcf1bSMarcin Wojtas 	conf->cmd = ETHERSWITCH_CONF_VLAN_MODE;
543451bcf1bSMarcin Wojtas 	conf->vlan_mode = sc->vlan_mode;
544451bcf1bSMarcin Wojtas 	return (0);
545451bcf1bSMarcin Wojtas }
546451bcf1bSMarcin Wojtas 
547451bcf1bSMarcin Wojtas static int
548451bcf1bSMarcin Wojtas felix_init_vlan(felix_softc_t sc)
549451bcf1bSMarcin Wojtas {
550451bcf1bSMarcin Wojtas 	int timeout = FELIX_INIT_TIMEOUT;
551451bcf1bSMarcin Wojtas 	uint32_t reg;
552451bcf1bSMarcin Wojtas 	int i;
553451bcf1bSMarcin Wojtas 
554451bcf1bSMarcin Wojtas 	/* Flush VLAN table in hardware. */
555451bcf1bSMarcin Wojtas 	FELIX_WR4(sc, FELIX_ANA_VT, FELIX_ANA_VT_RESET);
556451bcf1bSMarcin Wojtas 	do {
557451bcf1bSMarcin Wojtas 		DELAY(1000);
558451bcf1bSMarcin Wojtas 		reg = FELIX_RD4(sc, FELIX_ANA_VT);
559451bcf1bSMarcin Wojtas 		if ((reg & FELIX_ANA_VT_STS) == FELIX_ANA_VT_IDLE)
560451bcf1bSMarcin Wojtas 			break;
561451bcf1bSMarcin Wojtas 	} while (timeout-- > 0);
562451bcf1bSMarcin Wojtas 	if (timeout == 0) {
563451bcf1bSMarcin Wojtas 		device_printf(sc->dev,
564451bcf1bSMarcin Wojtas 		    "Timeout during VLAN table reset.\n");
565451bcf1bSMarcin Wojtas 		return (ETIMEDOUT);
566451bcf1bSMarcin Wojtas 	}
567451bcf1bSMarcin Wojtas 
568451bcf1bSMarcin Wojtas 	/* Flush VLAN table in sc. */
569451bcf1bSMarcin Wojtas 	for (i = 0; i < sc->info.es_nvlangroups; i++)
570451bcf1bSMarcin Wojtas 		sc->vlans[i] = 0;
571451bcf1bSMarcin Wojtas 
572451bcf1bSMarcin Wojtas 	/*
573451bcf1bSMarcin Wojtas 	 * Make all ports VLAN aware.
574451bcf1bSMarcin Wojtas 	 * Read VID from incoming frames and use it for port grouping
575451bcf1bSMarcin Wojtas 	 * purposes.
576451bcf1bSMarcin Wojtas 	 * Don't set this if pvid is set.
577451bcf1bSMarcin Wojtas 	 */
578451bcf1bSMarcin Wojtas 	for (i = 0; i < sc->info.es_nports; i++) {
579451bcf1bSMarcin Wojtas 		reg = FELIX_ANA_PORT_RD4(sc, i, FELIX_ANA_PORT_VLAN_CFG);
580451bcf1bSMarcin Wojtas 		if ((reg & FELIX_ANA_PORT_VLAN_CFG_VID_MASK) != 0)
581451bcf1bSMarcin Wojtas 			continue;
582451bcf1bSMarcin Wojtas 
583451bcf1bSMarcin Wojtas 		reg |= FELIX_ANA_PORT_VLAN_CFG_VID_AWARE;
584451bcf1bSMarcin Wojtas 		FELIX_ANA_PORT_WR4(sc, i, FELIX_ANA_PORT_VLAN_CFG, reg);
585451bcf1bSMarcin Wojtas 	}
586451bcf1bSMarcin Wojtas 	return (0);
587451bcf1bSMarcin Wojtas }
588451bcf1bSMarcin Wojtas 
589451bcf1bSMarcin Wojtas static int
590451bcf1bSMarcin Wojtas felix_setconf(device_t dev, etherswitch_conf_t *conf)
591451bcf1bSMarcin Wojtas {
592451bcf1bSMarcin Wojtas 	felix_softc_t sc;
593451bcf1bSMarcin Wojtas 	int error;
594451bcf1bSMarcin Wojtas 
595451bcf1bSMarcin Wojtas 	error = 0;
596451bcf1bSMarcin Wojtas 	/* Set the VLAN mode. */
597451bcf1bSMarcin Wojtas 	sc = device_get_softc(dev);
598451bcf1bSMarcin Wojtas 	FELIX_LOCK(sc);
599451bcf1bSMarcin Wojtas 	if (conf->cmd & ETHERSWITCH_CONF_VLAN_MODE) {
600451bcf1bSMarcin Wojtas 		switch (conf->vlan_mode) {
601451bcf1bSMarcin Wojtas 		case ETHERSWITCH_VLAN_DOT1Q:
602451bcf1bSMarcin Wojtas 			sc->vlan_mode = ETHERSWITCH_VLAN_DOT1Q;
603451bcf1bSMarcin Wojtas 			sc->info.es_nvlangroups = FELIX_NUM_VLANS;
604451bcf1bSMarcin Wojtas 			error = felix_init_vlan(sc);
605451bcf1bSMarcin Wojtas 			break;
606451bcf1bSMarcin Wojtas 		default:
607451bcf1bSMarcin Wojtas 			error = EINVAL;
608451bcf1bSMarcin Wojtas 		}
609451bcf1bSMarcin Wojtas 	}
610451bcf1bSMarcin Wojtas 	FELIX_UNLOCK(sc);
611451bcf1bSMarcin Wojtas 	return (error);
612451bcf1bSMarcin Wojtas }
613451bcf1bSMarcin Wojtas 
614451bcf1bSMarcin Wojtas static void
615451bcf1bSMarcin Wojtas felix_lock(device_t dev)
616451bcf1bSMarcin Wojtas {
617451bcf1bSMarcin Wojtas 	felix_softc_t sc;
618451bcf1bSMarcin Wojtas 
619451bcf1bSMarcin Wojtas 	sc = device_get_softc(dev);
620451bcf1bSMarcin Wojtas 	FELIX_LOCK_ASSERT(sc, MA_NOTOWNED);
621451bcf1bSMarcin Wojtas 	FELIX_LOCK(sc);
622451bcf1bSMarcin Wojtas }
623451bcf1bSMarcin Wojtas 
624451bcf1bSMarcin Wojtas static void
625451bcf1bSMarcin Wojtas felix_unlock(device_t dev)
626451bcf1bSMarcin Wojtas {
627451bcf1bSMarcin Wojtas 	felix_softc_t sc;
628451bcf1bSMarcin Wojtas 
629451bcf1bSMarcin Wojtas 	sc = device_get_softc(dev);
630451bcf1bSMarcin Wojtas 	FELIX_LOCK_ASSERT(sc, MA_OWNED);
631451bcf1bSMarcin Wojtas 	FELIX_UNLOCK(sc);
632451bcf1bSMarcin Wojtas }
633451bcf1bSMarcin Wojtas 
634451bcf1bSMarcin Wojtas static void
635451bcf1bSMarcin Wojtas felix_get_port_cfg(felix_softc_t sc, etherswitch_port_t *p)
636451bcf1bSMarcin Wojtas {
637451bcf1bSMarcin Wojtas 	uint32_t reg;
638451bcf1bSMarcin Wojtas 
639451bcf1bSMarcin Wojtas 	p->es_flags = 0;
640451bcf1bSMarcin Wojtas 
641451bcf1bSMarcin Wojtas 	reg = FELIX_ANA_PORT_RD4(sc, p->es_port, FELIX_ANA_PORT_DROP_CFG);
642451bcf1bSMarcin Wojtas 	if (reg & FELIX_ANA_PORT_DROP_CFG_TAGGED)
643451bcf1bSMarcin Wojtas 		p->es_flags |= ETHERSWITCH_PORT_DROPTAGGED;
644451bcf1bSMarcin Wojtas 
645451bcf1bSMarcin Wojtas 	if (reg & FELIX_ANA_PORT_DROP_CFG_UNTAGGED)
646451bcf1bSMarcin Wojtas 		p->es_flags |= ETHERSWITCH_PORT_DROPUNTAGGED;
647451bcf1bSMarcin Wojtas 
648451bcf1bSMarcin Wojtas 	reg = FELIX_DEVGMII_PORT_RD4(sc, p->es_port, FELIX_DEVGMII_VLAN_CFG);
649451bcf1bSMarcin Wojtas 	if (reg & FELIX_DEVGMII_VLAN_CFG_DOUBLE_ENA)
650451bcf1bSMarcin Wojtas 		p->es_flags |= ETHERSWITCH_PORT_DOUBLE_TAG;
651451bcf1bSMarcin Wojtas 
652451bcf1bSMarcin Wojtas 	reg = FELIX_REW_PORT_RD4(sc, p->es_port, FELIX_REW_PORT_TAG_CFG);
653451bcf1bSMarcin Wojtas 	if (reg & FELIX_REW_PORT_TAG_CFG_ALL)
654451bcf1bSMarcin Wojtas 		p->es_flags |= ETHERSWITCH_PORT_ADDTAG;
655451bcf1bSMarcin Wojtas 
656451bcf1bSMarcin Wojtas 	reg = FELIX_ANA_PORT_RD4(sc, p->es_port, FELIX_ANA_PORT_VLAN_CFG);
657451bcf1bSMarcin Wojtas 	if (reg & FELIX_ANA_PORT_VLAN_CFG_POP)
658451bcf1bSMarcin Wojtas 		p->es_flags |= ETHERSWITCH_PORT_STRIPTAGINGRESS;
659451bcf1bSMarcin Wojtas 
660451bcf1bSMarcin Wojtas 	p->es_pvid = reg & FELIX_ANA_PORT_VLAN_CFG_VID_MASK;
661451bcf1bSMarcin Wojtas }
662451bcf1bSMarcin Wojtas 
663451bcf1bSMarcin Wojtas static int
664451bcf1bSMarcin Wojtas felix_getport(device_t dev, etherswitch_port_t *p)
665451bcf1bSMarcin Wojtas {
666451bcf1bSMarcin Wojtas 	struct ifmediareq *ifmr;
667451bcf1bSMarcin Wojtas 	struct mii_data *mii;
668451bcf1bSMarcin Wojtas 	felix_softc_t sc;
669451bcf1bSMarcin Wojtas 	int error;
670451bcf1bSMarcin Wojtas 
671451bcf1bSMarcin Wojtas 	error = 0;
672451bcf1bSMarcin Wojtas 	sc = device_get_softc(dev);
673451bcf1bSMarcin Wojtas 	FELIX_LOCK_ASSERT(sc, MA_NOTOWNED);
674451bcf1bSMarcin Wojtas 
675451bcf1bSMarcin Wojtas 	if (p->es_port >= sc->info.es_nports || p->es_port < 0)
676451bcf1bSMarcin Wojtas 		return (EINVAL);
677451bcf1bSMarcin Wojtas 
678451bcf1bSMarcin Wojtas 	FELIX_LOCK(sc);
679451bcf1bSMarcin Wojtas 	felix_get_port_cfg(sc, p);
680451bcf1bSMarcin Wojtas 	if (sc->ports[p->es_port].fixed_port) {
681451bcf1bSMarcin Wojtas 		ifmr = &p->es_ifmr;
682451bcf1bSMarcin Wojtas 		ifmr->ifm_status = IFM_ACTIVE | IFM_AVALID;
683451bcf1bSMarcin Wojtas 		ifmr->ifm_count = 0;
684451bcf1bSMarcin Wojtas 		ifmr->ifm_active = sc->ports[p->es_port].fixed_link_status;
685451bcf1bSMarcin Wojtas 		ifmr->ifm_current = ifmr->ifm_active;
686451bcf1bSMarcin Wojtas 		ifmr->ifm_mask = 0;
687451bcf1bSMarcin Wojtas 	} else {
688451bcf1bSMarcin Wojtas 		mii = felix_miiforport(sc, p->es_port);
689451bcf1bSMarcin Wojtas 		error = ifmedia_ioctl(mii->mii_ifp, &p->es_ifr,
690451bcf1bSMarcin Wojtas 		    &mii->mii_media, SIOCGIFMEDIA);
691451bcf1bSMarcin Wojtas 	}
692451bcf1bSMarcin Wojtas 	FELIX_UNLOCK(sc);
693451bcf1bSMarcin Wojtas 	return (error);
694451bcf1bSMarcin Wojtas }
695451bcf1bSMarcin Wojtas 
696451bcf1bSMarcin Wojtas static void
697451bcf1bSMarcin Wojtas felix_set_port_cfg(felix_softc_t sc, etherswitch_port_t *p)
698451bcf1bSMarcin Wojtas {
699451bcf1bSMarcin Wojtas 	uint32_t reg;
700451bcf1bSMarcin Wojtas 
701451bcf1bSMarcin Wojtas 	reg = FELIX_ANA_PORT_RD4(sc, p->es_port, FELIX_ANA_PORT_DROP_CFG);
702451bcf1bSMarcin Wojtas 	if (p->es_flags & ETHERSWITCH_PORT_DROPTAGGED)
703451bcf1bSMarcin Wojtas 		reg |= FELIX_ANA_PORT_DROP_CFG_TAGGED;
704451bcf1bSMarcin Wojtas 	else
705451bcf1bSMarcin Wojtas 		reg &= ~FELIX_ANA_PORT_DROP_CFG_TAGGED;
706451bcf1bSMarcin Wojtas 
707451bcf1bSMarcin Wojtas 	if (p->es_flags & ETHERSWITCH_PORT_DROPUNTAGGED)
708451bcf1bSMarcin Wojtas 		reg |= FELIX_ANA_PORT_DROP_CFG_UNTAGGED;
709451bcf1bSMarcin Wojtas 	else
710451bcf1bSMarcin Wojtas 		reg &= ~FELIX_ANA_PORT_DROP_CFG_UNTAGGED;
711451bcf1bSMarcin Wojtas 
712451bcf1bSMarcin Wojtas 	FELIX_ANA_PORT_WR4(sc, p->es_port, FELIX_ANA_PORT_DROP_CFG, reg);
713451bcf1bSMarcin Wojtas 
714451bcf1bSMarcin Wojtas 	reg = FELIX_REW_PORT_RD4(sc, p->es_port, FELIX_REW_PORT_TAG_CFG);
715451bcf1bSMarcin Wojtas 	if (p->es_flags & ETHERSWITCH_PORT_ADDTAG)
716451bcf1bSMarcin Wojtas 		reg |= FELIX_REW_PORT_TAG_CFG_ALL;
717451bcf1bSMarcin Wojtas 	else
718451bcf1bSMarcin Wojtas 		reg &= ~FELIX_REW_PORT_TAG_CFG_ALL;
719451bcf1bSMarcin Wojtas 
720451bcf1bSMarcin Wojtas 	FELIX_REW_PORT_WR4(sc, p->es_port, FELIX_REW_PORT_TAG_CFG, reg);
721451bcf1bSMarcin Wojtas 
722451bcf1bSMarcin Wojtas 	reg = FELIX_ANA_PORT_RD4(sc, p->es_port, FELIX_ANA_PORT_VLAN_CFG);
723451bcf1bSMarcin Wojtas 	if (p->es_flags & ETHERSWITCH_PORT_STRIPTAGINGRESS)
724451bcf1bSMarcin Wojtas 		reg |= FELIX_ANA_PORT_VLAN_CFG_POP;
725451bcf1bSMarcin Wojtas 	else
726451bcf1bSMarcin Wojtas 		reg &= ~FELIX_ANA_PORT_VLAN_CFG_POP;
727451bcf1bSMarcin Wojtas 
728451bcf1bSMarcin Wojtas 	reg &= ~FELIX_ANA_PORT_VLAN_CFG_VID_MASK;
729451bcf1bSMarcin Wojtas 	reg |= p->es_pvid & FELIX_ANA_PORT_VLAN_CFG_VID_MASK;
730451bcf1bSMarcin Wojtas 	/*
731451bcf1bSMarcin Wojtas 	 * If port VID is set use it for VLAN classification,
732451bcf1bSMarcin Wojtas 	 * instead of frame VID.
733451bcf1bSMarcin Wojtas 	 * By default the frame tag takes precedence.
734451bcf1bSMarcin Wojtas 	 * Force the switch to ignore it.
735451bcf1bSMarcin Wojtas 	 */
736451bcf1bSMarcin Wojtas 	if (p->es_pvid != 0)
737451bcf1bSMarcin Wojtas 		reg &= ~FELIX_ANA_PORT_VLAN_CFG_VID_AWARE;
738451bcf1bSMarcin Wojtas 	else
739451bcf1bSMarcin Wojtas 		reg |= FELIX_ANA_PORT_VLAN_CFG_VID_AWARE;
740451bcf1bSMarcin Wojtas 
741451bcf1bSMarcin Wojtas 	FELIX_ANA_PORT_WR4(sc, p->es_port, FELIX_ANA_PORT_VLAN_CFG, reg);
742451bcf1bSMarcin Wojtas }
743451bcf1bSMarcin Wojtas 
744451bcf1bSMarcin Wojtas static int
745451bcf1bSMarcin Wojtas felix_setport(device_t dev, etherswitch_port_t *p)
746451bcf1bSMarcin Wojtas {
747451bcf1bSMarcin Wojtas 	felix_softc_t sc;
748451bcf1bSMarcin Wojtas 	struct mii_data *mii;
749451bcf1bSMarcin Wojtas 	int error;
750451bcf1bSMarcin Wojtas 
751451bcf1bSMarcin Wojtas 	error = 0;
752451bcf1bSMarcin Wojtas 	sc = device_get_softc(dev);
753451bcf1bSMarcin Wojtas 	FELIX_LOCK_ASSERT(sc, MA_NOTOWNED);
754451bcf1bSMarcin Wojtas 
755451bcf1bSMarcin Wojtas 	if (p->es_port >= sc->info.es_nports || p->es_port < 0)
756451bcf1bSMarcin Wojtas 		return (EINVAL);
757451bcf1bSMarcin Wojtas 
758451bcf1bSMarcin Wojtas 	FELIX_LOCK(sc);
759451bcf1bSMarcin Wojtas 	felix_set_port_cfg(sc, p);
760451bcf1bSMarcin Wojtas 	if (felix_is_phyport(sc, p->es_port)) {
761451bcf1bSMarcin Wojtas 		mii = felix_miiforport(sc, p->es_port);
762451bcf1bSMarcin Wojtas 		error = ifmedia_ioctl(mii->mii_ifp, &p->es_ifr, &mii->mii_media,
763451bcf1bSMarcin Wojtas 		    SIOCSIFMEDIA);
764451bcf1bSMarcin Wojtas 	}
765451bcf1bSMarcin Wojtas 	FELIX_UNLOCK(sc);
766451bcf1bSMarcin Wojtas 
767451bcf1bSMarcin Wojtas 	return (error);
768451bcf1bSMarcin Wojtas }
769451bcf1bSMarcin Wojtas 
770451bcf1bSMarcin Wojtas static int
771451bcf1bSMarcin Wojtas felix_readreg_wrapper(device_t dev, int addr_reg)
772451bcf1bSMarcin Wojtas {
773451bcf1bSMarcin Wojtas 	felix_softc_t sc;
774451bcf1bSMarcin Wojtas 
775451bcf1bSMarcin Wojtas 	sc = device_get_softc(dev);
776451bcf1bSMarcin Wojtas 	if (addr_reg > rman_get_size(sc->regs))
777451bcf1bSMarcin Wojtas 		return (UINT32_MAX);	/* Can't return errors here. */
778451bcf1bSMarcin Wojtas 
779451bcf1bSMarcin Wojtas 	return (FELIX_RD4(sc, addr_reg));
780451bcf1bSMarcin Wojtas }
781451bcf1bSMarcin Wojtas 
782451bcf1bSMarcin Wojtas static int
783451bcf1bSMarcin Wojtas felix_writereg_wrapper(device_t dev, int addr_reg, int val)
784451bcf1bSMarcin Wojtas {
785451bcf1bSMarcin Wojtas 	felix_softc_t sc;
786451bcf1bSMarcin Wojtas 
787451bcf1bSMarcin Wojtas 	sc = device_get_softc(dev);
788451bcf1bSMarcin Wojtas 	if (addr_reg > rman_get_size(sc->regs))
789451bcf1bSMarcin Wojtas 		return (EINVAL);
790451bcf1bSMarcin Wojtas 
791451bcf1bSMarcin Wojtas 	FELIX_WR4(sc, addr_reg, val);
792451bcf1bSMarcin Wojtas 	return (0);
793451bcf1bSMarcin Wojtas }
794451bcf1bSMarcin Wojtas 
795451bcf1bSMarcin Wojtas static int
796451bcf1bSMarcin Wojtas felix_readphy(device_t dev, int phy, int reg)
797451bcf1bSMarcin Wojtas {
798451bcf1bSMarcin Wojtas 	felix_softc_t sc;
799451bcf1bSMarcin Wojtas 
800451bcf1bSMarcin Wojtas 	sc = device_get_softc(dev);
801451bcf1bSMarcin Wojtas 
80229cf6a79SKornel Duleba 	return (enetc_mdio_read(sc->mdio, FELIX_MDIO_BASE, phy, reg));
803451bcf1bSMarcin Wojtas }
804451bcf1bSMarcin Wojtas 
805451bcf1bSMarcin Wojtas static int
806451bcf1bSMarcin Wojtas felix_writephy(device_t dev, int phy, int reg, int data)
807451bcf1bSMarcin Wojtas {
808451bcf1bSMarcin Wojtas 	felix_softc_t sc;
809451bcf1bSMarcin Wojtas 
810451bcf1bSMarcin Wojtas 	sc = device_get_softc(dev);
811451bcf1bSMarcin Wojtas 
81229cf6a79SKornel Duleba 	return (enetc_mdio_write(sc->mdio, FELIX_MDIO_BASE, phy, reg, data));
813451bcf1bSMarcin Wojtas }
814451bcf1bSMarcin Wojtas 
815451bcf1bSMarcin Wojtas static int
816451bcf1bSMarcin Wojtas felix_set_dot1q_vlan(felix_softc_t sc, etherswitch_vlangroup_t *vg)
817451bcf1bSMarcin Wojtas {
818451bcf1bSMarcin Wojtas 	uint32_t reg;
819451bcf1bSMarcin Wojtas 	int i, vid;
820451bcf1bSMarcin Wojtas 
821451bcf1bSMarcin Wojtas 	vid = vg->es_vid & ETHERSWITCH_VID_MASK;
822451bcf1bSMarcin Wojtas 
823451bcf1bSMarcin Wojtas 	/* Tagged mode is not supported. */
824451bcf1bSMarcin Wojtas 	if (vg->es_member_ports != vg->es_untagged_ports)
825451bcf1bSMarcin Wojtas 		return (EINVAL);
826451bcf1bSMarcin Wojtas 
827451bcf1bSMarcin Wojtas 	/*
828451bcf1bSMarcin Wojtas 	 * Hardware support 4096 groups, but we can't do group_id == vid.
829451bcf1bSMarcin Wojtas 	 * Note that hw_group_id == vid.
830451bcf1bSMarcin Wojtas 	 */
831451bcf1bSMarcin Wojtas 	if (vid == 0) {
832451bcf1bSMarcin Wojtas 		/* Clear VLAN table entry using old VID. */
833451bcf1bSMarcin Wojtas 		FELIX_WR4(sc, FELIX_ANA_VTIDX, sc->vlans[vg->es_vlangroup]);
834451bcf1bSMarcin Wojtas 		FELIX_WR4(sc, FELIX_ANA_VT, FELIX_ANA_VT_WRITE);
835451bcf1bSMarcin Wojtas 		sc->vlans[vg->es_vlangroup] = 0;
836451bcf1bSMarcin Wojtas 		return (0);
837451bcf1bSMarcin Wojtas 	}
838451bcf1bSMarcin Wojtas 
839451bcf1bSMarcin Wojtas 	/* The VID is already used in a different group. */
840451bcf1bSMarcin Wojtas 	for (i = 0; i < sc->info.es_nvlangroups; i++)
841451bcf1bSMarcin Wojtas 		if (i != vg->es_vlangroup && vid == sc->vlans[i])
842451bcf1bSMarcin Wojtas 			return (EINVAL);
843451bcf1bSMarcin Wojtas 
844451bcf1bSMarcin Wojtas 	/* This group already uses a different VID. */
845451bcf1bSMarcin Wojtas 	if (sc->vlans[vg->es_vlangroup] != 0 &&
846451bcf1bSMarcin Wojtas 	    sc->vlans[vg->es_vlangroup] != vid)
847451bcf1bSMarcin Wojtas 		return (EINVAL);
848451bcf1bSMarcin Wojtas 
849451bcf1bSMarcin Wojtas 	sc->vlans[vg->es_vlangroup] = vid;
850451bcf1bSMarcin Wojtas 
851451bcf1bSMarcin Wojtas 	/* Assign members to the given group. */
852451bcf1bSMarcin Wojtas 	reg = vg->es_member_ports & FELIX_ANA_VT_PORTMASK_MASK;
853451bcf1bSMarcin Wojtas 	reg <<= FELIX_ANA_VT_PORTMASK_SHIFT;
854451bcf1bSMarcin Wojtas 	reg |= FELIX_ANA_VT_WRITE;
855451bcf1bSMarcin Wojtas 
856451bcf1bSMarcin Wojtas 	FELIX_WR4(sc, FELIX_ANA_VTIDX, vid);
857451bcf1bSMarcin Wojtas 	FELIX_WR4(sc, FELIX_ANA_VT, reg);
858451bcf1bSMarcin Wojtas 
859451bcf1bSMarcin Wojtas 	/*
860451bcf1bSMarcin Wojtas 	 * According to documentation read and write commands
861451bcf1bSMarcin Wojtas 	 * are instant.
862451bcf1bSMarcin Wojtas 	 * Add a small delay just to be safe.
863451bcf1bSMarcin Wojtas 	 */
864451bcf1bSMarcin Wojtas 	mb();
865451bcf1bSMarcin Wojtas 	DELAY(100);
866451bcf1bSMarcin Wojtas 	reg = FELIX_RD4(sc, FELIX_ANA_VT);
867451bcf1bSMarcin Wojtas 	if ((reg & FELIX_ANA_VT_STS) != FELIX_ANA_VT_IDLE)
868451bcf1bSMarcin Wojtas 		return (ENXIO);
869451bcf1bSMarcin Wojtas 
870451bcf1bSMarcin Wojtas 	return (0);
871451bcf1bSMarcin Wojtas }
872451bcf1bSMarcin Wojtas 
873451bcf1bSMarcin Wojtas static int
874451bcf1bSMarcin Wojtas felix_setvgroup(device_t dev, etherswitch_vlangroup_t *vg)
875451bcf1bSMarcin Wojtas {
876451bcf1bSMarcin Wojtas 	felix_softc_t sc;
877451bcf1bSMarcin Wojtas 	int error;
878451bcf1bSMarcin Wojtas 
879451bcf1bSMarcin Wojtas 	sc = device_get_softc(dev);
880451bcf1bSMarcin Wojtas 
881451bcf1bSMarcin Wojtas 	FELIX_LOCK(sc);
882451bcf1bSMarcin Wojtas 	if (sc->vlan_mode == ETHERSWITCH_VLAN_DOT1Q)
883451bcf1bSMarcin Wojtas 		error = felix_set_dot1q_vlan(sc, vg);
884451bcf1bSMarcin Wojtas 	else
885451bcf1bSMarcin Wojtas 		error = EINVAL;
886451bcf1bSMarcin Wojtas 
887451bcf1bSMarcin Wojtas 	FELIX_UNLOCK(sc);
888451bcf1bSMarcin Wojtas 	return (error);
889451bcf1bSMarcin Wojtas }
890451bcf1bSMarcin Wojtas 
891451bcf1bSMarcin Wojtas static int
892451bcf1bSMarcin Wojtas felix_get_dot1q_vlan(felix_softc_t sc, etherswitch_vlangroup_t *vg)
893451bcf1bSMarcin Wojtas {
894451bcf1bSMarcin Wojtas 	uint32_t reg;
895451bcf1bSMarcin Wojtas 	int vid;
896451bcf1bSMarcin Wojtas 
897451bcf1bSMarcin Wojtas 	vid = sc->vlans[vg->es_vlangroup];
898451bcf1bSMarcin Wojtas 
899451bcf1bSMarcin Wojtas 	if (vid == 0)
900451bcf1bSMarcin Wojtas 		return (0);
901451bcf1bSMarcin Wojtas 
902451bcf1bSMarcin Wojtas 	FELIX_WR4(sc, FELIX_ANA_VTIDX, vid);
903451bcf1bSMarcin Wojtas 	FELIX_WR4(sc, FELIX_ANA_VT, FELIX_ANA_VT_READ);
904451bcf1bSMarcin Wojtas 
905451bcf1bSMarcin Wojtas 	/*
906451bcf1bSMarcin Wojtas 	 * According to documentation read and write commands
907451bcf1bSMarcin Wojtas 	 * are instant.
908451bcf1bSMarcin Wojtas 	 * Add a small delay just to be safe.
909451bcf1bSMarcin Wojtas 	 */
910451bcf1bSMarcin Wojtas 	mb();
911451bcf1bSMarcin Wojtas 	DELAY(100);
912451bcf1bSMarcin Wojtas 	reg = FELIX_RD4(sc, FELIX_ANA_VT);
913451bcf1bSMarcin Wojtas 	if ((reg & FELIX_ANA_VT_STS) != FELIX_ANA_VT_IDLE)
914451bcf1bSMarcin Wojtas 		return (ENXIO);
915451bcf1bSMarcin Wojtas 
916451bcf1bSMarcin Wojtas 	reg >>= FELIX_ANA_VT_PORTMASK_SHIFT;
917451bcf1bSMarcin Wojtas 	reg &= FELIX_ANA_VT_PORTMASK_MASK;
918451bcf1bSMarcin Wojtas 
919451bcf1bSMarcin Wojtas 	vg->es_untagged_ports = vg->es_member_ports = reg;
920451bcf1bSMarcin Wojtas 	vg->es_fid = 0;
921451bcf1bSMarcin Wojtas 	vg->es_vid = vid | ETHERSWITCH_VID_VALID;
922451bcf1bSMarcin Wojtas 	return (0);
923451bcf1bSMarcin Wojtas }
924451bcf1bSMarcin Wojtas 
925451bcf1bSMarcin Wojtas static int
926451bcf1bSMarcin Wojtas felix_getvgroup(device_t dev, etherswitch_vlangroup_t *vg)
927451bcf1bSMarcin Wojtas {
928451bcf1bSMarcin Wojtas 	felix_softc_t sc;
929451bcf1bSMarcin Wojtas 	int error;
930451bcf1bSMarcin Wojtas 
931451bcf1bSMarcin Wojtas 	sc = device_get_softc(dev);
932451bcf1bSMarcin Wojtas 
933451bcf1bSMarcin Wojtas 	FELIX_LOCK(sc);
934451bcf1bSMarcin Wojtas 	if (sc->vlan_mode == ETHERSWITCH_VLAN_DOT1Q)
935451bcf1bSMarcin Wojtas 		error = felix_get_dot1q_vlan(sc, vg);
936451bcf1bSMarcin Wojtas 	else
937451bcf1bSMarcin Wojtas 		error = EINVAL;
938451bcf1bSMarcin Wojtas 
939451bcf1bSMarcin Wojtas 	FELIX_UNLOCK(sc);
940451bcf1bSMarcin Wojtas 	return (error);
941451bcf1bSMarcin Wojtas }
942451bcf1bSMarcin Wojtas 
943451bcf1bSMarcin Wojtas static void
944451bcf1bSMarcin Wojtas felix_tick(void *arg)
945451bcf1bSMarcin Wojtas {
946451bcf1bSMarcin Wojtas 	struct mii_data *mii;
947451bcf1bSMarcin Wojtas 	felix_softc_t sc;
948451bcf1bSMarcin Wojtas 	int port;
949451bcf1bSMarcin Wojtas 
950451bcf1bSMarcin Wojtas 	sc = arg;
951451bcf1bSMarcin Wojtas 
952451bcf1bSMarcin Wojtas 	FELIX_LOCK_ASSERT(sc, MA_OWNED);
953451bcf1bSMarcin Wojtas 
954451bcf1bSMarcin Wojtas 	for (port = 0; port < sc->info.es_nports; port++) {
955451bcf1bSMarcin Wojtas 		if (!felix_is_phyport(sc, port))
956451bcf1bSMarcin Wojtas 			continue;
957451bcf1bSMarcin Wojtas 
958451bcf1bSMarcin Wojtas 		mii = felix_miiforport(sc, port);
959451bcf1bSMarcin Wojtas 		MPASS(mii != NULL);
960451bcf1bSMarcin Wojtas 		mii_tick(mii);
961451bcf1bSMarcin Wojtas 	}
962d88aecceSKornel Duleba 	if (sc->timer_ticks != 0)
963d88aecceSKornel Duleba 		callout_reset(&sc->tick_callout, sc->timer_ticks, felix_tick, sc);
964451bcf1bSMarcin Wojtas }
965451bcf1bSMarcin Wojtas 
966451bcf1bSMarcin Wojtas static int
9672e6a8c1aSJustin Hibbits felix_ifmedia_upd(if_t ifp)
968451bcf1bSMarcin Wojtas {
969451bcf1bSMarcin Wojtas 	struct mii_data *mii;
970451bcf1bSMarcin Wojtas 	felix_softc_t sc;
971451bcf1bSMarcin Wojtas 
9722e6a8c1aSJustin Hibbits 	sc = if_getsoftc(ifp);
973*b29549c7SJustin Hibbits 	mii = felix_miiforport(sc, if_getdunit(ifp));
974451bcf1bSMarcin Wojtas 	if (mii == NULL)
975451bcf1bSMarcin Wojtas 		return (ENXIO);
976451bcf1bSMarcin Wojtas 
977451bcf1bSMarcin Wojtas 	mii_mediachg(mii);
978451bcf1bSMarcin Wojtas 	return (0);
979451bcf1bSMarcin Wojtas }
980451bcf1bSMarcin Wojtas 
981451bcf1bSMarcin Wojtas static void
9822e6a8c1aSJustin Hibbits felix_ifmedia_sts(if_t ifp, struct ifmediareq *ifmr)
983451bcf1bSMarcin Wojtas {
984451bcf1bSMarcin Wojtas 	felix_softc_t sc;
985451bcf1bSMarcin Wojtas 	struct mii_data *mii;
986451bcf1bSMarcin Wojtas 
9872e6a8c1aSJustin Hibbits 	sc = if_getsoftc(ifp);
988*b29549c7SJustin Hibbits 	mii = felix_miiforport(sc, if_getdunit(ifp));
989451bcf1bSMarcin Wojtas 	if (mii == NULL)
990451bcf1bSMarcin Wojtas 		return;
991451bcf1bSMarcin Wojtas 
992451bcf1bSMarcin Wojtas 	mii_pollstat(mii);
993451bcf1bSMarcin Wojtas 	ifmr->ifm_active = mii->mii_media_active;
994451bcf1bSMarcin Wojtas 	ifmr->ifm_status = mii->mii_media_status;
995451bcf1bSMarcin Wojtas }
996451bcf1bSMarcin Wojtas 
997451bcf1bSMarcin Wojtas static  bool
998451bcf1bSMarcin Wojtas felix_is_phyport(felix_softc_t sc, int port)
999451bcf1bSMarcin Wojtas {
1000451bcf1bSMarcin Wojtas 
1001451bcf1bSMarcin Wojtas 	return (!sc->ports[port].fixed_port);
1002451bcf1bSMarcin Wojtas }
1003451bcf1bSMarcin Wojtas 
1004451bcf1bSMarcin Wojtas static  struct mii_data*
1005451bcf1bSMarcin Wojtas felix_miiforport(felix_softc_t sc, unsigned int port)
1006451bcf1bSMarcin Wojtas {
1007451bcf1bSMarcin Wojtas 
1008451bcf1bSMarcin Wojtas 	if (!felix_is_phyport(sc, port))
1009451bcf1bSMarcin Wojtas 		return (NULL);
1010451bcf1bSMarcin Wojtas 
1011451bcf1bSMarcin Wojtas 	return (device_get_softc(sc->ports[port].miibus));
1012451bcf1bSMarcin Wojtas }
1013