8ef3d2baf6
This revision introduces basic support for the internal ESW switch found Ralink/Mediatek SoCs such as RT3050, RT3352, RT5350, MT7628; and GSW found in MT7620 and MT7621. It only supports 802.1q VLANs and doesn't support external PHYs at the moment (only the ones that are built into the switch itself). Approved by: adrian (mentor) Sponsored by: Smartcom - Bulgaria AD Differential Revision: https://reviews.freebsd.org/D6348
669 lines
16 KiB
C
669 lines
16 KiB
C
/*-
|
|
* Copyright (c) 2016 Stanislav Galabov.
|
|
* Copyright (c) 2011-2012 Stefan Bethke.
|
|
* Copyright (c) 2012 Adrian Chadd.
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
*
|
|
* $FreeBSD$
|
|
*/
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/bus.h>
|
|
#include <sys/errno.h>
|
|
#include <sys/kernel.h>
|
|
#include <sys/lock.h>
|
|
#include <sys/malloc.h>
|
|
#include <sys/module.h>
|
|
#include <sys/mutex.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/sockio.h>
|
|
#include <sys/sysctl.h>
|
|
#include <sys/systm.h>
|
|
|
|
#include <net/if.h>
|
|
#include <net/if_var.h>
|
|
#include <net/ethernet.h>
|
|
#include <net/if_media.h>
|
|
#include <net/if_types.h>
|
|
|
|
#include <machine/bus.h>
|
|
#include <dev/mii/mii.h>
|
|
#include <dev/mii/miivar.h>
|
|
#include <dev/mdio/mdio.h>
|
|
|
|
#include <dev/etherswitch/etherswitch.h>
|
|
#include <dev/etherswitch/mtkswitch/mtkswitchvar.h>
|
|
|
|
#include <dev/ofw/ofw_bus_subr.h>
|
|
|
|
#include "mdio_if.h"
|
|
#include "miibus_if.h"
|
|
#include "etherswitch_if.h"
|
|
|
|
#define DEBUG
|
|
|
|
#if defined(DEBUG)
|
|
static SYSCTL_NODE(_debug, OID_AUTO, mtkswitch, CTLFLAG_RD, 0, "mtkswitch");
|
|
#endif
|
|
|
|
static inline int mtkswitch_portforphy(int phy);
|
|
static int mtkswitch_ifmedia_upd(struct ifnet *ifp);
|
|
static void mtkswitch_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr);
|
|
static void mtkswitch_tick(void *arg);
|
|
|
|
static const struct ofw_compat_data compat_data[] = {
|
|
{ "ralink,rt3050-esw", MTK_SWITCH_RT3050 },
|
|
{ "ralink,rt3352-esw", MTK_SWITCH_RT3352 },
|
|
{ "ralink,rt5350-esw", MTK_SWITCH_RT5350 },
|
|
{ "mediatek,mt7620-gsw", MTK_SWITCH_MT7620 },
|
|
{ "mediatek,mt7621-gsw", MTK_SWITCH_MT7621 },
|
|
{ "mediatek,mt7628-esw", MTK_SWITCH_MT7628 },
|
|
|
|
/* Sentinel */
|
|
{ NULL, MTK_SWITCH_NONE }
|
|
};
|
|
|
|
static int
|
|
mtkswitch_probe(device_t dev)
|
|
{
|
|
struct mtkswitch_softc *sc;
|
|
mtk_switch_type switch_type;
|
|
|
|
if (!ofw_bus_status_okay(dev))
|
|
return (ENXIO);
|
|
|
|
switch_type = ofw_bus_search_compatible(dev, compat_data)->ocd_data;
|
|
if (switch_type == MTK_SWITCH_NONE)
|
|
return (ENXIO);
|
|
|
|
sc = device_get_softc(dev);
|
|
bzero(sc, sizeof(*sc));
|
|
sc->sc_switchtype = switch_type;
|
|
|
|
device_set_desc_copy(dev, "MTK Switch Driver");
|
|
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
mtkswitch_attach_phys(struct mtkswitch_softc *sc)
|
|
{
|
|
int phy, err = 0;
|
|
char name[IFNAMSIZ];
|
|
|
|
/* PHYs need an interface, so we generate a dummy one */
|
|
snprintf(name, IFNAMSIZ, "%sport", device_get_nameunit(sc->sc_dev));
|
|
for (phy = 0; phy < sc->numphys; phy++) {
|
|
if ((sc->phymap & (1u << phy)) == 0) {
|
|
sc->ifp[phy] = NULL;
|
|
sc->ifname[phy] = NULL;
|
|
sc->miibus[phy] = NULL;
|
|
continue;
|
|
}
|
|
sc->ifp[phy] = if_alloc(IFT_ETHER);
|
|
sc->ifp[phy]->if_softc = sc;
|
|
sc->ifp[phy]->if_flags |= IFF_UP | IFF_BROADCAST |
|
|
IFF_DRV_RUNNING | IFF_SIMPLEX;
|
|
sc->ifname[phy] = malloc(strlen(name) + 1, M_DEVBUF, M_WAITOK);
|
|
bcopy(name, sc->ifname[phy], strlen(name) + 1);
|
|
if_initname(sc->ifp[phy], sc->ifname[phy],
|
|
mtkswitch_portforphy(phy));
|
|
err = mii_attach(sc->sc_dev, &sc->miibus[phy], sc->ifp[phy],
|
|
mtkswitch_ifmedia_upd, mtkswitch_ifmedia_sts,
|
|
BMSR_DEFCAPMASK, phy, MII_OFFSET_ANY, 0);
|
|
if (err != 0) {
|
|
device_printf(sc->sc_dev,
|
|
"attaching PHY %d failed\n",
|
|
phy);
|
|
} else {
|
|
DPRINTF(sc->sc_dev, "%s attached to pseudo interface "
|
|
"%s\n", device_get_nameunit(sc->miibus[phy]),
|
|
sc->ifp[phy]->if_xname);
|
|
}
|
|
}
|
|
return (err);
|
|
}
|
|
|
|
static int
|
|
mtkswitch_set_vlan_mode(struct mtkswitch_softc *sc, uint32_t mode)
|
|
{
|
|
|
|
/* Check for invalid modes. */
|
|
if ((mode & sc->info.es_vlan_caps) != mode)
|
|
return (EINVAL);
|
|
|
|
sc->vlan_mode = mode;
|
|
|
|
/* Reset VLANs. */
|
|
sc->hal.mtkswitch_vlan_init_hw(sc);
|
|
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
mtkswitch_attach(device_t dev)
|
|
{
|
|
struct mtkswitch_softc *sc;
|
|
int err = 0;
|
|
int port, rid;
|
|
|
|
sc = device_get_softc(dev);
|
|
|
|
/* sc->sc_switchtype is already decided in mtkswitch_probe() */
|
|
sc->numports = MTKSWITCH_MAX_PORTS;
|
|
sc->numphys = MTKSWITCH_MAX_PHYS;
|
|
sc->cpuport = MTKSWITCH_CPU_PORT;
|
|
sc->sc_dev = dev;
|
|
|
|
/* Attach switch related functions */
|
|
if (sc->sc_switchtype == MTK_SWITCH_NONE) {
|
|
device_printf(dev, "Unknown switch type\n");
|
|
return (ENXIO);
|
|
}
|
|
|
|
if (sc->sc_switchtype == MTK_SWITCH_MT7620 ||
|
|
sc->sc_switchtype == MTK_SWITCH_MT7621)
|
|
mtk_attach_switch_mt7620(sc);
|
|
else
|
|
mtk_attach_switch_rt3050(sc);
|
|
|
|
/* Allocate resources */
|
|
rid = 0;
|
|
sc->sc_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
|
|
RF_ACTIVE);
|
|
if (sc->sc_res == NULL) {
|
|
device_printf(dev, "could not map memory\n");
|
|
return (ENXIO);
|
|
}
|
|
|
|
mtx_init(&sc->sc_mtx, "mtkswitch", NULL, MTX_DEF);
|
|
|
|
/* Reset the switch */
|
|
if (sc->hal.mtkswitch_reset(sc)) {
|
|
DPRINTF(dev, "%s: mtkswitch_reset: failed\n", __func__);
|
|
return (ENXIO);
|
|
}
|
|
|
|
err = sc->hal.mtkswitch_hw_setup(sc);
|
|
DPRINTF(dev, "%s: hw_setup: err=%d\n", __func__, err);
|
|
if (err != 0)
|
|
return (err);
|
|
|
|
err = sc->hal.mtkswitch_hw_global_setup(sc);
|
|
DPRINTF(dev, "%s: hw_global_setup: err=%d\n", __func__, err);
|
|
if (err != 0)
|
|
return (err);
|
|
|
|
/* Initialize the switch ports */
|
|
for (port = 0; port < sc->numports; port++) {
|
|
sc->hal.mtkswitch_port_init(sc, port);
|
|
}
|
|
|
|
/* Attach the PHYs and complete the bus enumeration */
|
|
err = mtkswitch_attach_phys(sc);
|
|
DPRINTF(dev, "%s: attach_phys: err=%d\n", __func__, err);
|
|
if (err != 0)
|
|
return (err);
|
|
|
|
/* Default to ingress filters off. */
|
|
err = mtkswitch_set_vlan_mode(sc, ETHERSWITCH_VLAN_DOT1Q);
|
|
DPRINTF(dev, "%s: set_vlan_mode: err=%d\n", __func__, err);
|
|
if (err != 0)
|
|
return (err);
|
|
|
|
bus_generic_probe(dev);
|
|
bus_enumerate_hinted_children(dev);
|
|
err = bus_generic_attach(dev);
|
|
DPRINTF(dev, "%s: bus_generic_attach: err=%d\n", __func__, err);
|
|
if (err != 0)
|
|
return (err);
|
|
|
|
callout_init_mtx(&sc->callout_tick, &sc->sc_mtx, 0);
|
|
|
|
MTKSWITCH_LOCK(sc);
|
|
mtkswitch_tick(sc);
|
|
MTKSWITCH_UNLOCK(sc);
|
|
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
mtkswitch_detach(device_t dev)
|
|
{
|
|
struct mtkswitch_softc *sc = device_get_softc(dev);
|
|
int phy;
|
|
|
|
callout_drain(&sc->callout_tick);
|
|
|
|
for (phy = 0; phy < MTKSWITCH_MAX_PHYS; phy++) {
|
|
if (sc->miibus[phy] != NULL)
|
|
device_delete_child(dev, sc->miibus[phy]);
|
|
if (sc->ifp[phy] != NULL)
|
|
if_free(sc->ifp[phy]);
|
|
free(sc->ifname[phy], M_DEVBUF);
|
|
}
|
|
|
|
bus_generic_detach(dev);
|
|
mtx_destroy(&sc->sc_mtx);
|
|
|
|
return (0);
|
|
}
|
|
|
|
/* PHY <-> port mapping is currently 1:1 */
|
|
static inline int
|
|
mtkswitch_portforphy(int phy)
|
|
{
|
|
|
|
return (phy);
|
|
}
|
|
|
|
static inline int
|
|
mtkswitch_phyforport(int port)
|
|
{
|
|
|
|
return (port);
|
|
}
|
|
|
|
static inline struct mii_data *
|
|
mtkswitch_miiforport(struct mtkswitch_softc *sc, int port)
|
|
{
|
|
int phy = mtkswitch_phyforport(port);
|
|
|
|
if (phy < 0 || phy >= MTKSWITCH_MAX_PHYS || sc->miibus[phy] == NULL)
|
|
return (NULL);
|
|
|
|
return (device_get_softc(sc->miibus[phy]));
|
|
}
|
|
|
|
static inline struct ifnet *
|
|
mtkswitch_ifpforport(struct mtkswitch_softc *sc, int port)
|
|
{
|
|
int phy = mtkswitch_phyforport(port);
|
|
|
|
if (phy < 0 || phy >= MTKSWITCH_MAX_PHYS)
|
|
return (NULL);
|
|
|
|
return (sc->ifp[phy]);
|
|
}
|
|
|
|
/*
|
|
* Convert port status to ifmedia.
|
|
*/
|
|
static void
|
|
mtkswitch_update_ifmedia(uint32_t portstatus, u_int *media_status,
|
|
u_int *media_active)
|
|
{
|
|
*media_active = IFM_ETHER;
|
|
*media_status = IFM_AVALID;
|
|
|
|
if ((portstatus & MTKSWITCH_LINK_UP) != 0)
|
|
*media_status |= IFM_ACTIVE;
|
|
else {
|
|
*media_active |= IFM_NONE;
|
|
return;
|
|
}
|
|
|
|
switch (portstatus & MTKSWITCH_SPEED_MASK) {
|
|
case MTKSWITCH_SPEED_10:
|
|
*media_active |= IFM_10_T;
|
|
break;
|
|
case MTKSWITCH_SPEED_100:
|
|
*media_active |= IFM_100_TX;
|
|
break;
|
|
case MTKSWITCH_SPEED_1000:
|
|
*media_active |= IFM_1000_T;
|
|
break;
|
|
}
|
|
|
|
if ((portstatus & MTKSWITCH_DUPLEX) != 0)
|
|
*media_active |= IFM_FDX;
|
|
else
|
|
*media_active |= IFM_HDX;
|
|
|
|
if ((portstatus & MTKSWITCH_TXFLOW) != 0)
|
|
*media_active |= IFM_ETH_TXPAUSE;
|
|
if ((portstatus & MTKSWITCH_RXFLOW) != 0)
|
|
*media_active |= IFM_ETH_RXPAUSE;
|
|
}
|
|
|
|
static void
|
|
mtkswitch_miipollstat(struct mtkswitch_softc *sc)
|
|
{
|
|
struct mii_data *mii;
|
|
struct mii_softc *miisc;
|
|
uint32_t portstatus;
|
|
int i, port_flap = 0;
|
|
|
|
MTKSWITCH_LOCK_ASSERT(sc, MA_OWNED);
|
|
|
|
for (i = 0; i < sc->numphys; i++) {
|
|
if (sc->miibus[i] == NULL)
|
|
continue;
|
|
mii = device_get_softc(sc->miibus[i]);
|
|
portstatus = sc->hal.mtkswitch_get_port_status(sc,
|
|
mtkswitch_portforphy(i));
|
|
|
|
/* If a port has flapped - mark it so we can flush the ATU */
|
|
if (((mii->mii_media_status & IFM_ACTIVE) == 0 &&
|
|
(portstatus & MTKSWITCH_LINK_UP) != 0) ||
|
|
((mii->mii_media_status & IFM_ACTIVE) != 0 &&
|
|
(portstatus & MTKSWITCH_LINK_UP) == 0)) {
|
|
port_flap = 1;
|
|
}
|
|
|
|
mtkswitch_update_ifmedia(portstatus, &mii->mii_media_status,
|
|
&mii->mii_media_active);
|
|
LIST_FOREACH(miisc, &mii->mii_phys, mii_list) {
|
|
if (IFM_INST(mii->mii_media.ifm_cur->ifm_media) !=
|
|
miisc->mii_inst)
|
|
continue;
|
|
mii_phy_update(miisc, MII_POLLSTAT);
|
|
}
|
|
}
|
|
|
|
if (port_flap)
|
|
sc->hal.mtkswitch_atu_flush(sc);
|
|
}
|
|
|
|
static void
|
|
mtkswitch_tick(void *arg)
|
|
{
|
|
struct mtkswitch_softc *sc = arg;
|
|
|
|
mtkswitch_miipollstat(sc);
|
|
callout_reset(&sc->callout_tick, hz, mtkswitch_tick, sc);
|
|
}
|
|
|
|
static void
|
|
mtkswitch_lock(device_t dev)
|
|
{
|
|
struct mtkswitch_softc *sc = device_get_softc(dev);
|
|
|
|
MTKSWITCH_LOCK_ASSERT(sc, MA_NOTOWNED);
|
|
MTKSWITCH_LOCK(sc);
|
|
}
|
|
|
|
static void
|
|
mtkswitch_unlock(device_t dev)
|
|
{
|
|
struct mtkswitch_softc *sc = device_get_softc(dev);
|
|
|
|
MTKSWITCH_LOCK_ASSERT(sc, MA_OWNED);
|
|
MTKSWITCH_UNLOCK(sc);
|
|
}
|
|
|
|
static etherswitch_info_t *
|
|
mtkswitch_getinfo(device_t dev)
|
|
{
|
|
struct mtkswitch_softc *sc = device_get_softc(dev);
|
|
|
|
return (&sc->info);
|
|
}
|
|
|
|
static inline int
|
|
mtkswitch_is_cpuport(struct mtkswitch_softc *sc, int port)
|
|
{
|
|
|
|
return (sc->cpuport == port);
|
|
}
|
|
|
|
static int
|
|
mtkswitch_getport(device_t dev, etherswitch_port_t *p)
|
|
{
|
|
struct mtkswitch_softc *sc;
|
|
struct mii_data *mii;
|
|
struct ifmediareq *ifmr;
|
|
int err;
|
|
|
|
sc = device_get_softc(dev);
|
|
if (p->es_port < 0 || p->es_port > sc->info.es_nports)
|
|
return (ENXIO);
|
|
|
|
err = sc->hal.mtkswitch_port_vlan_get(sc, p);
|
|
if (err != 0)
|
|
return (err);
|
|
|
|
mii = mtkswitch_miiforport(sc, p->es_port);
|
|
if (mtkswitch_is_cpuport(sc, p->es_port)) {
|
|
/* fill in fixed values for CPU port */
|
|
/* XXX is this valid in all cases? */
|
|
p->es_flags |= ETHERSWITCH_PORT_CPU;
|
|
ifmr = &p->es_ifmr;
|
|
ifmr->ifm_count = 0;
|
|
ifmr->ifm_current = ifmr->ifm_active =
|
|
IFM_ETHER | IFM_1000_T | IFM_FDX;
|
|
ifmr->ifm_mask = 0;
|
|
ifmr->ifm_status = IFM_ACTIVE | IFM_AVALID;
|
|
} else if (mii != NULL) {
|
|
err = ifmedia_ioctl(mii->mii_ifp, &p->es_ifr,
|
|
&mii->mii_media, SIOCGIFMEDIA);
|
|
if (err)
|
|
return (err);
|
|
} else {
|
|
ifmr = &p->es_ifmr;
|
|
ifmr->ifm_count = 0;
|
|
ifmr->ifm_current = ifmr->ifm_active = IFM_NONE;
|
|
ifmr->ifm_mask = 0;
|
|
ifmr->ifm_status = 0;
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
mtkswitch_setport(device_t dev, etherswitch_port_t *p)
|
|
{
|
|
int err;
|
|
struct mtkswitch_softc *sc;
|
|
struct ifmedia *ifm;
|
|
struct mii_data *mii;
|
|
struct ifnet *ifp;
|
|
|
|
sc = device_get_softc(dev);
|
|
if (p->es_port < 0 || p->es_port > sc->info.es_nports)
|
|
return (ENXIO);
|
|
|
|
/* Port flags. */
|
|
if (sc->vlan_mode == ETHERSWITCH_VLAN_DOT1Q) {
|
|
err = sc->hal.mtkswitch_port_vlan_setup(sc, p);
|
|
if (err)
|
|
return (err);
|
|
}
|
|
|
|
/* Do not allow media changes on CPU port. */
|
|
if (mtkswitch_is_cpuport(sc, p->es_port))
|
|
return (0);
|
|
|
|
mii = mtkswitch_miiforport(sc, p->es_port);
|
|
if (mii == NULL)
|
|
return (ENXIO);
|
|
|
|
ifp = mtkswitch_ifpforport(sc, p->es_port);
|
|
|
|
ifm = &mii->mii_media;
|
|
return (ifmedia_ioctl(ifp, &p->es_ifr, ifm, SIOCSIFMEDIA));
|
|
}
|
|
|
|
static void
|
|
mtkswitch_statchg(device_t dev)
|
|
{
|
|
|
|
DPRINTF(dev, "%s\n", __func__);
|
|
}
|
|
|
|
static int
|
|
mtkswitch_ifmedia_upd(struct ifnet *ifp)
|
|
{
|
|
struct mtkswitch_softc *sc = ifp->if_softc;
|
|
struct mii_data *mii = mtkswitch_miiforport(sc, ifp->if_dunit);
|
|
|
|
if (mii == NULL)
|
|
return (ENXIO);
|
|
mii_mediachg(mii);
|
|
return (0);
|
|
}
|
|
|
|
static void
|
|
mtkswitch_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr)
|
|
{
|
|
struct mtkswitch_softc *sc = ifp->if_softc;
|
|
struct mii_data *mii = mtkswitch_miiforport(sc, ifp->if_dunit);
|
|
|
|
DPRINTF(sc->sc_dev, "%s\n", __func__);
|
|
|
|
if (mii == NULL)
|
|
return;
|
|
mii_pollstat(mii);
|
|
ifmr->ifm_active = mii->mii_media_active;
|
|
ifmr->ifm_status = mii->mii_media_status;
|
|
}
|
|
|
|
static int
|
|
mtkswitch_getconf(device_t dev, etherswitch_conf_t *conf)
|
|
{
|
|
struct mtkswitch_softc *sc;
|
|
|
|
sc = device_get_softc(dev);
|
|
|
|
/* Return the VLAN mode. */
|
|
conf->cmd = ETHERSWITCH_CONF_VLAN_MODE;
|
|
conf->vlan_mode = sc->vlan_mode;
|
|
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
mtkswitch_setconf(device_t dev, etherswitch_conf_t *conf)
|
|
{
|
|
struct mtkswitch_softc *sc;
|
|
int err;
|
|
|
|
sc = device_get_softc(dev);
|
|
|
|
/* Set the VLAN mode. */
|
|
if (conf->cmd & ETHERSWITCH_CONF_VLAN_MODE) {
|
|
err = mtkswitch_set_vlan_mode(sc, conf->vlan_mode);
|
|
if (err != 0)
|
|
return (err);
|
|
}
|
|
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
mtkswitch_getvgroup(device_t dev, etherswitch_vlangroup_t *e)
|
|
{
|
|
struct mtkswitch_softc *sc = device_get_softc(dev);
|
|
|
|
return (sc->hal.mtkswitch_vlan_getvgroup(sc, e));
|
|
}
|
|
|
|
static int
|
|
mtkswitch_setvgroup(device_t dev, etherswitch_vlangroup_t *e)
|
|
{
|
|
struct mtkswitch_softc *sc = device_get_softc(dev);
|
|
|
|
return (sc->hal.mtkswitch_vlan_setvgroup(sc, e));
|
|
}
|
|
|
|
static int
|
|
mtkswitch_readphy(device_t dev, int phy, int reg)
|
|
{
|
|
struct mtkswitch_softc *sc = device_get_softc(dev);
|
|
|
|
return (sc->hal.mtkswitch_phy_read(dev, phy, reg));
|
|
}
|
|
|
|
static int
|
|
mtkswitch_writephy(device_t dev, int phy, int reg, int val)
|
|
{
|
|
struct mtkswitch_softc *sc = device_get_softc(dev);
|
|
|
|
return (sc->hal.mtkswitch_phy_write(dev, phy, reg, val));
|
|
}
|
|
|
|
static int
|
|
mtkswitch_readreg(device_t dev, int addr)
|
|
{
|
|
struct mtkswitch_softc *sc = device_get_softc(dev);
|
|
|
|
return (sc->hal.mtkswitch_reg_read(dev, addr));
|
|
}
|
|
|
|
static int
|
|
mtkswitch_writereg(device_t dev, int addr, int value)
|
|
{
|
|
struct mtkswitch_softc *sc = device_get_softc(dev);
|
|
|
|
return (sc->hal.mtkswitch_reg_write(dev, addr, value));
|
|
}
|
|
|
|
static device_method_t mtkswitch_methods[] = {
|
|
/* Device interface */
|
|
DEVMETHOD(device_probe, mtkswitch_probe),
|
|
DEVMETHOD(device_attach, mtkswitch_attach),
|
|
DEVMETHOD(device_detach, mtkswitch_detach),
|
|
|
|
/* bus interface */
|
|
DEVMETHOD(bus_add_child, device_add_child_ordered),
|
|
|
|
/* MII interface */
|
|
DEVMETHOD(miibus_readreg, mtkswitch_readphy),
|
|
DEVMETHOD(miibus_writereg, mtkswitch_writephy),
|
|
DEVMETHOD(miibus_statchg, mtkswitch_statchg),
|
|
|
|
/* MDIO interface */
|
|
DEVMETHOD(mdio_readreg, mtkswitch_readphy),
|
|
DEVMETHOD(mdio_writereg, mtkswitch_writephy),
|
|
|
|
/* ehterswitch interface */
|
|
DEVMETHOD(etherswitch_lock, mtkswitch_lock),
|
|
DEVMETHOD(etherswitch_unlock, mtkswitch_unlock),
|
|
DEVMETHOD(etherswitch_getinfo, mtkswitch_getinfo),
|
|
DEVMETHOD(etherswitch_readreg, mtkswitch_readreg),
|
|
DEVMETHOD(etherswitch_writereg, mtkswitch_writereg),
|
|
DEVMETHOD(etherswitch_readphyreg, mtkswitch_readphy),
|
|
DEVMETHOD(etherswitch_writephyreg, mtkswitch_writephy),
|
|
DEVMETHOD(etherswitch_getport, mtkswitch_getport),
|
|
DEVMETHOD(etherswitch_setport, mtkswitch_setport),
|
|
DEVMETHOD(etherswitch_getvgroup, mtkswitch_getvgroup),
|
|
DEVMETHOD(etherswitch_setvgroup, mtkswitch_setvgroup),
|
|
DEVMETHOD(etherswitch_getconf, mtkswitch_getconf),
|
|
DEVMETHOD(etherswitch_setconf, mtkswitch_setconf),
|
|
|
|
DEVMETHOD_END
|
|
};
|
|
|
|
DEFINE_CLASS_0(mtkswitch, mtkswitch_driver, mtkswitch_methods,
|
|
sizeof(struct mtkswitch_softc));
|
|
static devclass_t mtkswitch_devclass;
|
|
|
|
DRIVER_MODULE(mtkswitch, simplebus, mtkswitch_driver, mtkswitch_devclass, 0, 0);
|
|
DRIVER_MODULE(miibus, mtkswitch, miibus_driver, miibus_devclass, 0, 0);
|
|
DRIVER_MODULE(mdio, mtkswitch, mdio_driver, mdio_devclass, 0, 0);
|
|
DRIVER_MODULE(etherswitch, mtkswitch, etherswitch_driver, etherswitch_devclass,
|
|
0, 0);
|
|
MODULE_VERSION(mtkswitch, 1);
|
|
MODULE_DEPEND(mtkswitch, miibus, 1, 1, 1);
|
|
MODULE_DEPEND(mtkswitch, etherswitch, 1, 1, 1);
|