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