diff --git a/sbin/ifconfig/Makefile b/sbin/ifconfig/Makefile index 77491f2..66df654 100644 --- a/sbin/ifconfig/Makefile +++ b/sbin/ifconfig/Makefile @@ -23,6 +23,7 @@ SRCS+= af_nd6.c # ND6 support SRCS+= ifclone.c # clone device support SRCS+= ifmac.c # MAC support SRCS+= ifmedia.c # SIOC[GS]IFMEDIA support +SRCS+= ifswitch.c # SIOC[GS]IFSWMEDIA support SRCS+= ifvlan.c # SIOC[GS]ETVLAN support SRCS+= ifgre.c # GRE keys etc SRCS+= ifgif.c # GIF reversed header workaround diff --git a/sbin/ifconfig/ifconfig.c b/sbin/ifconfig/ifconfig.c index 7c5d351..669c3be 100644 --- a/sbin/ifconfig/ifconfig.c +++ b/sbin/ifconfig/ifconfig.c @@ -94,6 +94,7 @@ int noload; int supmedia = 0; int printkeys = 0; /* Print keying material for interfaces. */ +int haveswphy = 0; static int ifconfig(int argc, char *const *argv, int iscreate, const struct afswtch *afp); diff --git a/sbin/ifconfig/ifconfig.h b/sbin/ifconfig/ifconfig.h index d6f5349..b3aabcb 100644 --- a/sbin/ifconfig/ifconfig.h +++ b/sbin/ifconfig/ifconfig.h @@ -130,6 +130,7 @@ extern char name[IFNAMSIZ]; /* name of interface */ extern int allmedia; extern int supmedia; extern int printkeys; +extern int haveswphy; extern int newaddr; extern int verbose; diff --git a/sbin/ifconfig/ifmedia.c b/sbin/ifconfig/ifmedia.c index 889c54a..ecd595c 100644 --- a/sbin/ifconfig/ifmedia.c +++ b/sbin/ifconfig/ifmedia.c @@ -88,14 +88,13 @@ #include #include "ifconfig.h" +#include "ifmedia.h" static void domediaopt(const char *, int, int); static int get_media_subtype(int, const char *); static int get_media_mode(int, const char *); static int get_media_options(int, const char *); static int lookup_media_word(struct ifmedia_description *, const char *); -static void print_media_word(int, int); -static void print_media_word_ifconfig(int); static struct ifmedia_description *get_toptype_desc(int); static struct ifmedia_type_to_subtype *get_toptype_ttos(int); @@ -107,6 +106,18 @@ static struct ifmedia_description *get_subtype_desc(int, IFM_IEEE80211_IBSS | IFM_IEEE80211_WDS | IFM_IEEE80211_MONITOR | \ IFM_IEEE80211_MBSS)) #define IFM_IEEE80211_STA 0 +#define IFM_PHY_SHIFT 16 +#define IFM_HAVE_PHY 1 +#define IFM_WANT_PHYS 2 +#define IOCGIFMEDIA \ + ((haveswphy & IFM_HAVE_PHY) ? SIOCGIFSWMEDIA : SIOCGIFMEDIA) +#define STRGIFMEDIA \ + ((haveswphy & IFM_HAVE_PHY) ? "SIOCGIFSWMEDIA" : "SIOCGIFMEDIA") +#define IOCSIFMEDIA \ + ((haveswphy & IFM_HAVE_PHY) ? SIOCSIFSWMEDIA : SIOCSIFMEDIA) +#define STRSIFMEDIA \ + ((haveswphy & IFM_HAVE_PHY) ? "SIOCSIFSWMEDIA (media)" : \ + "SIOCSIFMEDIA (media)") static void media_status(int s) @@ -216,10 +227,12 @@ ifmedia_getstate(int s) * supported media because we need to know both * the current media type and the top-level type. */ - - if (ioctl(s, SIOCGIFMEDIA, (caddr_t)ifmr) < 0) { - err(1, "SIOCGIFMEDIA"); + if (haveswphy & IFM_HAVE_PHY) { + ifmr->ifm_phy = haveswphy >> IFM_PHY_SHIFT; + ifmr->ifm_flags |= IFM_HAVE_PHY; } + if (ioctl(s, IOCGIFMEDIA, (caddr_t)ifmr) < 0) + err(1, STRGIFMEDIA); if (ifmr->ifm_count == 0) errx(1, "%s: no media types?", name); @@ -229,8 +242,8 @@ ifmedia_getstate(int s) err(1, "malloc"); ifmr->ifm_ulist = mwords; - if (ioctl(s, SIOCGIFMEDIA, (caddr_t)ifmr) < 0) - err(1, "SIOCGIFMEDIA"); + if (ioctl(s, IOCGIFMEDIA, (caddr_t)ifmr) < 0) + err(1, STRGIFMEDIA); } return ifmr; @@ -240,12 +253,23 @@ static void setifmediacallback(int s, void *arg) { struct ifmediareq *ifmr = (struct ifmediareq *)arg; + struct ifswmediareq ifswmr; static int did_it = 0; if (!did_it) { ifr.ifr_media = ifmr->ifm_current; - if (ioctl(s, SIOCSIFMEDIA, (caddr_t)&ifr) < 0) - err(1, "SIOCSIFMEDIA (media)"); + if (haveswphy & IFM_HAVE_PHY) { + ifswmr.ifswm_phy = haveswphy >> IFM_PHY_SHIFT; + memcpy (ifswmr.ifswm_name, ifr.ifr_name, + sizeof(ifswmr.ifswm_name)); + memcpy (&ifswmr.ifswm_ifreq, &ifr, + sizeof(ifswmr.ifswm_ifreq)); + if (ioctl(s, IOCSIFMEDIA, (caddr_t)&ifswmr) < 0) + err(1, STRSIFMEDIA); + } else { + if (ioctl(s, IOCSIFMEDIA, (caddr_t)&ifr) < 0) + err(1, STRSIFMEDIA); + } free(ifmr->ifm_ulist); free(ifmr); did_it = 1; @@ -692,7 +716,7 @@ static struct ifmedia_description *get_mode_desc(int ifmw, return NULL; } -static void +void print_media_word(int ifmw, int print_toptype) { struct ifmedia_description *desc; @@ -752,7 +776,7 @@ print_media_word(int ifmw, int print_toptype) printf(" instance %d", IFM_INST(ifmw)); } -static void +void print_media_word_ifconfig(int ifmw) { struct ifmedia_description *desc; diff --git a/sbin/ifconfig/ifmedia.h b/sbin/ifconfig/ifmedia.h new file mode 100644 index 0000000..1714734 --- /dev/null +++ b/sbin/ifconfig/ifmedia.h @@ -0,0 +1,8 @@ + +#ifndef _IFMEDIA_H_ +#define _IFMEDIA_H_ + +void print_media_word(int, int); +void print_media_word_ifconfig(int); + +#endif /* _IFMEDIA_H_ */ diff --git a/sbin/ifconfig/ifswitch.c b/sbin/ifconfig/ifswitch.c new file mode 100644 index 0000000..bb94b12 --- /dev/null +++ b/sbin/ifconfig/ifswitch.c @@ -0,0 +1,366 @@ +/*- + * Copyright (c) 2010 Luiz Otavio O Souza. 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. + */ + +#ifndef lint +static const char rcsid[] = + "$FreeBSD$"; +#endif /* not lint */ + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "ifconfig.h" +#include "ifmedia.h" + +#define SW_STRIPVTAG 0x0001 +#define SW_ADDVTAG 0x0002 +#define SW_VLANBYPASS 0x0400 +#define IFM_HAVE_PHY 0x00000001 +#define IFM_PHY_SHIFT 16 +#define MII_NPHY 32 + +static void print_sw_flags(int, int); +static void print_sw_vlans(int, int); + +/* ARGSUSED */ +static void +setswitchvlan(const char *vlan, int d, int s, const struct afswtch *rafp) +{ + struct ifswitchreq ifswr; + + memset(&ifswr, 0, sizeof(ifswr)); + strlcpy(ifswr.ifsw_name, name, IFNAMSIZ); + + if ((haveswphy & IFM_HAVE_PHY) == 0) + errx(1, "no phy selected"); + + ifswr.ifsw_phy = haveswphy >> IFM_PHY_SHIFT; + ifswr.ifsw_vlan = atoi(vlan); + + if (ifswr.ifsw_vlan & ~EVL_VLID_MASK) + errx(1, "setswitchvlan: invalid vlan number"); + + if (ioctl(s, SIOCAIFSWVLAN, (caddr_t)&ifswr) == -1) + err(1, "SIOCAIFSWVLAN"); +} + +/* ARGSUSED */ +static void +unsetswitchvlan(const char *vlan, int d, int s, const struct afswtch *rafp) +{ + struct ifswitchreq ifswr; + + memset(&ifswr, 0, sizeof(ifswr)); + strlcpy(ifswr.ifsw_name, name, IFNAMSIZ); + + if ((haveswphy & IFM_HAVE_PHY) == 0) + errx(1, "no phy selected"); + + ifswr.ifsw_phy = haveswphy >> IFM_PHY_SHIFT; + ifswr.ifsw_vlan = atoi(vlan); + + if (ifswr.ifsw_vlan & ~EVL_VLID_MASK) + errx(1, "setswitchvlan: invalid vlan number"); + + if (ioctl(s, SIOCDIFSWVLAN, (caddr_t)&ifswr) == -1) + err(1, "SIOCDIFSWVLAN"); +} + +/* ARGSUSED */ +static void +setdefaultvlan(const char *vlan, int d, int s, const struct afswtch *rafp) +{ + struct ifswitchreq ifswr; + + memset(&ifswr, 0, sizeof(ifswr)); + strlcpy(ifswr.ifsw_name, name, IFNAMSIZ); + + if ((haveswphy & IFM_HAVE_PHY) == 0) + errx(1, "no phy selected"); + + ifswr.ifsw_phy = haveswphy >> IFM_PHY_SHIFT; + ifswr.ifsw_vlan = atoi(vlan); + ifswr.ifsw_flags = 1; + + if (ifswr.ifsw_vlan & ~EVL_VLID_MASK) + errx(1, "setswitchvlan: invalid vlan number"); + + if (ioctl(s, SIOCAIFSWVLAN, (caddr_t)&ifswr) == -1) + err(1, "SIOCAIFSWVLAN"); +} + +/* ARGSUSED */ +static void +setswphynum(const char *phy_num, int d, int s, const struct afswtch *rafp) +{ + int phy; + + phy = atoi(phy_num); + if (phy < 0 || phy > MII_NPHY) + errx(1, "setswphynum: invalid phy number"); + + haveswphy = IFM_HAVE_PHY | (atoi(phy_num) << IFM_PHY_SHIFT); +} + +/* ARGSUSED */ +static void +setswflags(const char *vname, int value, int s, const struct afswtch *rafp) +{ + struct ifswitchreq ifswr; + + memset(&ifswr, 0, sizeof(ifswr)); + strlcpy(ifswr.ifsw_name, name, IFNAMSIZ); + + if ((haveswphy & IFM_HAVE_PHY) == 0) + errx(1, "no phy selected"); + + ifswr.ifsw_phy = haveswphy >> IFM_PHY_SHIFT; + if (ioctl(s, SIOCGIFSWFLAGS, (caddr_t)&ifswr) == -1) + err(1, "SIOCGIFSWFLAGS"); + + if (value < 0) { + value = -value; + ifswr.ifsw_flags &= ~value; + } else + ifswr.ifsw_flags |= value; + + if (ioctl(s, SIOCSIFSWFLAGS, (caddr_t)&ifswr) == -1) + err(1, "SIOCSIFSWFLAGS"); +} + +#define IFSWBITS \ +"\020\1STRIPVTAG\2ADDVTAG\3DEBUG\4LOOPBACK\5ISOLATE\6FIRSTLOCK\7FLOWCONTROL" \ +"\10CoS\11HIGHPRI\12DIFFSERV\13VLANBYPASS\14WANPORT" + +static void +switch_media_status(int s) +{ + struct ifmediareq ifmr, ifpmr; + int *phy_list, *media_list, i, p; + + (void) memset(&ifpmr, 0, sizeof(ifpmr)); + (void) strncpy(ifpmr.ifm_name, name, sizeof(ifpmr.ifm_name)); + + if (ioctl(s, SIOCGIFSWMEDIA, (caddr_t)&ifpmr) < 0) { + /* + * Interface doesn't support SIOC{G,S}IFSWMEDIA. + */ + return; + } + + if (ifpmr.ifm_phy == 0) { + warnx("%s: no phys?", name); + return; + } + + phy_list = (int *)malloc(ifpmr.ifm_phy * sizeof(int)); + if (phy_list == NULL) + err(1, "malloc"); + ifpmr.ifm_ulist = phy_list; + + if (ioctl(s, SIOCGIFSWMEDIA, (caddr_t)&ifpmr) < 0) + err(1, "SIOCGIFSWMEDIA"); + + if (ifpmr.ifm_phy == 0) { + warnx("%s: no phys?", name); + return; + } + + for (p = 0; p < ifpmr.ifm_phy; p++) { + + (void) memset(&ifmr, 0, sizeof(ifmr)); + (void) strncpy(ifmr.ifm_name, name, sizeof(ifmr.ifm_name)); + + ifmr.ifm_phy = phy_list[p]; + ifmr.ifm_flags |= IFM_HAVE_PHY; + + if (ioctl(s, SIOCGIFSWMEDIA, (caddr_t)&ifmr) < 0) { + /* + * Interface doesn't support SIOC{G,S}IFSWMEDIA. + * Should not fail here... + */ + return; + } + + if (ifmr.ifm_count == 0) { + warnx("%s: no media types?", name); + return; + } + + media_list = (int *)malloc(ifmr.ifm_count * sizeof(int)); + if (media_list == NULL) + err(1, "malloc"); + ifmr.ifm_ulist = media_list; + + if (ioctl(s, SIOCGIFSWMEDIA, (caddr_t)&ifmr) < 0) + err(1, "SIOCGIFSWMEDIA"); + + printf("\tphy %d ", ifmr.ifm_phy); + print_sw_flags(s, ifmr.ifm_phy); + printf("\tmedia: "); + print_media_word(ifmr.ifm_current, 1); + if (ifmr.ifm_active != ifmr.ifm_current) { + putchar(' '); + putchar('('); + print_media_word(ifmr.ifm_active, 0); + putchar(')'); + } + putchar('\n'); + + if (ifmr.ifm_status & IFM_AVALID) { + printf("\tstatus: "); + if (ifmr.ifm_status & IFM_ACTIVE) + printf("active"); + else + printf("no carrier"); + putchar('\n'); + } + + if (ifmr.ifm_count > 0 && supmedia) { + printf("\tsupported media:\n"); + for (i = 0; i < ifmr.ifm_count; i++) { + printf("\t\t"); + print_media_word_ifconfig(media_list[i]); + putchar('\n'); + } + } + + free(media_list); + } + + free(phy_list); +} + +static void +print_sw_flags(int s, int phy) +{ + struct ifswitchreq ifswr; + + memset(&ifswr, 0, sizeof(ifswr)); + strlcpy(ifswr.ifsw_name, name, IFNAMSIZ); + + ifswr.ifsw_phy = phy; + if (ioctl(s, SIOCGIFSWFLAGS, (caddr_t)&ifswr) == -1) + err(1, "SIOCGIFSWFLAGS"); + + printb("options", ifswr.ifsw_flags, IFSWBITS); + putchar('\n'); + + if ((ifswr.ifsw_flags & SW_VLANBYPASS) == 0) + print_sw_vlans(s, phy); +} + +static void +print_sw_vlans(int s, int phy) +{ + struct ifswitchreq ifswr; + int *vlan_list; + int first; + int i; + + memset(&ifswr, 0, sizeof(ifswr)); + strlcpy(ifswr.ifsw_name, name, IFNAMSIZ); + + ifswr.ifsw_phy = phy; + + if (ioctl(s, SIOCGIFSWVLAN, (caddr_t)&ifswr) == -1) + err(1, "SIOCGIFSWVLAN"); + + if (ifswr.ifsw_count == 0) { + warnx("%s: no vlans?", name); + return; + } + + vlan_list = (int *)malloc(ifswr.ifsw_count * sizeof(int)); + if (vlan_list == NULL) + err(1, "malloc"); + ifswr.ifsw_vlist = vlan_list; + + if (ioctl(s, SIOCGIFSWVLAN, (caddr_t)&ifswr) == -1) + err(1, "SIOCGIFSWVLAN"); + + if (ifswr.ifsw_count == 0) { + warnx("%s: no vlans?", name); + return; + } + + printf("\tvlans: "); + first = 1; + for (i = 1; i < ifswr.ifsw_count; i++) { + if (first == 0) + printf(", "); + else + first = 0; + printf("%d", vlan_list[i]); + /* check for default vlan */ + if (vlan_list[i] == vlan_list[0]) + printf("*"); + } + putchar('\n'); + + free(vlan_list); +} + +static struct cmd switch_cmds[] = { + DEF_CMD_ARG("phy", setswphynum), + DEF_CMD_ARG("swvlan", setswitchvlan), + DEF_CMD_ARG("-swvlan", unsetswitchvlan), + DEF_CMD_ARG("defaultvlan", setdefaultvlan), + DEF_CMD("vlanbypass", SW_VLANBYPASS, setswflags), + DEF_CMD("-vlanbypass", -SW_VLANBYPASS, setswflags), + DEF_CMD("stripvtag", SW_STRIPVTAG, setswflags), + DEF_CMD("-stripvtag", -SW_STRIPVTAG, setswflags), + DEF_CMD("addvtag", SW_ADDVTAG, setswflags), + DEF_CMD("-addvtag", -SW_ADDVTAG, setswflags), +}; + +static struct afswtch af_switch = { + .af_name = "af_switch", + .af_af = AF_UNSPEC, + .af_other_status = switch_media_status, +}; + +static __constructor void +switch_ctor(void) +{ +#define N(a) (sizeof(a) / sizeof(a[0])) + int i; + + for (i = 0; i < N(switch_cmds); i++) + cmd_register(&switch_cmds[i]); + af_register(&af_switch); +#undef N +} diff --git a/sys/conf/files b/sys/conf/files index f85df8d..c7cb488 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -102,6 +102,11 @@ pccarddevs.h standard \ compile-with "${AWK} -f $S/tools/pccarddevs2h.awk $S/dev/pccard/pccarddevs" \ no-obj no-implicit-rule before-depend \ clean "pccarddevs.h" +switchdevs.h optional switch \ + dependency "$S/tools/miidevs2h.awk $S/dev/switch/switchdevs" \ + compile-with "${AWK} -f $S/tools/miidevs2h.awk $S/dev/switch/switchdevs" \ + no-obj no-implicit-rule before-depend \ + clean "switchdevs.h" teken_state.h optional sc \ dependency "$S/teken/gensequences $S/teken/sequences" \ compile-with "${AWK} -f $S/teken/gensequences $S/teken/sequences > teken_state.h" \ @@ -1679,6 +1684,14 @@ dev/stg/tmc18c30_pci.c optional stg pci dev/stg/tmc18c30_subr.c optional stg dev/stge/if_stge.c optional stge dev/streams/streams.c optional streams +dev/switch/switch.c optional switch \ + dependency "switchbus_if.h" +dev/switch/switchbus_if.m optional switch +dev/switch/ar8316.c optional switch +dev/switch/ip175x.c optional switch +dev/switch/ukswitch.c optional switch +dev/switch/ukswitch_subr.c optional switch +dev/switch/switch_physubr.c optional switch dev/sym/sym_hipd.c optional sym \ dependency "$S/dev/sym/sym_{conf,defs}.h" dev/syscons/blank/blank_saver.c optional blank_saver diff --git a/sys/dev/switch/ar8316.c b/sys/dev/switch/ar8316.c new file mode 100644 index 0000000..14b087b --- /dev/null +++ b/sys/dev/switch/ar8316.c @@ -0,0 +1,798 @@ +/*- + * Copyright (c) 2009 Felix Fietkau + * Copyright (c) 2010 Luiz Otavio O Souza. + * 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 ``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 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. + * + */ + +#include +__FBSDID("$FreeBSD$"); + +/* + * AR8316 Switch driver + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include "switchdevs.h" +#include "switchbus_if.h" + +#define AR8316_DEBUG 0 + +struct ar8316_softc { + struct phy_softc sc_phy; + __uint32_t sc_flags; /* switch port flags */ + device_t sc_pdev; /* parent device - miibus */ +}; + +struct ar8316_vlan { + __uint32_t ports; + int vlanid; +}; + +struct ar8316_cpu_softc { + struct ar8316_vlan sc_vlan[AR8316_MAX_VLANS]; + __uint32_t sc_defaultvlan[AR8316_NUM_PORTS]; + __uint32_t sc_addvtag; + __uint32_t sc_stripvtag; + device_t sc_pdev; /* parent device - miibus */ + int sc_cpu_en; + int sc_vlan_en; +} ar8316_cpu; + +static __uint32_t ar8316_reg_read(device_t, __uint32_t); +static void ar8316_reg_write(device_t, __uint32_t, __uint32_t); +static __uint32_t ar8316_reg_update(device_t, __uint32_t, __uint32_t, + __uint32_t); +static int ar8316_verify_chip_id(struct phy_softc *); +static int ar8316_cpu_enable(struct ar8316_cpu_softc *, device_t); +static int ar8316_cpu_disable(struct ar8316_cpu_softc *); +static int ar8316_reset(struct ar8316_cpu_softc *); +static int ar8316_wait_bit(struct ar8316_cpu_softc *, int, __uint32_t, + __uint32_t); +static void ar8316_vtu_op(struct ar8316_cpu_softc *, __uint32_t, __uint32_t); +static int ar8316_set_state(struct ar8316_cpu_softc *); + +static void ar8316_reset_vlans(struct ar8316_cpu_softc *); +static int ar8316_add_vlan(struct ar8316_cpu_softc *, int, int, int); +static int ar8316_del_vlan(struct ar8316_cpu_softc *, int, int); +static int ar8316_count_vlans(struct ar8316_cpu_softc *, int); + +static int ar8316_phy_probe(device_t); +static int ar8316_phy_attach(device_t); +static int ar8316_phy_detach(device_t); +static int ar8316_phy_add_vlan(device_t, int, int, int); +static int ar8316_phy_del_vlan(device_t, int, int); +static int ar8316_phy_get_vlans(device_t, int, int *, int **); +static int ar8316_phy_get_flags(device_t, int, int *); +static int ar8316_phy_set_flags(device_t, int, int); + +static device_method_t ar8316_phy_methods[] = { + /* device interface */ + DEVMETHOD(device_probe, ar8316_phy_probe), + DEVMETHOD(device_attach, ar8316_phy_attach), + DEVMETHOD(device_detach, ar8316_phy_detach), + DEVMETHOD(device_shutdown, bus_generic_shutdown), + + /* switch bus interface */ + DEVMETHOD(switchbus_add_vlan, ar8316_phy_add_vlan), + DEVMETHOD(switchbus_del_vlan, ar8316_phy_del_vlan), + DEVMETHOD(switchbus_get_vlans, ar8316_phy_get_vlans), + DEVMETHOD(switchbus_get_flags, ar8316_phy_get_flags), + DEVMETHOD(switchbus_set_flags, ar8316_phy_set_flags), + + { 0, 0 } +}; + +static devclass_t ar8316_phy_devclass; + +static driver_t ar8316_phy_driver = { + "ar8316", + ar8316_phy_methods, + sizeof(struct ar8316_softc) +}; + +DRIVER_MODULE(ar8316, switchbus, ar8316_phy_driver, ar8316_phy_devclass, 0, 0); + +static const struct mii_phydesc ar8316s[] = { + MII_PHY_DESC(ATHEROS, AR8316), + MII_PHY_END +}; + +static int +ar8316_phy_probe(device_t dev) +{ + + return (mii_phy_dev_probe(dev, ar8316s, BUS_PROBE_DEFAULT)); +} + +static int +ar8316_phy_attach(device_t dev) +{ + struct ar8316_cpu_softc *sc_cpu = &ar8316_cpu; + struct ar8316_softc *sc; + struct phy_softc *phy; + struct phy_attach_args *pa; + struct switchbus_ivars *ivars; + struct miibus_softc *mii; + int err; + + sc = device_get_softc(dev); + pa = device_get_ivars(dev); + sc->sc_pdev = device_get_parent(dev); + mii = device_get_softc(sc->sc_pdev); + + phy = &sc->sc_phy; + LIST_INSERT_HEAD(&mii->mii_phys, phy, phy_list); + + phy->phy_pdev = sc->sc_pdev; + phy->phy_dev = dev; + phy->phy_pdata = mii; + phy->phy_num = pa->phy_num; + phy->phy_inst = mii->mii_instance; + phy->phy_service = switch_service; + phy->phy_flags |= MIIF_NOISOLATE; + + /* update the switchbus instance counter */ + mii->mii_instance++; + + mii_phy_reset(phy); + + /* init the PHY media list */ + ivars = device_get_ivars(sc->sc_pdev); + ifmedia_init(&phy->phy_media, IFM_IMASK, ivars->ifmedia_upd, + ivars->ifmedia_sts); + + /* save the PHY capabilities */ + phy->phy_capabilities = PHY_READ(phy, MII_BMSR) & pa->phy_capmask; + if (phy->phy_capabilities & BMSR_EXTSTAT) + phy->phy_extcapabilities = PHY_READ(phy, MII_EXTSR); + device_printf(dev, " "); + + /* add supported media types to PHY media list */ + mii_phy_add_media(phy); + printf("\n"); + + SWITCHBUS_MEDIAINIT(sc->sc_pdev); + + /* force the media default to (IFM_ETHER | IFM_AUTO) */ + mii_phy_setmedia(phy); + + err = ar8316_verify_chip_id(phy); + if (err) + return (err); + + /* enable the switch 'core' */ + ar8316_cpu_enable(sc_cpu, sc->sc_pdev); + + return (0); +} + +static int +ar8316_phy_detach(device_t dev) +{ + struct ar8316_softc *sc; + struct phy_softc *phy; + + sc = device_get_softc(dev); + phy = &sc->sc_phy; + + /* disable the switch 'core' */ + ar8316_cpu_disable(&ar8316_cpu); + + mii_phy_down(phy); + ifmedia_removeall(&phy->phy_media); + phy->phy_pdev = NULL; + LIST_REMOVE(phy, phy_list); + + return (0); +} + +static int +ar8316_phy_add_vlan(device_t dev, int vlanid, int flags, int phy_num) +{ + struct ar8316_cpu_softc *sc_cpu = &ar8316_cpu; + struct ar8316_softc *sc; + struct phy_softc *phy; + struct miibus_softc *mii; + int err; + + sc = device_get_softc(dev); + mii = device_get_softc(sc->sc_pdev); + phy = mii_get_phy(mii, phy_num); + if (phy != NULL && sc_cpu->sc_vlan_en) { + if ((err = ar8316_add_vlan(sc_cpu, phy_num, vlanid, flags))) + return (err); + return (ar8316_set_state(sc_cpu)); + } + return (EINVAL); +} + +static int +ar8316_phy_del_vlan(device_t dev, int vlanid, int phy_num) +{ + struct ar8316_cpu_softc *sc_cpu = &ar8316_cpu; + struct ar8316_softc *sc; + struct phy_softc *phy; + struct miibus_softc *mii; + int err; + + sc = device_get_softc(dev); + mii = device_get_softc(sc->sc_pdev); + phy = mii_get_phy(mii, phy_num); + if (phy != NULL && sc_cpu->sc_vlan_en) { + if ((err = ar8316_del_vlan(sc_cpu, phy_num, vlanid))) + return (err); + return (ar8316_set_state(sc_cpu)); + } + return (EINVAL); +} + +static int +ar8316_phy_get_vlans(device_t dev, int phy_num, int *count, int **vlist) +{ + struct ar8316_cpu_softc *sc_cpu = &ar8316_cpu; + struct ar8316_softc *sc; + struct phy_softc *phy; + struct miibus_softc *mii; + int error; + int *kptr; + int max, i, p; + + sc = device_get_softc(dev); + mii = device_get_softc(sc->sc_pdev); + phy = mii_get_phy(mii, phy_num); + if (phy == NULL) + return (EINVAL); + + if (*count == 0) { + + *count = ar8316_count_vlans(sc_cpu, phy_num) + 1; + return (0); + } else if (*count > 0) { + + max = ar8316_count_vlans(sc_cpu, phy_num) + 1; + if (*count > max) + *count = max; + kptr = (int *)malloc(*count * sizeof(int), M_TEMP, M_NOWAIT); + if (kptr == NULL) + return (ENOMEM); + + kptr[0] = sc_cpu->sc_defaultvlan[phy_num]; + for (i = 0, p = 1; i < AR8316_MAX_VLANS && p < *count; i++) { + if (sc_cpu->sc_vlan[i].vlanid == 0) + continue; + if (sc_cpu->sc_vlan[i].ports & (1 << phy_num)) + kptr[p++] = sc_cpu->sc_vlan[i].vlanid; + } + + error = copyout((caddr_t)kptr, (caddr_t)*vlist, + *count * sizeof(int)); + + free(kptr, M_TEMP); + return (error); + } + + return (EINVAL); +} + +static int +ar8316_phy_get_flags(device_t dev, int phy_num, int *flags) +{ + struct ar8316_cpu_softc *sc_cpu = &ar8316_cpu; + struct ar8316_softc *sc; + struct phy_softc *phy; + struct miibus_softc *mii; + + sc = device_get_softc(dev); + mii = device_get_softc(sc->sc_pdev); + phy = mii_get_phy(mii, phy_num); + if (phy == NULL) + return (EINVAL); + + *flags = sc->sc_flags; + if (sc_cpu->sc_vlan_en == 0) + *flags |= SW_VLANBYPASS; +#if 0 + if (IP175X_WANPORT == phy->phy_num) + *flags |= SW_WANPORT; +#endif + + return (0); +} + +static int +ar8316_phy_set_flags(device_t dev, int phy_num, int flags) +{ + struct ar8316_cpu_softc *sc_cpu = &ar8316_cpu; + struct ar8316_softc *sc; + struct phy_softc *phy; + struct miibus_softc *mii; + + sc = device_get_softc(dev); + mii = device_get_softc(sc->sc_pdev); + phy = mii_get_phy(mii, phy_num); + if (phy == NULL) + return (EINVAL); + + /* mutually exclusive */ + if (flags & SW_ADDVTAG && flags & SW_STRIPVTAG) + return (EINVAL); + + /* cannot enable addvtag or stripvtag without vlan support */ + if ((flags & SW_ADDVTAG || flags & SW_STRIPVTAG) && + sc_cpu->sc_vlan_en == 0) + return (EINVAL); + + /* reset the tag setting for this port */ + sc_cpu->sc_addvtag &= ~(1 << phy->phy_num); + sc_cpu->sc_stripvtag &= ~(1 << phy->phy_num); + + /* and then set it to the new value */ + if (flags & SW_ADDVTAG) + sc_cpu->sc_addvtag |= (1 << phy->phy_num); + if (flags & SW_STRIPVTAG) + sc_cpu->sc_stripvtag |= (1 << phy->phy_num); + + /* disable the 802.1q vlan support - bypass all vlans */ + if (flags & SW_VLANBYPASS && sc_cpu->sc_vlan_en == 1) { + sc_cpu->sc_vlan_en = 0; + ar8316_reset_vlans(sc_cpu); + } + + /* enable the 802.1q vlan support */ + if ((flags & SW_VLANBYPASS) == 0 && sc_cpu->sc_vlan_en == 0) + sc_cpu->sc_vlan_en = 1; + + /* ignore global flags */ + sc->sc_flags = flags & ~(SW_WANPORT | SW_VLANBYPASS); + + return (ar8316_set_state(sc_cpu)); +} + +/* + * Enable the switch 'core'. + */ +static int +ar8316_cpu_enable(struct ar8316_cpu_softc *sc, device_t pdev) +{ + __uint32_t mode; + + /* cpu already enabled ? */ + if (sc->sc_cpu_en) + return (0); + + sc->sc_pdev = pdev; + device_printf(sc->sc_pdev, "enabling the switch core\n"); + + mode = ar8316_reg_read(sc->sc_pdev, 0x8); + /* + * Already initialized by bootloader. + * Values taken from Ubiquiti RouterStation Pro (0x81461bea - rgmii) + * and from AVM Fritz!Box 7390 sources (0x010e5b71 - gmii). + * We only support rgmii atm. + */ + if (mode != 0x010e5b71 && mode != 0x81461bea) + ar8316_reg_write(sc->sc_pdev, 0x8, 0x81461bea); + + /* standard atheros magic */ + ar8316_reg_write(sc->sc_pdev, 0x38, AR8X16_MAGIC); + + /* set cpu as enabled */ + sc->sc_cpu_en = 1; + + /* reset the switch */ + ar8316_reset(sc); + + return (0); +} + +/* + * Disable the switch 'core'. + */ +static int +ar8316_cpu_disable(struct ar8316_cpu_softc *sc) +{ + + /* is the cpu enabled ? */ + if (sc->sc_cpu_en == 0) + return (0); + + /* set cpu as disabled */ + sc->sc_cpu_en = 0; + + /* reset the switch */ + ar8316_reset(sc); + + return (0); +} + +/* + * Reset the ar8316 switch + */ +static int +ar8316_reset(struct ar8316_cpu_softc *sc) +{ + int i; + + /* XXX - mtx_lock() */ + + /* bypass all vlans */ + sc->sc_vlan_en = 0; + ar8316_reset_vlans(sc); + + for (i = 0; i < AR8316_NUM_PORTS; i++) { + /* Enable port learning and tx */ + ar8316_reg_write(sc->sc_pdev, AR8316_REG_PORT_CTRL(i), + AR8316_PORT_CTRL_LEARN | + (AR8316_PORT_STATE_FORWARD << + AR8316_PORT_CTRL_STATE_SHIFT)); + + /* Flush all vlan settings */ + ar8316_reg_write(sc->sc_pdev, AR8316_REG_PORT_VLAN(i), 0); + + if (i != AR8316_CPU_PORT) + continue; + + /* Configure the CPU port - let the PHYs to be set on attach */ + ar8316_reg_write(sc->sc_pdev, AR8316_REG_PORT_STATUS(i), + AR8316_PORT_STATUS_LINK_UP | AR8316_PORT_SPEED_1000M | + AR8316_PORT_STATUS_TXMAC | AR8316_PORT_STATUS_RXMAC | + AR8316_PORT_STATUS_RXFLOW | AR8316_PORT_STATUS_TXFLOW | + AR8316_PORT_STATUS_DUPLEX); + } + + /* standard atheros magic */ + ar8316_reg_write(sc->sc_pdev, 0x38, AR8X16_MAGIC); + + /* enable jumbo frames */ + ar8316_reg_update(sc->sc_pdev, AR8316_REG_GLOBAL_CTRL, + AR8316_GCTRL_MTU, 9018 + 8 + 2); + + /* enable cpu port to receive multicast and broadcast frames */ + ar8316_reg_write(sc->sc_pdev, AR8316_REG_FLOOD_MASK, + AR8316_FM_UNI_DEST_PORTS | AR8316_FM_MULTI_DEST_PORTS); + + /* XXX - mtx_unlock() */ + + /* update all settings */ + ar8316_set_state(sc); + + return (0); +} + +static int +ar8316_wait_bit(struct ar8316_cpu_softc *sc, int reg, __uint32_t mask, + __uint32_t val) +{ + int timeout = 20; + + while ((ar8316_reg_read(sc->sc_pdev, reg) & mask) != val) { + if (timeout-- <= 0) { + device_printf(sc->sc_pdev, + "timeout waiting for operation to complete\n"); + return (1); + } + } + return (0); +} + +static void +ar8316_vtu_op(struct ar8316_cpu_softc *sc, __uint32_t op, __uint32_t val) +{ + + if (ar8316_wait_bit(sc, AR8316_REG_VTU, AR8316_VTU_ACTIVE, 0)) + return; + if ((op & AR8316_VTU_OP) == AR8316_VTU_OP_LOAD) { + val &= AR8316_VTUDATA_MEMBER; + val |= AR8316_VTUDATA_VALID; + ar8316_reg_write(sc->sc_pdev, AR8316_REG_VTU_DATA, val); + } + op |= AR8316_VTU_ACTIVE; + ar8316_reg_write(sc->sc_pdev, AR8316_REG_VTU, op); +} + +static int +ar8316_set_state(struct ar8316_cpu_softc *sc) +{ + struct ar8316_vlan *v; + __uint8_t portmask[AR8316_NUM_PORTS]; + int i, j; + + memset(portmask, 0, sizeof(portmask)); + + /* XXX - mtx_lock() */ + + /* flush all vlan translation unit entries */ + ar8316_vtu_op(sc, AR8316_VTU_OP_FLUSH, 0); + + if (sc->sc_vlan_en) { + /* + * calculate the port destination masks and load vlans + * into the vlan translation unit + */ + for (j = 0; j < AR8316_MAX_VLANS; j++) { + + v = &sc->sc_vlan[j]; + if (v->vlanid == 0) + continue; + + /* add the vlan id to vlan translation unit */ + ar8316_vtu_op(sc, AR8316_VTU_OP_LOAD | + (v->vlanid << AR8316_VTU_VID_SHIFT), + (v->ports << 1) | (1 << AR8316_CPU_PORT)); + } + } else { + /* vlan disabled. connect all ports together */ + for (i = 0; i < AR8316_NUM_PORTS; i++) + portmask[i] |= ~(1 << i); + } + + /* update the port destination mask registers and tag settings */ + for (i = 0; i < AR8316_NUM_PORTS; i++) { + int egress = AR8316_OUT_KEEP; + int pvid = 1; + int ingress; + + if (i > AR8316_CPU_PORT) { + pvid = sc->sc_defaultvlan[i - 1]; + + /* egress filters */ + if (sc->sc_addvtag & (1 << (i - 1))) + egress = AR8316_OUT_ADD_VLAN; + if (sc->sc_stripvtag & (1 << (i - 1))) + egress = AR8316_OUT_STRIP_VLAN; + } + + /* ingress filters */ + if (sc->sc_vlan_en) + ingress = AR8316_IN_SECURE; + else + ingress = AR8316_IN_PORT_ONLY; + + ar8316_reg_update(sc->sc_pdev, AR8316_REG_PORT_CTRL(i), + AR8316_PORT_CTRL_LEARN | AR8316_PORT_CTRL_VLAN_MODE | + AR8316_PORT_CTRL_SINGLE_VLAN | AR8316_PORT_CTRL_STATE | + AR8316_PORT_CTRL_HEADER | AR8316_PORT_CTRL_LEARN_LOCK, + AR8316_PORT_CTRL_LEARN | + (egress << AR8316_PORT_CTRL_VLAN_MODE_SHIFT) | + (AR8316_PORT_STATE_FORWARD << + AR8316_PORT_CTRL_STATE_SHIFT)); + + ar8316_reg_update(sc->sc_pdev, AR8316_REG_PORT_VLAN(i), + AR8316_PORT_VLAN_DEST_PORTS | AR8316_PORT_VLAN_MODE | + AR8316_PORT_VLAN_DEFAULT_ID, + (portmask[i] << AR8316_PORT_VLAN_DEST_PORTS_SHIFT) | + (ingress << AR8316_PORT_VLAN_MODE_SHIFT) | + (pvid << AR8316_PORT_VLAN_DEFAULT_ID_SHIFT)); + } + + /* XXX - mtx_unlock() */ + + return (0); +} + +static void +ar8316_reset_vlans(struct ar8316_cpu_softc *sc) +{ + struct ar8316_vlan *v; + int i; + + /* reset all the vlan data */ + memset(sc->sc_vlan, 0, sizeof(sc->sc_vlan)); + memset(sc->sc_defaultvlan, 0, sizeof(sc->sc_defaultvlan)); + sc->sc_addvtag = 0; + sc->sc_stripvtag = 0; + + /* set the default vlan (1) */ + v = &sc->sc_vlan[0]; + v->vlanid = 1; + for (i = 0; i < AR8316_NUM_PORTS; i++) { + sc->sc_defaultvlan[i] = v->vlanid; + v->ports |= (1 << i); + } +} + +static int +ar8316_add_vlan(struct ar8316_cpu_softc *sc, int phy, int vlan, int dflt) +{ + struct ar8316_vlan *v; + int i; + + /* check if vlan is not already set */ + v = NULL; + for (i = 0; i < AR8316_MAX_VLANS; i++) { + if (sc->sc_vlan[i].vlanid == vlan) { + v = &sc->sc_vlan[i]; + break; + } + } + + /* if not found try a find a free one */ + if (v == NULL) { + for (i = 0; i < AR8316_MAX_VLANS; i++) { + if (sc->sc_vlan[i].vlanid == 0) { + v = &sc->sc_vlan[i]; + break; + } + } + } + + /* no free vlan found */ + if (v == NULL) + return (EINVAL); + + v->vlanid = vlan; + v->ports |= (1 << phy); + if (dflt) + sc->sc_defaultvlan[phy] = vlan; + + return (0); +} + +static int +ar8316_del_vlan(struct ar8316_cpu_softc *sc, int phy, int vlan) +{ + struct ar8316_vlan *v; + int i; + + /* find the vlan */ + v = NULL; + for (i = 0; i < AR8316_MAX_VLANS; i++) { + if (sc->sc_vlan[i].vlanid == vlan) { + v = &sc->sc_vlan[i]; + break; + } + } + + /* no vlan found */ + if (v == NULL) + return (EINVAL); + + /* cannot remove the default vlan */ + if (sc->sc_defaultvlan[phy] == vlan) + return (EINVAL); + + v->ports &= ~(1 << phy); + if (v->ports == 0) + v->vlanid = 0; + + return (0); +} + +static int +ar8316_count_vlans(struct ar8316_cpu_softc *sc, int phy) +{ + int i, count; + + count = 0; + for (i = 0; i < AR8316_MAX_VLANS; i++) { + if (sc->sc_vlan[i].vlanid == 0) + continue; + if (sc->sc_vlan[i].ports & (1 << phy)) + count++; + } + return (count); +} + +static inline void +split_addr(__uint32_t reg, __uint16_t *r1, __uint16_t *r2, __uint16_t *page) +{ + + reg >>= 1; + *r1 = reg & 0x1e; + + reg >>= 5; + *r2 = reg & 0x7; + + reg >>= 3; + *page = reg & 0x1ff; +} + +static __uint32_t +ar8316_reg_read(device_t miibus, __uint32_t reg) +{ + __uint16_t r1, r2, page; + __uint16_t lo, hi; + + split_addr(reg, &r1, &r2, &page); + SWITCHBUS_WRITEREG(miibus, 0x18, 0, page); + /* wait for the page switch to propagate */ + DELAY(1); + + lo = SWITCHBUS_READREG(miibus, 0x10 | r2, r1); + hi = SWITCHBUS_READREG(miibus, 0x10 | r2, r1 + 1); + return ((hi << 16) | lo); +} + +static void +ar8316_reg_write(device_t miibus, __uint32_t reg, __uint32_t val) +{ + __uint16_t r1, r2, page; + + split_addr(reg, &r1, &r2, &page); + SWITCHBUS_WRITEREG(miibus, 0x18, 0, page); + /* wait for the page switch to propagate */ + DELAY(1); + + SWITCHBUS_WRITEREG(miibus, 0x10 | r2, r1 + 1, val >> 16); + SWITCHBUS_WRITEREG(miibus, 0x10 | r2, r1, val & 0xffff); +} + +static __uint32_t +ar8316_reg_update(device_t miibus, __uint32_t reg, __uint32_t mask, + __uint32_t val) +{ + __uint32_t v; + + v = ar8316_reg_read(miibus, reg); + v &= ~mask; + v |= val; + ar8316_reg_write(miibus, reg, v); + return (v); +} + +static int +ar8316_verify_chip_id(struct phy_softc *sc) +{ + struct phy_attach_args *pa; + __uint32_t val; + __uint16_t id; + + val = ar8316_reg_read(sc->phy_pdev, AR8316_REG_CTRL); + if (val == ~0) + return (ENXIO); + + id = val & (AR8316_CTRL_REV | AR8316_CTRL_VER); + switch (id) { + case 0x1001: + /* AR8316 */ + return (0); + case 0x0101: + /* AR8216 - not supported yet */ + default: + pa = device_get_ivars(sc->phy_dev); + device_printf(sc->phy_dev, + "Unknown Atheros device " + "[ver=%d, rev=%d, phy_id=%04x%04x]\n", + (int)(id >> AR8316_CTRL_VER_SHIFT), + (int)(id & AR8316_CTRL_REV), + pa->phy_id1, pa->phy_id2); + + return (ENXIO); + } +} + diff --git a/sys/dev/switch/ar8316var.h b/sys/dev/switch/ar8316var.h new file mode 100644 index 0000000..554bcf5 --- /dev/null +++ b/sys/dev/switch/ar8316var.h @@ -0,0 +1,134 @@ +/*- + * Copyright (c) 2009 Felix Fietkau + * Copyright (c) 2010 Luiz Otavio O Souza. + * 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 ``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 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. + * + */ + +#ifndef _DEV_SWITCH_AR8316VAR_H_ +#define _DEV_SWITCH_AR8316VAR_H_ + +#define AR8316_CPU_PORT 0 +#define AR8316_NUM_PORTS 6 +#define AR8316_MAX_VLANS 128 +#define AR8X16_MAGIC 0xc000050e + +#define AR8316_REG_CTRL 0x0000 +#define AR8316_CTRL_REV 0x000000ff +#define AR8316_CTRL_REV_SHIFT 0 +#define AR8316_CTRL_VER 0x0000ff00 +#define AR8316_CTRL_VER_SHIFT 8 +#define AR8316_CTRL_RESET (1 << 31) + +#define AR8316_REG_FLOOD_MASK 0x002C +#define AR8316_FM_UNI_DEST_PORTS 0x0000003f +#define AR8316_FM_MULTI_DEST_PORTS 0x003f0000 + +#define AR8316_REG_GLOBAL_CTRL 0x0030 +#define AR8316_GCTRL_MTU 0x00003fff + +#define AR8316_REG_VTU 0x0040 +#define AR8316_VTU_OP 0x00000007 +#define AR8316_VTU_OP_NOOP 0x0 +#define AR8316_VTU_OP_FLUSH 0x1 +#define AR8316_VTU_OP_LOAD 0x2 +#define AR8316_VTU_OP_PURGE 0x3 +#define AR8316_VTU_OP_REMOVE_PORT 0x4 +#define AR8316_VTU_ACTIVE (1 << 3) +#define AR8316_VTU_FULL (1 << 4) +#define AR8316_VTU_PORT 0x00000f00 +#define AR8316_VTU_PORT_SHIFT 8 +#define AR8316_VTU_VID 0x0fff0000 +#define AR8316_VTU_VID_SHIFT 16 +#define AR8316_VTU_PRIO 0x70000000 +#define AR8316_VTU_PRIO_SHIFT 28 +#define AR8316_VTU_PRIO_EN (1 << 31) + +#define AR8316_REG_VTU_DATA 0x0044 +#define AR8316_VTUDATA_MEMBER 0x000003ff +#define AR8316_VTUDATA_VALID (1 << 11) + +#define AR8316_PORT_OFFSET(_i) (0x0100 * (_i + 1)) +#define AR8316_REG_PORT_STATUS(_i) (AR8316_PORT_OFFSET(_i) + 0) +#define AR8316_PORT_STATUS_SPEED 0x00000003 +#define AR8316_PORT_STATUS_SPEED_SHIFT 0 +#define AR8316_PORT_STATUS_TXMAC (1 << 2) +#define AR8316_PORT_STATUS_RXMAC (1 << 3) +#define AR8316_PORT_STATUS_TXFLOW (1 << 4) +#define AR8316_PORT_STATUS_RXFLOW (1 << 5) +#define AR8316_PORT_STATUS_DUPLEX (1 << 6) +#define AR8316_PORT_STATUS_LINK_UP (1 << 8) +#define AR8316_PORT_STATUS_LINK_AUTO (1 << 9) +#define AR8316_PORT_STATUS_LINK_PAUSE (1 << 10) + +#define AR8316_PORT_SPEED_10M 0 +#define AR8316_PORT_SPEED_100M 1 +#define AR8316_PORT_SPEED_1000M 2 +#define AR8316_PORT_SPEED_ERR 3 + +#define AR8316_REG_PORT_CTRL(_i) (AR8316_PORT_OFFSET(_i) + 4) + +/* port forwarding state */ +#define AR8316_PORT_CTRL_STATE 0x00000007 +#define AR8316_PORT_CTRL_STATE_SHIFT 0 + +#define AR8316_PORT_CTRL_LEARN_LOCK (1 << 7) + +/* egress 802.1q mode */ +#define AR8316_PORT_CTRL_VLAN_MODE 0x00000300 +#define AR8316_PORT_CTRL_VLAN_MODE_SHIFT 8 +#define AR8316_OUT_KEEP 0 +#define AR8316_OUT_STRIP_VLAN 1 +#define AR8316_OUT_ADD_VLAN 2 + +#define AR8316_PORT_CTRL_IGMP_SNOOP (1 << 10) +#define AR8316_PORT_CTRL_HEADER (1 << 11) +#define AR8316_PORT_CTRL_MAC_LOOP (1 << 12) +#define AR8316_PORT_CTRL_SINGLE_VLAN (1 << 13) +#define AR8316_PORT_CTRL_LEARN (1 << 14) +#define AR8316_PORT_CTRL_MIRROR_TX (1 << 16) +#define AR8316_PORT_CTRL_MIRROR_RX (1 << 17) + +#define AR8316_REG_PORT_VLAN(_i) (AR8316_PORT_OFFSET(_i) + 8) + +#define AR8316_PORT_VLAN_DEFAULT_ID 0x00000fff +#define AR8316_PORT_VLAN_DEFAULT_ID_SHIFT 0 + +#define AR8316_PORT_VLAN_DEST_PORTS 0x01ff0000 +#define AR8316_PORT_VLAN_DEST_PORTS_SHIFT 16 + +/* ingress 802.1q mode */ +#define AR8316_PORT_VLAN_MODE 0xc0000000 +#define AR8316_PORT_VLAN_MODE_SHIFT 30 +#define AR8316_IN_PORT_ONLY 0 +#define AR8316_IN_PORT_FALLBACK 1 +#define AR8316_IN_VLAN_ONLY 2 +#define AR8316_IN_SECURE 3 + +#define AR8316_PORT_STATE_DISABLED 0 +#define AR8316_PORT_STATE_BLOCK 1 +#define AR8316_PORT_STATE_LISTEN 2 +#define AR8316_PORT_STATE_LEARN 3 +#define AR8316_PORT_STATE_FORWARD 4 + +#endif /* _DEV_SWITCH_AR8316VAR_H_ */ diff --git a/sys/dev/switch/ip175x.c b/sys/dev/switch/ip175x.c new file mode 100644 index 0000000..6f8e59b --- /dev/null +++ b/sys/dev/switch/ip175x.c @@ -0,0 +1,772 @@ +/*- + * Copyright (c) 2010 Luiz Otavio O Souza. + * 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 ``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 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. + * + */ + +#include +__FBSDID("$FreeBSD$"); + +/* + * IP175x Switch driver + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include "switchdevs.h" +#include "switchbus_if.h" + +#define IP175X_DEBUG 0 +#define IP175X_NUM_PORTS 6 +#define IP175X_CPU_PORT 5 +#define IP175X_WAN_PORT 4 +#define IP175X_MAX_VLANS 16 + +struct ip175x_softc { + struct phy_softc sc_phy; + __uint32_t sc_flags; /* switch port flags */ + device_t sc_pdev; /* parent device - miibus */ +}; + +struct ip175x_vlan { + __uint32_t ports; + int vlanid; +}; + +struct ip175x_cpu_softc_def; + +struct ip175x_cpu_softc { + struct ip175x_func_def *sc_func; + struct ip175x_vlan sc_vlan[IP175X_MAX_VLANS]; + __uint32_t sc_defaultvlan[IP175X_NUM_PORTS]; + __uint32_t sc_addvtag; + __uint32_t sc_stripvtag; + device_t sc_pdev; /* parent device - miibus */ + int sc_cpu_en; + int sc_vlan_en; +} ip175x_cpu; + +struct ip175x_func_def { + int (* ip175x_reset) (struct ip175x_cpu_softc *); + int (* ip175x_set_state) (struct ip175x_cpu_softc *); + int (* ip175x_set_vlan_mode) (struct ip175x_cpu_softc *); + int ip175x_reset_phy; + int ip175x_reset_reg; + int ip175x_reset_val; + int ip175x_mode_phy; + int ip175x_mode_reg; + int ip175x_mode_val; +}; + +static int ip175x_cpu_enable(struct ip175x_cpu_softc *, device_t); +static int ip175x_cpu_disable(struct ip175x_cpu_softc *); +static void ip175x_reset_vlans(struct ip175x_cpu_softc *); +static int ip175x_add_vlan(struct ip175x_cpu_softc *, int, int, int); +static int ip175x_del_vlan(struct ip175x_cpu_softc *, int, int); +static int ip175x_count_vlans(struct ip175x_cpu_softc *, int); +static int ip175x_reset(struct ip175x_cpu_softc *); +static int ip175x_updatereg(struct ip175x_cpu_softc *, __uint16_t, __uint16_t, + __uint16_t, __uint16_t); + +static int ip175x_phy_probe(device_t); +static int ip175x_phy_attach(device_t); +static int ip175x_phy_detach(device_t); +static int ip175x_phy_add_vlan(device_t, int, int, int); +static int ip175x_phy_del_vlan(device_t, int, int); +static int ip175x_phy_get_vlans(device_t, int, int *, int **); +static int ip175x_phy_get_flags(device_t, int, int *); +static int ip175x_phy_set_flags(device_t, int, int); + +static device_method_t ip175x_phy_methods[] = { + /* device interface */ + DEVMETHOD(device_probe, ip175x_phy_probe), + DEVMETHOD(device_attach, ip175x_phy_attach), + DEVMETHOD(device_detach, ip175x_phy_detach), + DEVMETHOD(device_shutdown, bus_generic_shutdown), + + /* switch bus interface */ + DEVMETHOD(switchbus_add_vlan, ip175x_phy_add_vlan), + DEVMETHOD(switchbus_del_vlan, ip175x_phy_del_vlan), + DEVMETHOD(switchbus_get_vlans, ip175x_phy_get_vlans), + DEVMETHOD(switchbus_get_flags, ip175x_phy_get_flags), + DEVMETHOD(switchbus_set_flags, ip175x_phy_set_flags), + + { 0, 0 } +}; + +static devclass_t ip175x_phy_devclass; + +static driver_t ip175x_phy_driver = { + "ip175x", + ip175x_phy_methods, + sizeof(struct ip175x_softc) +}; + +DRIVER_MODULE(ip175x, switchbus, ip175x_phy_driver, ip175x_phy_devclass, 0, 0); + +static const struct mii_phydesc ip175xs[] = { + MII_PHY_DESC(ICPLUS, IP175x), + MII_PHY_END +}; + +static int +ip175x_phy_probe(device_t dev) +{ + + return (mii_phy_dev_probe(dev, ip175xs, BUS_PROBE_DEFAULT)); +} + +static int +ip175x_phy_attach(device_t dev) +{ + struct ip175x_cpu_softc *sc_cpu = &ip175x_cpu; + struct ip175x_softc *sc; + struct phy_softc *phy; + struct phy_attach_args *pa; + struct switchbus_ivars *ivars; + struct miibus_softc *mii; + int err; + + sc = device_get_softc(dev); + pa = device_get_ivars(dev); + sc->sc_pdev = device_get_parent(dev); + mii = device_get_softc(sc->sc_pdev); + + phy = &sc->sc_phy; + LIST_INSERT_HEAD(&mii->mii_phys, phy, phy_list); + + phy->phy_pdev = sc->sc_pdev; + phy->phy_dev = dev; + phy->phy_pdata = mii; + phy->phy_num = pa->phy_num; + phy->phy_inst = mii->mii_instance; + phy->phy_service = switch_service; + phy->phy_flags |= MIIF_NOISOLATE; + + /* update the switchbus instance counter */ + mii->mii_instance++; + + mii_phy_reset(phy); + + /* init the PHY media list */ + ivars = device_get_ivars(sc->sc_pdev); + ifmedia_init(&phy->phy_media, IFM_IMASK, ivars->ifmedia_upd, + ivars->ifmedia_sts); + + /* save the PHY capabilities */ + phy->phy_capabilities = PHY_READ(phy, MII_BMSR) & pa->phy_capmask; + if (phy->phy_capabilities & BMSR_EXTSTAT) + phy->phy_extcapabilities = PHY_READ(phy, MII_EXTSR); + device_printf(dev, " "); + + /* add supported media types to PHY media list */ + mii_phy_add_media(phy); + printf("\n"); + + SWITCHBUS_MEDIAINIT(sc->sc_pdev); + + /* force the media default to (IFM_ETHER | IFM_AUTO) */ + mii_phy_setmedia(phy); + + /* enable the switch 'core' */ + ip175x_cpu_enable(sc_cpu, sc->sc_pdev); + + /* add the default vlan (1) */ + err = ip175x_add_vlan(sc_cpu, phy->phy_num, 1, 1); + if (err) + return (err); + + /* set the default vlan for cpu port */ + sc_cpu->sc_defaultvlan[IP175X_CPU_PORT] = 1; + + return (0); +} + +static int +ip175x_phy_detach(device_t dev) +{ + struct ip175x_softc *sc; + struct phy_softc *phy; + + sc = device_get_softc(dev); + phy = &sc->sc_phy; + + /* disable the switch 'core' */ + ip175x_cpu_disable(&ip175x_cpu); + + mii_phy_down(phy); + ifmedia_removeall(&phy->phy_media); + phy->phy_pdev = NULL; + LIST_REMOVE(phy, phy_list); + + return (0); +} + +static int +ip175x_phy_add_vlan(device_t dev, int vlanid, int flags, int phy_num) +{ + struct ip175x_cpu_softc *sc_cpu = &ip175x_cpu; + struct ip175x_softc *sc; + struct phy_softc *phy; + struct miibus_softc *mii; + int err; + + sc = device_get_softc(dev); + mii = device_get_softc(sc->sc_pdev); + phy = mii_get_phy(mii, phy_num); + if (phy != NULL && sc_cpu->sc_vlan_en) { + if ((err = ip175x_add_vlan(sc_cpu, phy_num, vlanid, flags))) + return (err); + return (sc_cpu->sc_func->ip175x_set_state(sc_cpu)); + } + return (EINVAL); +} + +static int +ip175x_phy_del_vlan(device_t dev, int vlanid, int phy_num) +{ + struct ip175x_cpu_softc *sc_cpu = &ip175x_cpu; + struct ip175x_softc *sc; + struct phy_softc *phy; + struct miibus_softc *mii; + int err; + + sc = device_get_softc(dev); + mii = device_get_softc(sc->sc_pdev); + phy = mii_get_phy(mii, phy_num); + if (phy != NULL && sc_cpu->sc_vlan_en) { + if ((err = ip175x_del_vlan(sc_cpu, phy_num, vlanid))) + return (err); + return (sc_cpu->sc_func->ip175x_set_state(sc_cpu)); + } + return (EINVAL); +} + +static int +ip175x_phy_get_vlans(device_t dev, int phy_num, int *count, int **vlist) +{ + struct ip175x_cpu_softc *sc_cpu = &ip175x_cpu; + struct ip175x_softc *sc; + struct phy_softc *phy; + struct miibus_softc *mii; + int error; + int *kptr; + int max, i, p; + + sc = device_get_softc(dev); + mii = device_get_softc(sc->sc_pdev); + phy = mii_get_phy(mii, phy_num); + if (phy == NULL) + return (EINVAL); + + if (*count == 0) { + + *count = ip175x_count_vlans(sc_cpu, phy_num) + 1; + return (0); + } else if (*count > 0) { + + max = ip175x_count_vlans(sc_cpu, phy_num) + 1; + if (*count > max) + *count = max; + kptr = (int *)malloc(*count * sizeof(int), M_TEMP, M_NOWAIT); + if (kptr == NULL) + return (ENOMEM); + + kptr[0] = sc_cpu->sc_defaultvlan[phy_num]; + for (i = 0, p = 1; i < IP175X_MAX_VLANS && p < *count; i++) { + if (sc_cpu->sc_vlan[i].vlanid == 0) + continue; + if (sc_cpu->sc_vlan[i].ports & (1 << phy_num)) + kptr[p++] = sc_cpu->sc_vlan[i].vlanid; + } + + error = copyout((caddr_t)kptr, (caddr_t)*vlist, + *count * sizeof(int)); + + free(kptr, M_TEMP); + return (error); + } + + return (EINVAL); +} + +static int +ip175x_phy_get_flags(device_t dev, int phy_num, int *flags) +{ + struct ip175x_cpu_softc *sc_cpu = &ip175x_cpu; + struct ip175x_softc *sc; + struct phy_softc *phy; + struct miibus_softc *mii; + + sc = device_get_softc(dev); + mii = device_get_softc(sc->sc_pdev); + phy = mii_get_phy(mii, phy_num); + if (phy == NULL) + return (EINVAL); + + *flags = sc->sc_flags; + if (sc_cpu->sc_vlan_en == 0) + *flags |= SW_VLANBYPASS; + if (IP175X_WAN_PORT == phy->phy_num) + *flags |= SW_WANPORT; + + return (0); +} + +static int +ip175x_phy_set_flags(device_t dev, int phy_num, int flags) +{ + struct ip175x_cpu_softc *sc_cpu = &ip175x_cpu; + struct ip175x_softc *sc; + struct phy_softc *phy; + struct miibus_softc *mii; + + sc = device_get_softc(dev); + mii = device_get_softc(sc->sc_pdev); + phy = mii_get_phy(mii, phy_num); + if (phy == NULL) + return (EINVAL); + + /* mutually exclusive */ + if (flags & SW_ADDVTAG && flags & SW_STRIPVTAG) + return (EINVAL); + + /* cannot enable addvtag or stripvtag without vlan support */ + if ((flags & SW_ADDVTAG || flags & SW_STRIPVTAG) && + sc_cpu->sc_vlan_en == 0) + return (EINVAL); + + /* reset the tag setting for this port */ + sc_cpu->sc_addvtag &= ~(1 << phy->phy_num); + sc_cpu->sc_stripvtag &= ~(1 << phy->phy_num); + + /* and then set it to the new value */ + if (flags & SW_ADDVTAG) + sc_cpu->sc_addvtag |= (1 << phy->phy_num); + if (flags & SW_STRIPVTAG) + sc_cpu->sc_stripvtag |= (1 << phy->phy_num); + + /* disable the 802.1q vlan support - bypass all vlans */ + if (flags & SW_VLANBYPASS && sc_cpu->sc_vlan_en == 1) { + sc_cpu->sc_vlan_en = 0; + ip175x_reset_vlans(sc_cpu); + sc_cpu->sc_func->ip175x_set_vlan_mode(sc_cpu); + } + + /* enable the 802.1q vlan support */ + if ((flags & SW_VLANBYPASS) == 0 && sc_cpu->sc_vlan_en == 0) { + sc_cpu->sc_vlan_en = 1; + sc_cpu->sc_func->ip175x_set_vlan_mode(sc_cpu); + } + + /* ignore global flags */ + sc->sc_flags = flags & ~(SW_WANPORT | SW_VLANBYPASS); + + return (sc_cpu->sc_func->ip175x_set_state(sc_cpu)); +} + +/* + * IP175C + */ + +static int +ip175c_reset(struct ip175x_cpu_softc *sc) +{ + +printf("%s: called\n", __func__); + if (sc->sc_func->ip175x_mode_phy != -1) { + SWITCHBUS_WRITEREG(sc->sc_pdev, sc->sc_func->ip175x_mode_phy, + sc->sc_func->ip175x_mode_reg, + sc->sc_func->ip175x_mode_val); + } + return (0); +} + +static int +ip175c_set_state(struct ip175x_cpu_softc *sc) +{ + +printf("%s: called\n", __func__); + return (0); +} + +static int +ip175c_set_vlan_mode(struct ip175x_cpu_softc *sc) +{ + +printf("%s: called\n", __func__); + if (sc->sc_vlan_en) + ip175x_updatereg(sc, 30, 9, 0x0080, 0x0080); + else + ip175x_updatereg(sc, 30, 9, 0x0080, 0x0000); + + return (0); +} + +struct ip175x_func_def ip175c_func_def = { + &ip175c_reset, + &ip175c_set_state, + &ip175c_set_vlan_mode, + 30, /* reset phy */ + 0, /* reset reg */ + 0x175c, /* reset val */ + 29, /* mode phy */ + 31, /* mode reg */ + 0x175c, /* mode val */ +}; + +/* + * IP175D + */ + +static int +ip175d_reset(struct ip175x_cpu_softc *sc) +{ + +printf("%s: called\n", __func__); + /* Disable the special tagging mode */ + ip175x_updatereg(sc, 21, 22, 0x0003, 0x0000); + + /* Set 802.1q protocol type */ + SWITCHBUS_WRITEREG(sc->sc_pdev, 22, 3, 0x8100); + + return (0); +} + +static int +ip175d_set_state(struct ip175x_cpu_softc *sc) +{ + struct ip175x_cpu_softc *sc_cpu = &ip175x_cpu; + struct ip175x_vlan *v; + __uint32_t ports[IP175X_MAX_VLANS]; + __uint32_t addtag[IP175X_MAX_VLANS]; + __uint32_t striptag[IP175X_MAX_VLANS]; + __uint32_t vlan_mask; + int i, j; + +printf("%s: called\n", __func__); + vlan_mask = 0; + for (i = 0; i < IP175X_MAX_VLANS; i++) { + + ports[i] = 0; + addtag[i] = 0; + striptag[i] = 0; + + v = &sc_cpu->sc_vlan[i]; + if (v->vlanid == 0) { + /* reset the filter */ + SWITCHBUS_WRITEREG(sc_cpu->sc_pdev, 22, 14 + i, 0); + ports[i] = 0x3f; + continue; + } + + vlan_mask |= (1 << i); + SWITCHBUS_WRITEREG(sc_cpu->sc_pdev, 22, 14 + i, v->vlanid); + ports[i] = v->ports | (1 << IP175X_CPU_PORT); + + for (j = 0; j < IP175X_NUM_PORTS; j++) { + if ((ports[i] & (1 << j)) == 0) + continue; + if (sc_cpu->sc_addvtag & (1 << j)) + addtag[i] |= (1 << j); + if (sc_cpu->sc_stripvtag & (1 << j)) + striptag[i] |= (1 << j); + } + } + + /* Port masks, tag adds and removals */ + for (i = 0; i < IP175X_MAX_VLANS / 2; i++) { + SWITCHBUS_WRITEREG(sc_cpu->sc_pdev, 23, i, + ports[2 * i] | (ports[2 * i + 1] << 8)); + SWITCHBUS_WRITEREG(sc_cpu->sc_pdev, 23, i + 8, + addtag[2 * i] | (addtag[2 * i + 1] << 8)); + SWITCHBUS_WRITEREG(sc_cpu->sc_pdev, 23, i + 16, + striptag[2 * i] | (striptag[2 * i + 1] << 8)); + } + SWITCHBUS_WRITEREG(sc_cpu->sc_pdev, 22, 10, vlan_mask); + + /* default vlan for each port */ + for (i = 0; i < IP175X_NUM_PORTS; i++) { + SWITCHBUS_WRITEREG(sc_cpu->sc_pdev, 22, 4 + i, + sc->sc_defaultvlan[i]); + } + + return (0); +} + +static int +ip175d_set_vlan_mode(struct ip175x_cpu_softc *sc) +{ + +printf("%s: called\n", __func__); + if (sc->sc_vlan_en) { + /* + * VLAN classification rules: tag-based VLANs, use VID to classify, + * drop packets that cannot be classified. + */ + ip175x_updatereg(sc, 22, 0, 0x3fff, 0x003f); + + /* + * Ingress rules: CFI=1 dropped, null VID is untagged, VID=1 passed, + * VID=0xfff discarded, admin both tagged and untagged, ingress + * filters enabled. + */ + ip175x_updatereg(sc, 22, 1, 0x0fff, 0x0c3f); + + /* Egress rules: IGMP processing off, keep VLAN header off */ + ip175x_updatereg(sc, 22, 2, 0x0fff, 0x0000); + } else { + /* VLAN classification rules: everything off & clear table */ + ip175x_updatereg(sc, 22, 0, 0xbfff, 0x8000); + + /* Ingress and egress rules: set to defaults */ + ip175x_updatereg(sc, 22, 1, 0x0fff, 0x0c3f); + ip175x_updatereg(sc, 22, 2, 0x0fff, 0x0000); + } + + return (0); +} + +struct ip175x_func_def ip175d_func_def = { + &ip175d_reset, + &ip175d_set_state, + &ip175d_set_vlan_mode, + 20, /* reset phy */ + 2, /* reset reg */ + 0x175d, /* reset val */ + -1, /* mode phy */ + -1, /* mode reg */ + -1, /* mode val */ +}; + +/* + * Generic code. + */ + +/* + * Reset the IP175x switch + */ +static int +ip175x_reset(struct ip175x_cpu_softc *sc) +{ + +printf("%s: called\n", __func__); + if (sc->sc_func->ip175x_reset_phy != -1) { + SWITCHBUS_WRITEREG(sc->sc_pdev, sc->sc_func->ip175x_reset_phy, + sc->sc_func->ip175x_reset_reg, + sc->sc_func->ip175x_reset_val); + DELAY(2); + } + + sc->sc_func->ip175x_reset(sc); + + /* bypass all vlans */ + sc->sc_vlan_en = 0; + ip175x_reset_vlans(sc); + sc->sc_func->ip175x_set_vlan_mode(sc); + + /* update all settings */ + sc->sc_func->ip175x_set_state(sc); + + return (0); +} + +static int +ip175x_updatereg(struct ip175x_cpu_softc *sc, __uint16_t phy, __uint16_t reg, +__uint16_t mask, __uint16_t val) +{ + __uint16_t v; + + v = SWITCHBUS_READREG(sc->sc_pdev, phy, reg); + v &= ~mask; + v |= val; + SWITCHBUS_WRITEREG(sc->sc_pdev, phy, reg, v); + return (0); +} + +/* + * Enable the switch 'core'. + */ +static int +ip175x_cpu_enable(struct ip175x_cpu_softc *sc, device_t pdev) +{ + __uint32_t model; + + /* cpu already enabled ? */ + if (sc->sc_cpu_en) + return (0); + + sc->sc_pdev = pdev; + sc->sc_func = &ip175c_func_def; + + device_printf(sc->sc_pdev, "enabling the switch core\n"); + /* check for IP175D */ + model = SWITCHBUS_READREG(sc->sc_pdev, 20, 0); + if (model == 0x175d) { + device_printf(sc->sc_pdev, "IP175D detected\n"); + sc->sc_func = &ip175d_func_def; + } + + /* set cpu as enabled */ + sc->sc_cpu_en = 1; + + /* reset the switch */ + ip175x_reset(sc); + + return (0); +} + +/* + * Disable the switch 'core'. + */ +static int +ip175x_cpu_disable(struct ip175x_cpu_softc *sc) +{ + + /* is the cpu enabled ? */ + if (sc->sc_cpu_en == 0) + return (0); + + /* set cpu as disabled */ + sc->sc_cpu_en = 0; + + /* reset the switch */ + ip175x_reset(sc); + + return (0); +} + +static void +ip175x_reset_vlans(struct ip175x_cpu_softc *sc) +{ + struct ip175x_vlan *v; + int i; + + /* reset all the vlan data */ + memset(sc->sc_vlan, 0, sizeof(sc->sc_vlan)); + memset(sc->sc_defaultvlan, 0, sizeof(sc->sc_defaultvlan)); + sc->sc_addvtag = 0; + sc->sc_stripvtag = 0; + + /* set the default vlan (1) */ + v = &sc->sc_vlan[0]; + v->vlanid = 1; + for (i = 0; i < IP175X_NUM_PORTS; i++) { + sc->sc_defaultvlan[i] = v->vlanid; + v->ports |= (1 << i); + } +} + +static int +ip175x_add_vlan(struct ip175x_cpu_softc *sc, int phy, int vlan, int dflt) +{ + struct ip175x_vlan *v; + int i; + + /* check if vlan is not already set */ + v = NULL; + for (i = 0; i < IP175X_MAX_VLANS; i++) { + if (sc->sc_vlan[i].vlanid == vlan) { + v = &sc->sc_vlan[i]; + break; + } + } + + /* if not found try a find a free one */ + if (v == NULL) { + for (i = 0; i < IP175X_MAX_VLANS; i++) { + if (sc->sc_vlan[i].vlanid == 0) { + v = &sc->sc_vlan[i]; + break; + } + } + } + + /* no free vlan found */ + if (v == NULL) + return (EINVAL); + + v->vlanid = vlan; + v->ports |= (1 << phy); + if (dflt) + sc->sc_defaultvlan[phy] = vlan; + + return (0); +} + +static int +ip175x_del_vlan(struct ip175x_cpu_softc *sc, int phy, int vlan) +{ + struct ip175x_vlan *v; + int i; + + /* find the vlan */ + v = NULL; + for (i = 0; i < IP175X_MAX_VLANS; i++) { + if (sc->sc_vlan[i].vlanid == vlan) { + v = &sc->sc_vlan[i]; + break; + } + } + + /* no vlan found */ + if (v == NULL) + return (EINVAL); + + /* cannot remove the default vlan */ + if (sc->sc_defaultvlan[phy] == vlan) + return (EINVAL); + + v->ports &= ~(1 << phy); + if (v->ports == 0) + v->vlanid = 0; + + return (0); +} + +static int +ip175x_count_vlans(struct ip175x_cpu_softc *sc, int phy) +{ + int i, count; + + count = 0; + for (i = 0; i < IP175X_MAX_VLANS; i++) { + if (sc->sc_vlan[i].vlanid == 0) + continue; + if (sc->sc_vlan[i].ports & (1 << phy)) + count++; + } + return (count); +} diff --git a/sys/dev/switch/switch.c b/sys/dev/switch/switch.c new file mode 100644 index 0000000..83182ca --- /dev/null +++ b/sys/dev/switch/switch.c @@ -0,0 +1,757 @@ +/* $NetBSD: mii.c,v 1.12 1999/08/03 19:41:49 drochner Exp $ */ + +/*- + * Copyright (c) 1998 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility, + * NASA Ames Research Center. + * + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + */ + +#include +__FBSDID("$FreeBSD: head/sys/dev/mii/mii.c 205275 2010-03-18 07:35:20Z ed $"); + +/* + * MII bus layer, glues MII-capable network interface drivers to sharable + * PHY drivers. This exports an interface compatible with BSD/OS 3.0's, + * plus some NetBSD extensions. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include + +MODULE_VERSION(switchbus, 1); + +#include "switchbus_if.h" + +static int switchbus_print_child(device_t, device_t); +static int switchbus_read_ivar(device_t, device_t, int, uintptr_t *); +static int switchbus_child_location_str(device_t, device_t, char *, size_t); +static int switchbus_child_pnpinfo_str(device_t, device_t, char *, size_t); +static int switchbus_readreg(device_t, int, int); +static int switchbus_writereg(device_t, int, int, int); +static void switchbus_statchg(device_t); +static void switchbus_linkchg(device_t); +static void switchbus_mediainit(device_t); +static int switch_get_media(struct ifnet *, struct ifreq *, + struct miibus_softc *); +static int switch_set_media(struct ifnet *, struct ifreq *, + struct miibus_softc *); +static int switch_add_vlan(struct ifnet *, struct ifreq *, + struct miibus_softc *); +static int switch_del_vlan(struct ifnet *, struct ifreq *, + struct miibus_softc *); +static int switch_get_vlans(struct ifnet *, struct ifreq *, + struct miibus_softc *); +static int switch_get_phy_flags(struct ifnet *, struct ifreq *, + struct miibus_softc *); +static int switch_set_phy_flags(struct ifnet *, struct ifreq *, + struct miibus_softc *); + +static device_method_t switchbus_methods[] = { + /* device interface */ + DEVMETHOD(device_probe, switchbus_probe), + DEVMETHOD(device_attach, switchbus_attach), + DEVMETHOD(device_detach, switchbus_detach), + DEVMETHOD(device_shutdown, bus_generic_shutdown), + + /* bus interface */ + DEVMETHOD(bus_print_child, switchbus_print_child), + DEVMETHOD(bus_read_ivar, switchbus_read_ivar), + DEVMETHOD(bus_driver_added, bus_generic_driver_added), + DEVMETHOD(bus_child_pnpinfo_str, switchbus_child_pnpinfo_str), + DEVMETHOD(bus_child_location_str, switchbus_child_location_str), + + /* switch MII interface */ + DEVMETHOD(switchbus_readreg, switchbus_readreg), + DEVMETHOD(switchbus_writereg, switchbus_writereg), + DEVMETHOD(switchbus_statchg, switchbus_statchg), + DEVMETHOD(switchbus_linkchg, switchbus_linkchg), + DEVMETHOD(switchbus_mediainit, switchbus_mediainit), + + { 0, 0 } +}; + +devclass_t switchbus_devclass; + +driver_t switchbus_driver = { + "switchbus", + switchbus_methods, + sizeof(struct miibus_softc) +}; + +int +switchbus_probe(device_t dev) +{ + + device_set_desc(dev, "Switch MII bus"); + + return (BUS_PROBE_SPECIFIC); +} + +int +switchbus_attach(device_t dev) +{ + struct switchbus_ivars *ivars; + struct miibus_softc *mii; + device_t *children; + int nchildren; + + mii = device_get_softc(dev); + nchildren = 0; + if (device_get_children(dev, &children, &nchildren) == 0) + free(children, M_TEMP); + if (nchildren == 0) { + device_printf(dev, "cannot get children"); + return (ENXIO); + } + ivars = device_get_ivars(dev); + mii->mii_ifp = ivars->ifp; + mii->mii_ifp->if_capabilities |= IFCAP_LINKSTATE; + mii->mii_ifp->if_capenable |= IFCAP_LINKSTATE; + LIST_INIT(&mii->mii_phys); + + return (bus_generic_attach(dev)); +} + +int +switchbus_detach(device_t dev) +{ + struct miibus_softc *mii; + + bus_generic_detach(dev); + mii = device_get_softc(dev); + mii->mii_ifp = NULL; + + return (0); +} + +static int +switchbus_print_child(device_t dev, device_t child) +{ + struct phy_attach_args *pa; + int retval; + + pa = device_get_ivars(child); + retval = bus_print_child_header(dev, child); + retval += printf(" PHY %d", pa->phy_num); + retval += bus_print_child_footer(dev, child); + + return (retval); +} + +static int +switchbus_read_ivar(device_t dev, device_t child __unused, int which, + uintptr_t *result) +{ + struct switchbus_ivars *ivars; + + /* + * NB: this uses the instance variables of the miibus rather than + * its PHY children. + */ + ivars = device_get_ivars(dev); + switch (which) { + case SWITCHBUS_IVAR_FLAGS: + *result = ivars->mii_flags; + break; + default: + return (ENOENT); + } + return (0); +} + +static int +switchbus_child_pnpinfo_str(device_t bus, device_t child, char *buf, + size_t buflen) +{ + struct phy_attach_args *pa; + + pa = device_get_ivars(child); + snprintf(buf, buflen, "oui=0x%x model=0x%x rev=0x%x", + MII_OUI(pa->phy_id1, pa->phy_id2), + MII_MODEL(pa->phy_id2), MII_REV(pa->phy_id2)); + return (0); +} + +static int +switchbus_child_location_str(device_t bus, device_t child, char *buf, + size_t buflen) +{ + struct phy_attach_args *pa; + + pa = device_get_ivars(child); + snprintf(buf, buflen, "phy_num=%d", pa->phy_num); + return (0); +} + +static int +switchbus_readreg(device_t dev, int phy, int reg) +{ + device_t parent; + + parent = device_get_parent(dev); + return (SWITCHBUS_READREG(parent, phy, reg)); +} + +static int +switchbus_writereg(device_t dev, int phy, int reg, int data) +{ + device_t parent; + + parent = device_get_parent(dev); + return (SWITCHBUS_WRITEREG(parent, phy, reg, data)); +} + +static void +switchbus_statchg(device_t dev) +{ + device_t parent; + struct miibus_softc *mii; + + parent = device_get_parent(dev); + SWITCHBUS_STATCHG(parent); + + mii = device_get_softc(dev); + mii->mii_ifp->if_baudrate = ifmedia_baudrate(mii->mii_media_active); +} + +static void +switchbus_linkchg(device_t dev) +{ + struct miibus_softc *mii; + device_t parent; + int link_state; + + parent = device_get_parent(dev); + SWITCHBUS_LINKCHG(parent); + + mii = device_get_softc(dev); + + if (mii->mii_media_status & IFM_AVALID) { + if (mii->mii_media_status & IFM_ACTIVE) + link_state = LINK_STATE_UP; + else + link_state = LINK_STATE_DOWN; + } else + link_state = LINK_STATE_UNKNOWN; + if_link_state_change(mii->mii_ifp, link_state); +} + +static void +switchbus_mediainit(device_t dev) +{ + struct miibus_softc *mii; + struct phy_softc *phy; + struct ifmedia_entry *m; + int media = 0; + + /* Poke the parent in case it has any media of its own to add. */ + SWITCHBUS_MEDIAINIT(device_get_parent(dev)); + + mii = device_get_softc(dev); + LIST_FOREACH(phy, &mii->mii_phys, phy_list) { + LIST_FOREACH(m, &phy->phy_media.ifm_list, ifm_list) { + media = m->ifm_media; + if (media == (IFM_ETHER | IFM_AUTO)) + break; + } + + ifmedia_set(&phy->phy_media, media); + } +} + +/* + * Helper function used by network interface drivers, attaches the switchbus + * and the PHYs to the network interface driver parent. + */ +int +mii_attach(device_t dev, device_t *miibus, struct ifnet *ifp, + ifm_change_cb_t ifmedia_upd, ifm_stat_cb_t ifmedia_sts, int capmask, + int phyloc, int offloc, int flags) +{ + struct switchbus_ivars *ivars; + struct phy_attach_args pa, *args; + device_t *children, phy; + int bmsr, first, i, nchildren, offset, phymax, phymin, rv; + + if (phyloc != MII_PHY_ANY && offloc != MII_OFFSET_ANY) { + printf("%s: phyloc and offloc specified", __func__); + return (EINVAL); + } + + if (offloc != MII_OFFSET_ANY && (offloc < 0 || offloc >= MII_NPHY)) { + printf("%s: ivalid offloc %d", __func__, offloc); + return (EINVAL); + } + + if (phyloc == MII_PHY_ANY) { + phymin = 0; + phymax = MII_NPHY - 1; + } else { + if (phyloc < 0 || phyloc >= MII_NPHY) { + printf("%s: ivalid phyloc %d", __func__, phyloc); + return (EINVAL); + } + phymin = phymax = phyloc; + } + + first = 0; + if (*miibus == NULL) { + first = 1; + ivars = malloc(sizeof(*ivars), M_DEVBUF, M_NOWAIT); + if (ivars == NULL) + return (ENOMEM); + ivars->ifp = ifp; + ivars->ifmedia_upd = ifmedia_upd; + ivars->ifmedia_sts = ifmedia_sts; + ivars->mii_flags = flags; + *miibus = device_add_child(dev, "switchbus", -1); + if (*miibus == NULL) { + rv = ENXIO; + goto fail; + } + device_set_ivars(*miibus, ivars); + } else { + ivars = device_get_ivars(*miibus); + if (ivars->ifp != ifp || ivars->ifmedia_upd != ifmedia_upd || + ivars->ifmedia_sts != ifmedia_sts || + ivars->mii_flags != flags) { + printf("%s: non-matching invariant", __func__); + return (EINVAL); + } + } + + pa.phy_capmask = capmask; + + phy = NULL; + offset = 0; + for (pa.phy_num = phymin; pa.phy_num <= phymax; pa.phy_num++) { + /* + * Make sure we haven't already configured a PHY at this + * address. This allows mii_attach() to be called + * multiple times. + */ + if (device_get_children(*miibus, &children, &nchildren) == 0) { + for (i = 0; i < nchildren; i++) { + args = device_get_ivars(children[i]); + if (args->phy_num == pa.phy_num) { + /* + * Yes, there is already something + * configured at this address. + */ + free(children, M_TEMP); + goto skip; + } + } + free(children, M_TEMP); + } + + /* + * Check to see if there is a PHY at this address. Note, + * many braindead PHYs report 0/0 in their ID registers, + * so we test for media in the BMSR. + */ + bmsr = SWITCHBUS_READREG(dev, pa.phy_num, MII_BMSR); + if (bmsr == 0 || bmsr == 0xffff || + (bmsr & (BMSR_EXTSTAT | BMSR_MEDIAMASK)) == 0) { + /* Assume no PHY at this address. */ + continue; + } + + /* + * There is a PHY at this address. If we were given an + * `offset' locator, skip this PHY if it doesn't match. + */ + if (offloc != MII_OFFSET_ANY && offloc != offset) + goto skip; + + /* + * Extract the IDs. Braindead PHYs will be handled by the + * `ukswitch' driver, as we have no ID information to + * match on. + */ + pa.phy_id1 = SWITCHBUS_READREG(dev, pa.phy_num, MII_PHYIDR1); + pa.phy_id2 = SWITCHBUS_READREG(dev, pa.phy_num, MII_PHYIDR2); + + args = malloc(sizeof(struct phy_attach_args), M_DEVBUF, + M_NOWAIT); + if (args == NULL) + goto skip; + bcopy((char *)&pa, (char *)args, sizeof(pa)); + phy = device_add_child(*miibus, NULL, -1); + if (phy == NULL) { + free(args, M_DEVBUF); + goto skip; + } + device_set_ivars(phy, args); + skip: + offset++; + } + + if (first != 0) { + if (phy == NULL) { + rv = ENXIO; + goto fail; + } + rv = bus_generic_attach(dev); + if (rv != 0) + goto fail; + } + rv = bus_generic_attach(*miibus); + if (rv != 0) + goto fail; + + return (0); + + fail: + if (*miibus != NULL) + device_delete_child(dev, *miibus); + free(ivars, M_DEVBUF); + if (first != 0) + *miibus = NULL; + return (rv); +} + +static int +switch_get_media(struct ifnet *ifp, struct ifreq *ifr, struct miibus_softc *mii) +{ + struct ifmediareq *ifmr = (struct ifmediareq *)ifr; + struct phy_softc *phy; + int *kptr; + int error; + int count; + int i; + + /* + * Return the number of phys on bus + */ + if (ifmr->ifm_phy == 0 && (ifmr->ifm_flags & IFM_HAVE_PHY) == 0) { + + /* count the number of phys on switchbus */ + count = 0; + LIST_FOREACH(phy, &mii->mii_phys, phy_list) + count++; + + /* just return the number of phys on bus */ + ifmr->ifm_phy = count; + + return (0); + + /* + * Return the phy list + */ + } else if (ifmr->ifm_phy > 0 && (ifmr->ifm_flags & IFM_HAVE_PHY) == 0) { + + /* count the number of phys on switchbus */ + count = 0; + LIST_FOREACH(phy, &mii->mii_phys, phy_list) + count++; + + /* verify the userland request */ + if (ifmr->ifm_phy > count) + ifmr->ifm_phy = count; + if (ifmr->ifm_phy < 0) + return (EINVAL); + + if (ifmr->ifm_phy != 0) { + + kptr = (int *)malloc(ifmr->ifm_phy * sizeof(int), + M_TEMP, M_NOWAIT); + if (kptr == NULL) + return (ENOMEM); + + /* copy the phy list */ + phy = LIST_FIRST(&mii->mii_phys); + for (i = 0; phy != NULL && i < ifmr->ifm_phy; + phy = LIST_NEXT(phy, phy_list), i++) + kptr[i] = phy->phy_num; + + error = copyout((caddr_t)kptr, + (caddr_t)ifmr->ifm_ulist, + ifmr->ifm_phy * sizeof(int)); + + free(kptr, M_TEMP); + return (error); + } + + return (0); + + /* + * Retrieve the phy media settings + */ + } else if (ifmr->ifm_flags & IFM_HAVE_PHY) { + + phy = mii_get_phy(mii, ifmr->ifm_phy); + if (phy == NULL) + return (EINVAL); + return (ifmedia_ioctl(ifp, ifr, &phy->phy_media, SIOCGIFMEDIA)); + } + + return (EINVAL); +} + +/* + * Set the phy media settings + */ +static int +switch_set_media(struct ifnet *ifp, struct ifreq *ifr, struct miibus_softc *mii) +{ + struct ifswmediareq *ifswmr = (struct ifswmediareq *)ifr; + struct phy_softc *phy; + + phy = mii_get_phy(mii, ifswmr->ifswm_phy); + if (phy == NULL) + return (EINVAL); + return (ifmedia_ioctl(ifp, &ifswmr->ifswm_ifreq, &phy->phy_media, + SIOCSIFMEDIA)); +} + +/* + * Add vlan id to switch port (phy) + */ +static int +switch_add_vlan(struct ifnet *ifp, struct ifreq *ifr, struct miibus_softc *mii) +{ + struct ifswitchreq *ifswr = (struct ifswitchreq *)ifr; + struct phy_softc *phy; + + phy = mii_get_phy(mii, ifswr->ifsw_phy); + if (phy == NULL) + return (EINVAL); + return (SWITCHBUS_ADD_VLAN(phy->phy_dev, ifswr->ifsw_vlan, + ifswr->ifsw_flags, phy->phy_num)); +} + +/* + * Remove vlan id from switch port (phy) + */ +static int +switch_del_vlan(struct ifnet *ifp, struct ifreq *ifr, struct miibus_softc *mii) +{ + struct ifswitchreq *ifswr = (struct ifswitchreq *)ifr; + struct phy_softc *phy; + + phy = mii_get_phy(mii, ifswr->ifsw_phy); + if (phy == NULL) + return (EINVAL); + return (SWITCHBUS_DEL_VLAN(phy->phy_dev, ifswr->ifsw_vlan, + phy->phy_num)); +} + +/* + * List the vlan ids in a switch port (phy) + */ +static int +switch_get_vlans(struct ifnet *ifp, struct ifreq *ifr, struct miibus_softc *mii) +{ + struct ifswitchreq *ifswr = (struct ifswitchreq *)ifr; + struct phy_softc *phy; + + phy = mii_get_phy(mii, ifswr->ifsw_phy); + if (phy == NULL) + return (EINVAL); + return (SWITCHBUS_GET_VLANS(phy->phy_dev, phy->phy_num, + &ifswr->ifsw_count, &ifswr->ifsw_vlist)); +} + +/* + * Get switch port flags + */ +static int +switch_get_phy_flags(struct ifnet *ifp, struct ifreq *ifr, struct miibus_softc *mii) +{ + struct ifswitchreq *ifswr = (struct ifswitchreq *)ifr; + struct phy_softc *phy; + + phy = mii_get_phy(mii, ifswr->ifsw_phy); + if (phy == NULL) + return (EINVAL); + return (SWITCHBUS_GET_FLAGS(phy->phy_dev, phy->phy_num, + &ifswr->ifsw_flags)); +} + +/* + * Set switch port flags + */ +static int +switch_set_phy_flags(struct ifnet *ifp, struct ifreq *ifr, struct miibus_softc *mii) +{ + struct ifswitchreq *ifswr = (struct ifswitchreq *)ifr; + struct phy_softc *phy; + + phy = mii_get_phy(mii, ifswr->ifsw_phy); + if (phy == NULL) + return (EINVAL); + return (SWITCHBUS_SET_FLAGS(phy->phy_dev, phy->phy_num, + ifswr->ifsw_flags)); +} + +int +switch_ioctl(struct ifnet *ifp, struct ifreq *ifr, struct miibus_softc *mii, + u_long cmd) +{ + + if (ifp == NULL || ifr == NULL || mii == NULL) + return(EINVAL); + + switch (cmd) { + + /* get switch media */ + case SIOCGIFSWMEDIA: + return (switch_get_media(ifp, ifr, mii)); + + /* set switch media */ + case SIOCSIFSWMEDIA: + return (switch_set_media(ifp, ifr, mii)); + + /* add vlan to switch port */ + case SIOCAIFSWVLAN: + return (switch_add_vlan(ifp, ifr, mii)); + + /* remove vlan from switch port */ + case SIOCDIFSWVLAN: + return (switch_del_vlan(ifp, ifr, mii)); + + /* get a list of allowed vlans for a switch port */ + case SIOCGIFSWVLAN: + return (switch_get_vlans(ifp, ifr, mii)); + + /* get switch port flags */ + case SIOCGIFSWFLAGS: + return (switch_get_phy_flags(ifp, ifr, mii)); + + /* set switch port flags */ + case SIOCSIFSWFLAGS: + return (switch_set_phy_flags(ifp, ifr, mii)); + + } + + return (EINVAL); +} + +/* + * Media changed; notify all PHYs. + */ +int +mii_mediachg(struct miibus_softc *mii) +{ + struct phy_softc *child; + int rv; + + mii->mii_media_status = 0; + mii->mii_media_active = IFM_NONE; + + LIST_FOREACH(child, &mii->mii_phys, phy_list) { + rv = (*child->phy_service)(child, mii, MII_MEDIACHG); + if (rv) + return (rv); + } + return (0); +} + +/* + * Call the PHY tick routines, used during autonegotiation. + */ +void +mii_tick(struct miibus_softc *mii) +{ + struct phy_softc *child; + + LIST_FOREACH(child, &mii->mii_phys, phy_list) + (void) (*child->phy_service)(child, mii, MII_TICK); +} + +/* + * Get media status from PHYs. + */ +void +mii_pollstat(struct miibus_softc *mii) +{ + struct phy_softc *child; + + LIST_FOREACH(child, &mii->mii_phys, phy_list) + (void) (*child->phy_service)(child, mii, MII_POLLSTAT); +} + +/* + * Inform the PHYs that the interface is down. + */ +void +mii_down(struct miibus_softc *mii) +{ + struct phy_softc *child; + + LIST_FOREACH(child, &mii->mii_phys, phy_list) + mii_phy_down(child); +} + +/* + * Generic MII bus service function. + */ +int +switch_service(struct phy_softc *phy, struct miibus_softc *mii, int cmd) +{ + + switch (cmd) { + case MII_POLLSTAT: + break; + + case MII_MEDIACHG: + /* + * If the interface is not up, don't do anything. + */ + if ((mii->mii_ifp->if_flags & IFF_UP) == 0) + break; + + mii_phy_setmedia(phy); + break; + + case MII_TICK: + if (mii_phy_tick(phy) == EJUSTRETURN) + return (0); + break; + } + + /* Update the media status. */ + ukswitch_status(phy); + + /* Callback if something changed. */ + mii_phy_update(phy, cmd); + return (0); +} diff --git a/sys/dev/switch/switch_physubr.c b/sys/dev/switch/switch_physubr.c new file mode 100644 index 0000000..1cd2b01 --- /dev/null +++ b/sys/dev/switch/switch_physubr.c @@ -0,0 +1,514 @@ +/* $NetBSD: mii_physubr.c,v 1.5 1999/08/03 19:41:49 drochner Exp $ */ + +/*- + * Copyright (c) 1998, 1999, 2000, 2001 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility, + * NASA Ames Research Center. + * + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + */ + +#include +__FBSDID("$FreeBSD: head/sys/dev/mii/mii_physubr.c 204646 2010-03-03 17:55:51Z joel $"); + +/* + * Subroutines common to all PHYs. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include + +#include "switchbus_if.h" + +#define IF_STATUS_UP (IFM_AVALID | IFM_ACTIVE) + + +/* + * Media to register setting conversion table. Order matters. + */ +const struct mii_media mii_media_table[MII_NMEDIA] = { + /* None */ + { BMCR_ISO, ANAR_CSMA, + 0, }, + + /* 10baseT */ + { BMCR_S10, ANAR_CSMA|ANAR_10, + 0, }, + + /* 10baseT-FDX */ + { BMCR_S10|BMCR_FDX, ANAR_CSMA|ANAR_10_FD, + 0, }, + + /* 100baseT4 */ + { BMCR_S100, ANAR_CSMA|ANAR_T4, + 0, }, + + /* 100baseTX */ + { BMCR_S100, ANAR_CSMA|ANAR_TX, + 0, }, + + /* 100baseTX-FDX */ + { BMCR_S100|BMCR_FDX, ANAR_CSMA|ANAR_TX_FD, + 0, }, + + /* 1000baseX */ + { BMCR_S1000, ANAR_CSMA, + 0, }, + + /* 1000baseX-FDX */ + { BMCR_S1000|BMCR_FDX, ANAR_CSMA, + 0, }, + + /* 1000baseT */ + { BMCR_S1000, ANAR_CSMA, + GTCR_ADV_1000THDX }, + + /* 1000baseT-FDX */ + { BMCR_S1000, ANAR_CSMA, + GTCR_ADV_1000TFDX }, +}; + +void +mii_phy_setmedia(struct phy_softc *sc) +{ + struct ifmedia_entry *ife = sc->phy_media.ifm_cur; + int bmcr, anar, gtcr; + + if (IFM_SUBTYPE(ife->ifm_media) == IFM_AUTO) { + if ((PHY_READ(sc, MII_BMCR) & BMCR_AUTOEN) == 0 || + (sc->phy_flags & MIIF_FORCEANEG)) + (void) mii_phy_auto(sc); + return; + } + + /* + * Table index is stored in the media entry. + */ + + KASSERT(ife->ifm_data >= 0 && ife->ifm_data < MII_NMEDIA, + ("invalid ife->ifm_data (0x%x) in mii_phy_setmedia", + ife->ifm_data)); + + anar = mii_media_table[ife->ifm_data].mm_anar; + bmcr = mii_media_table[ife->ifm_data].mm_bmcr; + gtcr = mii_media_table[ife->ifm_data].mm_gtcr; + + if (sc->phy_media.ifm_media & IFM_ETH_MASTER) { + switch (IFM_SUBTYPE(ife->ifm_media)) { + case IFM_1000_T: + gtcr |= GTCR_MAN_MS|GTCR_ADV_MS; + break; + + default: + panic("mii_phy_setmedia: MASTER on wrong media"); + } + } + + if (ife->ifm_media & IFM_LOOP) + bmcr |= BMCR_LOOP; + + PHY_WRITE(sc, MII_ANAR, anar); + PHY_WRITE(sc, MII_BMCR, bmcr); + if (sc->phy_flags & MIIF_HAVE_GTCR) + PHY_WRITE(sc, MII_100T2CR, gtcr); +} + +int +mii_phy_auto(struct phy_softc *sc) +{ + + /* + * Check for 1000BASE-X. Autonegotiation is a bit + * different on such devices. + */ + if (sc->phy_flags & MIIF_IS_1000X) { + uint16_t anar = 0; + + if (sc->phy_extcapabilities & EXTSR_1000XFDX) + anar |= ANAR_X_FD; + if (sc->phy_extcapabilities & EXTSR_1000XHDX) + anar |= ANAR_X_HD; + + if (sc->phy_flags & MIIF_DOPAUSE) { + /* XXX Asymmetric vs. symmetric? */ + anar |= ANLPAR_X_PAUSE_TOWARDS; + } + + PHY_WRITE(sc, MII_ANAR, anar); + } else { + uint16_t anar; + + anar = BMSR_MEDIA_TO_ANAR(sc->phy_capabilities) | + ANAR_CSMA; + if (sc->phy_flags & MIIF_DOPAUSE) + anar |= ANAR_FC; + PHY_WRITE(sc, MII_ANAR, anar); + if (sc->phy_flags & MIIF_HAVE_GTCR) { + uint16_t gtcr = 0; + + if (sc->phy_extcapabilities & EXTSR_1000TFDX) + gtcr |= GTCR_ADV_1000TFDX; + if (sc->phy_extcapabilities & EXTSR_1000THDX) + gtcr |= GTCR_ADV_1000THDX; + + PHY_WRITE(sc, MII_100T2CR, gtcr); + } + } + PHY_WRITE(sc, MII_BMCR, BMCR_AUTOEN | BMCR_STARTNEG); + return (EJUSTRETURN); +} + +int +mii_phy_tick(struct phy_softc *sc) +{ + struct ifmedia_entry *ife = sc->phy_media.ifm_cur; + struct ifnet *ifp = sc->phy_pdata->mii_ifp; + int reg; + + /* Just bail now if the interface is down. */ + if ((ifp->if_flags & IFF_UP) == 0) + return (EJUSTRETURN); + + /* + * If we're not doing autonegotiation, we don't need to do + * any extra work here. However, we need to check the link + * status so we can generate an announcement if the status + * changes. + */ + if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO) { + sc->phy_ticks = 0; /* reset autonegotiation timer. */ + return (0); + } + + /* Read the status register twice; BMSR_LINK is latch-low. */ + reg = PHY_READ(sc, MII_BMSR) | PHY_READ(sc, MII_BMSR); + if (reg & BMSR_LINK) { + sc->phy_ticks = 0; /* reset autonegotiation timer. */ + /* See above. */ + return (0); + } + + /* Announce link loss right after it happens */ + if (sc->phy_ticks++ == 0) + return (0); + + /* XXX: use default value if phy driver did not set mii_anegticks */ + if (sc->phy_anegticks == 0) + sc->phy_anegticks = MII_ANEGTICKS_GIGE; + + /* Only retry autonegotiation every mii_anegticks ticks. */ + if (sc->phy_ticks <= sc->phy_anegticks) + return (EJUSTRETURN); + + sc->phy_ticks = 0; + mii_phy_reset(sc); + mii_phy_auto(sc); + return (0); +} + +void +mii_phy_reset(struct phy_softc *sc) +{ + int reg, i; + + if (sc->phy_flags & MIIF_NOISOLATE) + reg = BMCR_RESET; + else + reg = BMCR_RESET | BMCR_ISO; + PHY_WRITE(sc, MII_BMCR, reg); + + /* Wait 100ms for it to complete. */ + for (i = 0; i < 100; i++) { + reg = PHY_READ(sc, MII_BMCR); + if ((reg & BMCR_RESET) == 0) + break; + DELAY(1000); + } +} + +void +mii_phy_down(struct phy_softc *sc) +{ + +} + +void +mii_phy_update(struct phy_softc *sc, int cmd) +{ + struct miibus_softc *mii; + struct phy_softc *phy; + int mii_media_active; + int mii_media_status; + + mii = device_get_softc(sc->phy_pdev); + + mii_media_active = IFM_ETHER | IFM_NONE; + LIST_FOREACH(phy, &mii->mii_phys, phy_list) { + if (IFM_SUBTYPE(phy->phy_media_active) != IFM_NONE && + IFM_SUBTYPE(mii_media_active) < + IFM_SUBTYPE(phy->phy_media_active)) + mii_media_active = phy->phy_media_active; + } + if (mii_media_active != mii->mii_media_active || cmd == MII_MEDIACHG) { + mii->mii_media_active = mii_media_active; + SWITCHBUS_STATCHG(sc->phy_pdev); + } + + mii_media_status = 0; + LIST_FOREACH(phy, &mii->mii_phys, phy_list) { + if ((phy->phy_media_status & IF_STATUS_UP) == IF_STATUS_UP && + (mii_media_status & IFM_ACTIVE) == 0) + mii_media_status = phy->phy_media_status; + } + if (mii_media_status != mii->mii_media_status) { + mii->mii_media_status = mii_media_status; + SWITCHBUS_LINKCHG(sc->phy_pdev); + } +} + +/* + * Given an ifmedia word, return the corresponding ANAR value. + */ +int +mii_anar(int media) +{ + int rv; + + switch (media & (IFM_TMASK|IFM_NMASK|IFM_FDX)) { + case IFM_ETHER|IFM_10_T: + rv = ANAR_10|ANAR_CSMA; + break; + case IFM_ETHER|IFM_10_T|IFM_FDX: + rv = ANAR_10_FD|ANAR_CSMA; + break; + case IFM_ETHER|IFM_100_TX: + rv = ANAR_TX|ANAR_CSMA; + break; + case IFM_ETHER|IFM_100_TX|IFM_FDX: + rv = ANAR_TX_FD|ANAR_CSMA; + break; + case IFM_ETHER|IFM_100_T4: + rv = ANAR_T4|ANAR_CSMA; + break; + default: + rv = 0; + break; + } + + return (rv); +} + +/* + * Initialize generic PHY media based on BMSR, called when a PHY is + * attached. We expect to be set up to print a comma-separated list + * of media names. Does not print a newline. + */ +void +mii_phy_add_media(struct phy_softc *sc) +{ + const char *sep = ""; + + if ((sc->phy_capabilities & BMSR_MEDIAMASK) == 0 && + (sc->phy_extcapabilities & EXTSR_MEDIAMASK) == 0) { + printf("no media present"); + return; + } + + /* Set aneg timer for 10/100 media. Gigabit media handled below. */ + sc->phy_anegticks = MII_ANEGTICKS; + +#define ADD(m, c) ifmedia_add(&sc->phy_media, (m), (c), NULL) +#define PRINT(s) printf("%s%s", sep, s); sep = ", " + + if ((sc->phy_flags & MIIF_NOISOLATE) == 0) + ADD(IFM_MAKEWORD(IFM_ETHER, IFM_NONE, 0, 0), + MII_MEDIA_NONE); + + /* + * There are different interpretations for the bits in + * HomePNA PHYs. And there is really only one media type + * that is supported. + */ + if (sc->phy_flags & MIIF_IS_HPNA) { + if (sc->phy_capabilities & BMSR_10THDX) { + ADD(IFM_MAKEWORD(IFM_ETHER, IFM_HPNA_1, 0, 0), + MII_MEDIA_10_T); + PRINT("HomePNA1"); + } + return; + } + + if (sc->phy_capabilities & BMSR_10THDX) { + ADD(IFM_MAKEWORD(IFM_ETHER, IFM_10_T, 0, 0), + MII_MEDIA_10_T); + PRINT("10baseT"); + } + if (sc->phy_capabilities & BMSR_10TFDX) { + ADD(IFM_MAKEWORD(IFM_ETHER, IFM_10_T, IFM_FDX, 0), + MII_MEDIA_10_T_FDX); + PRINT("10baseT-FDX"); + } + if (sc->phy_capabilities & BMSR_100TXHDX) { + ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_TX, 0, 0), + MII_MEDIA_100_TX); + PRINT("100baseTX"); + } + if (sc->phy_capabilities & BMSR_100TXFDX) { + ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_TX, IFM_FDX, 0), + MII_MEDIA_100_TX_FDX); + PRINT("100baseTX-FDX"); + } + if (sc->phy_capabilities & BMSR_100T4) { + ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_T4, 0, 0), + MII_MEDIA_100_T4); + PRINT("100baseT4"); + } + + if (sc->phy_extcapabilities & EXTSR_MEDIAMASK) { + /* + * XXX Right now only handle 1000SX and 1000TX. Need + * XXX to handle 1000LX and 1000CX some how. + */ + if (sc->phy_extcapabilities & EXTSR_1000XHDX) { + sc->phy_anegticks = MII_ANEGTICKS_GIGE; + sc->phy_flags |= MIIF_IS_1000X; + ADD(IFM_MAKEWORD(IFM_ETHER, IFM_1000_SX, 0, 0), + MII_MEDIA_1000_X); + PRINT("1000baseSX"); + } + if (sc->phy_extcapabilities & EXTSR_1000XFDX) { + sc->phy_anegticks = MII_ANEGTICKS_GIGE; + sc->phy_flags |= MIIF_IS_1000X; + ADD(IFM_MAKEWORD(IFM_ETHER, IFM_1000_SX, IFM_FDX, 0), + MII_MEDIA_1000_X_FDX); + PRINT("1000baseSX-FDX"); + } + + /* + * 1000baseT media needs to be able to manipulate + * master/slave mode. We set IFM_ETH_MASTER in + * the "don't care mask" and filter it out when + * the media is set. + * + * All 1000baseT PHYs have a 1000baseT control register. + */ + if (sc->phy_extcapabilities & EXTSR_1000THDX) { + sc->phy_anegticks = MII_ANEGTICKS_GIGE; + sc->phy_flags |= MIIF_HAVE_GTCR; + sc->phy_media.ifm_mask |= IFM_ETH_MASTER; + ADD(IFM_MAKEWORD(IFM_ETHER, IFM_1000_T, 0, 0), + MII_MEDIA_1000_T); + PRINT("1000baseT"); + } + if (sc->phy_extcapabilities & EXTSR_1000TFDX) { + sc->phy_anegticks = MII_ANEGTICKS_GIGE; + sc->phy_flags |= MIIF_HAVE_GTCR; + sc->phy_media.ifm_mask |= IFM_ETH_MASTER; + ADD(IFM_MAKEWORD(IFM_ETHER, IFM_1000_T, IFM_FDX, 0), + MII_MEDIA_1000_T_FDX); + PRINT("1000baseT-FDX"); + } + } + + if (sc->phy_capabilities & BMSR_ANEG) { + ADD(IFM_MAKEWORD(IFM_ETHER, IFM_AUTO, 0, 0), + MII_NMEDIA); /* intentionally invalid index */ + PRINT("auto"); + } +#undef ADD +#undef PRINT +} + +int +mii_phy_detach(device_t dev) +{ + struct phy_softc *sc; + + sc = device_get_softc(dev); + mii_phy_down(sc); + ifmedia_removeall(&sc->phy_media); + sc->phy_pdev = NULL; + LIST_REMOVE(sc, phy_list); + + return(0); +} + +const struct mii_phydesc * +mii_phy_match_gen(const struct phy_attach_args *pa, + const struct mii_phydesc *mpd, size_t len) +{ + + for (; mpd->mpd_name != NULL; + mpd = (const struct mii_phydesc *) ((const char *) mpd + len)) { + if (MII_OUI(pa->phy_id1, pa->phy_id2) == mpd->mpd_oui && + MII_MODEL(pa->phy_id2) == mpd->mpd_model) + return (mpd); + } + return (NULL); +} + +const struct mii_phydesc * +mii_phy_match(const struct phy_attach_args *pa, const struct mii_phydesc *mpd) +{ + + return (mii_phy_match_gen(pa, mpd, sizeof(struct mii_phydesc))); +} + +int +mii_phy_dev_probe(device_t dev, const struct mii_phydesc *mpd, int mrv) +{ + + mpd = mii_phy_match(device_get_ivars(dev), mpd); + if (mpd != NULL) { + device_set_desc(dev, mpd->mpd_name); + return (mrv); + } + + return (ENXIO); +} + +struct phy_softc * +mii_get_phy(struct miibus_softc *mii, int phy_num) +{ + struct phy_softc *phy; + + phy = LIST_FIRST(&mii->mii_phys); + for (; phy != NULL; phy = LIST_NEXT(phy, phy_list)) + if (phy->phy_num == phy_num) + break; + return (phy); +} diff --git a/sys/dev/switch/switchbus_if.m b/sys/dev/switch/switchbus_if.m new file mode 100644 index 0000000..1bbdccc --- /dev/null +++ b/sys/dev/switch/switchbus_if.m @@ -0,0 +1,106 @@ +# $FreeBSD: head/sys/dev/mii/miibus_if.m 84140 2001-09-29 18:40:06Z jlemon $ + +#include + +INTERFACE switchbus; + +# +# Read register from device on MII bus +# +METHOD int readreg { + device_t dev; + int phy; + int reg; +}; + +# +# Write register to device on MII bus +# +METHOD int writereg { + device_t dev; + int phy; + int reg; + int val; +}; + +# +# Notify bus about PHY status change. +# +METHOD void statchg { + device_t dev; +}; + +# +# Notify bus about PHY link change. +# +METHOD void linkchg { + device_t dev; +}; + +# +# Notify bus that media has been set. +# +METHOD void mediainit { + device_t dev; +}; + +# +# Add VLAN ID to switch port +# +METHOD int add_vlan { + device_t dev; + int vlan; + int flags; + int phy; +}; + +# +# Remove VLAN ID from switch port +# +METHOD int del_vlan { + device_t dev; + int vlan; + int phy; +}; + +# +# Get the list of all VLAN IDs on a switch port +# +METHOD int get_vlans { + device_t dev; + int phy; + int *count; + int **vlist; +}; + +# +# Get switch port flags +# +METHOD int get_flags { + device_t dev; + int phy; + int *flags; +}; + +# +# Set switch port flags +# +METHOD int set_flags { + device_t dev; + int phy; + int flags; +}; + +# +# Enable the switch 'core' +# +METHOD int cpu_enable { + device_t dev; +}; + +# +# Disable the switch 'core' +# +METHOD int cpu_disable { + device_t dev; +}; diff --git a/sys/dev/switch/switchdevs b/sys/dev/switch/switchdevs new file mode 100644 index 0000000..9f17a1f --- /dev/null +++ b/sys/dev/switch/switchdevs @@ -0,0 +1,110 @@ +$FreeBSD: head/sys/dev/mii/miidevs 211103 2010-08-09 17:22:14Z yongari $ +/*$NetBSD: miidevs,v 1.6 1999/05/14 11:37:30 drochner Exp $*/ + +/*- + * Copyright (c) 1998, 1999 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility, + * NASA Ames Research Center. + * + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + */ + +/* + * List of known MII OUIs. + * For a complete list see http://standards.ieee.org/regauth/oui/ + * + * XXX Vendors do obviously not agree how OUIs (18 bit) are mapped + * to the 16 bits available in the id registers. The MII_OUI() macro + * in "mii.h" reflects the most obvious way. If a vendor uses a + * different mapping, an "xx" prefixed OUI is defined here which is + * mangled accordingly to compensate. + */ + +oui ADMTEK 0x00002e Infineon ADMtek +oui AGERE 0x00a0bc Agere Systems +oui ALTIMA 0x0010a9 Altima Communications +oui AMD 0x00001a Advanced Micro Devices +oui ASIX 0x00602e Asix Semiconductor +oui ATHEROS 0x001374 Atheros Communications +oui BROADCOM 0x001018 Broadcom Corporation +oui BROADCOM2 0x000af7 Broadcom Corporation +oui CICADA 0x0003F1 Cicada Semiconductor +oui DAVICOM 0x00606e Davicom Semiconductor +oui ICPLUS 0x0090c3 IC Plus Corp. +oui ICS 0x00a0be Integrated Circuit Systems +oui INTEL 0x00aa00 Intel +oui JATO 0x00e083 Jato Technologies +oui JMICRON 0x001b8c JMicron Technologies +oui LEVEL1 0x00207b Level 1 +oui NATSEMI 0x080017 National Semiconductor +oui QUALSEMI 0x006051 Quality Semiconductor +oui REALTEK 0x000020 RealTek Semicondctor +oui SEEQ 0x00a07d Seeq +oui SIS 0x00e006 Silicon Integrated Systems +oui SMSC 0x0005be SMSC +oui TDK 0x00c039 TDK +oui TI 0x080028 Texas Instruments +oui VITESSE 0x0001c1 Vitesse Semiconductor +oui XAQTI 0x00e0ae XaQti Corp. +oui MARVELL 0x005043 Marvell Semiconductor +oui xxMARVELL 0x000ac2 Marvell Semiconductor + +/* in the 79c873, AMD uses another OUI (which matches Davicom!) */ +oui xxAMD 0x00606e Advanced Micro Devices + +/* Intel 82553 A/B steppings */ +oui xxINTEL 0x00f800 Intel + +/* some vendors have the bits swapped within bytes + (ie, ordered as on the wire) */ +oui xxALTIMA 0x000895 Altima Communications +oui xxBROADCOM 0x000818 Broadcom Corporation +oui xxBROADCOM_ALT1 0x0050ef Broadcom Corporation +oui xxICS 0x00057d Integrated Circuit Systems +oui xxSEEQ 0x0005be Seeq +oui xxSIS 0x000760 Silicon Integrated Systems +oui xxTI 0x100014 Texas Instruments +oui xxXAQTI 0x350700 XaQti Corp. + +/* Level 1 is completely different - from right to left. + (Two bits get lost in the third OUI byte.) */ +oui xxLEVEL1 0x1e0400 Level 1 + +/* Don't know what's going on here. */ +oui xxDAVICOM 0x006040 Davicom Semiconductor + +/* This is the OUI of the gigE PHY in the RealTek 8169S/8110S/8211B chips */ +oui xxREALTEK 0x000732 + +/* + * List of known models. Grouped by oui. + */ + +/* Atheros */ +model ATHEROS AR8316 0x0004 Atheros 8316 Gigabit Switch + +/* IC Plus Corp. */ +model ICPLUS IP175x 0x0018 IC Plus IP175x 10/100 Switch + diff --git a/sys/dev/switch/switchvar.h b/sys/dev/switch/switchvar.h new file mode 100644 index 0000000..788d438 --- /dev/null +++ b/sys/dev/switch/switchvar.h @@ -0,0 +1,524 @@ +/* $NetBSD: miivar.h,v 1.8 1999/04/23 04:24:32 thorpej Exp $ */ + +/*- + * Copyright (c) 1998, 1999 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility, + * NASA Ames Research Center. + * + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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: head/sys/dev/mii/miivar.h 204646 2010-03-03 17:55:51Z joel $ + */ + +#ifndef _DEV_SWITCH_SWITCHVAR_H_ +#define _DEV_SWITCH_SWITCHVAR_H_ + +#include + +/* + * Media Independent Interface configuration defintions. + */ + +struct phy_softc; + +/* + * switchbus ivars + */ +struct switchbus_ivars { + struct ifnet *ifp; + ifm_change_cb_t ifmedia_upd; + ifm_stat_cb_t ifmedia_sts; + int mii_flags; +}; + +/* + * Callbacks from MII layer into network interface device driver. + */ +typedef int (*mii_readreg_t)(struct device *, int, int); +typedef void (*mii_writereg_t)(struct device *, int, int, int); +typedef void (*mii_statchg_t)(struct device *); + +/* + * A network interface driver has one of these structures in its softc. + * It is the interface from the network interface driver to the MII + * layer. + */ +struct miibus_softc { + struct ifnet *mii_ifp; /* pointer back to network interface */ + + /* + * For network interfaces with multiple PHYs, a list of all + * PHYs is required so they can all be notified when a media + * request is made. + */ + LIST_HEAD(mii_listhead, phy_softc) mii_phys; + int mii_instance; + + /* + * PHY drivers fills this in with active media status. + */ + int mii_media_status; + int mii_media_active; + + /* + * Calls from MII layer into network interface driver. + */ + mii_readreg_t mii_readreg; + mii_writereg_t mii_writereg; + mii_statchg_t mii_statchg; +}; + +/* + * This call is used by the MII layer to call into the PHY driver + * to perform a `service request'. + */ +typedef int (*phy_downcall_t)(struct phy_softc *, struct miibus_softc *, int); + +/* + * Requests that can be made to the downcall. + */ +#define MII_TICK 1 /* once-per-second tick */ +#define MII_MEDIACHG 2 /* user changed media; perform the switch */ +#define MII_POLLSTAT 3 /* user requested media status; fill it in */ + +/* + * Each PHY driver's softc has one of these as the first member. + * XXX This would be better named "phy_softc", but this is the name + * XXX BSDI used, and we would like to have the same interface. + */ +struct phy_softc { + struct ifmedia phy_media; /* media information */ + device_t phy_dev; /* generic device glue */ + device_t phy_pdev; /* parent device glue */ + + LIST_ENTRY(phy_softc) phy_list; /* entry on parent's PHY list */ + + int phy_num; /* our MII address */ + int phy_inst; /* instance for ifmedia */ + + phy_downcall_t phy_service; /* our downcall */ + struct miibus_softc *phy_pdata; /* pointer to parent's miibus_softc */ + + int phy_flags; /* misc. flags; see below */ + int phy_capabilities; /* capabilities from BMSR */ + int phy_extcapabilities; /* extended capabilities */ + int phy_ticks; /* MII_TICK counter */ + int phy_anegticks; /* ticks before retrying aneg */ + int phy_media_active; /* active media */ + int phy_media_status; /* active status */ +}; + +/* switch phy flags */ +#define SW_STRIPVTAG 0x0001 +#define SW_ADDVTAG 0x0002 +#define SW_VLANBYPASS 0x0400 +#define SW_WANPORT 0x0800 + +/* mii_flags */ +#define MIIF_INITDONE 0x0001 /* has been initialized (mii_data) */ +#define MIIF_NOISOLATE 0x0002 /* do not isolate the PHY */ +#define MIIF_NOLOOP 0x0004 /* no loopback capability */ +#define MIIF_AUTOTSLEEP 0x0010 /* use tsleep(), not callout() */ +#define MIIF_HAVEFIBER 0x0020 /* from parent: has fiber interface */ +#define MIIF_HAVE_GTCR 0x0040 /* has 100base-T2/1000base-T CR */ +#define MIIF_IS_1000X 0x0080 /* is a 1000BASE-X device */ +#define MIIF_DOPAUSE 0x0100 /* advertise PAUSE capability */ +#define MIIF_IS_HPNA 0x0200 /* is a HomePNA device */ +#define MIIF_FORCEANEG 0x0400 /* force auto-negotiation */ + +/* Default mii_anegticks values */ +#define MII_ANEGTICKS 5 +#define MII_ANEGTICKS_GIGE 17 + +#define MIIF_INHERIT_MASK (MIIF_NOISOLATE|MIIF_NOLOOP|MIIF_AUTOTSLEEP) + +/* + * Special `locators' passed to mii_attach(). If one of these is not + * an `any' value, we look for *that* PHY and configure it. If both + * are not `any', that is an error, and mii_attach() will panic. + */ +#define MII_OFFSET_ANY -1 +#define MII_PHY_ANY -1 + +/* ioctl flags */ +#define IFM_HAVE_PHY 1 + +/* + * Used to attach a PHY to a parent. + */ +struct phy_attach_args { + int phy_num; /* MII address */ + int phy_id1; /* PHY ID register 1 */ + int phy_id2; /* PHY ID register 2 */ + int phy_capmask; /* capability mask from BMSR */ +}; + +/* + * Used to match a PHY. + */ +struct mii_phydesc { + u_int32_t mpd_oui; /* the PHY's OUI */ + u_int32_t mpd_model; /* the PHY's model */ + const char *mpd_name; /* the PHY's name */ +}; +#define MII_PHY_DESC(a, b) { MII_OUI_ ## a, MII_MODEL_ ## a ## _ ## b, \ + MII_STR_ ## a ## _ ## b } +#define MII_PHY_END { 0, 0, NULL } + +/* + * An array of these structures map MII media types to BMCR/ANAR settings. + */ +struct mii_media { + int mm_bmcr; /* BMCR settings for this media */ + int mm_anar; /* ANAR settings for this media */ + int mm_gtcr; /* 100base-T2 or 1000base-T CR */ +}; + +#define MII_MEDIA_NONE 0 +#define MII_MEDIA_10_T 1 +#define MII_MEDIA_10_T_FDX 2 +#define MII_MEDIA_100_T4 3 +#define MII_MEDIA_100_TX 4 +#define MII_MEDIA_100_TX_FDX 5 +#define MII_MEDIA_1000_X 6 +#define MII_MEDIA_1000_X_FDX 7 +#define MII_MEDIA_1000_T 8 +#define MII_MEDIA_1000_T_FDX 9 +#define MII_NMEDIA 10 + +#ifdef _KERNEL + +#define PHY_READ(p, r) \ + SWITCHBUS_READREG((p)->phy_pdev, (p)->phy_num, (r)) + +#define PHY_WRITE(p, r, v) \ + SWITCHBUS_WRITEREG((p)->phy_pdev, (p)->phy_num, (r), (v)) + +enum miibus_device_ivars { + SWITCHBUS_IVAR_FLAGS +}; + +extern devclass_t switchbus_devclass; +extern driver_t switchbus_driver; + +int switchbus_probe(device_t); +int switchbus_attach(device_t); +int switchbus_detach(device_t); + +int switch_ioctl(struct ifnet *, struct ifreq *, struct miibus_softc *, + u_long); +int switch_service(struct phy_softc *, struct miibus_softc *, int); + +int mii_attach(device_t, device_t *, struct ifnet *, ifm_change_cb_t, + ifm_stat_cb_t, int, int, int, int); +int mii_anar(int); +void mii_down(struct miibus_softc *); +int mii_mediachg(struct miibus_softc *); +void mii_tick(struct miibus_softc *); +void mii_pollstat(struct miibus_softc *); +int mii_phy_probe(device_t, device_t *, ifm_change_cb_t, ifm_stat_cb_t); +void mii_phy_add_media(struct phy_softc *); +struct phy_softc *mii_get_phy(struct miibus_softc *, int); + +int mii_phy_auto(struct phy_softc *); +int mii_phy_detach(device_t dev); +void mii_phy_down(struct phy_softc *); +void mii_phy_reset(struct phy_softc *); +void mii_phy_setmedia(struct phy_softc *sc); +void mii_phy_update(struct phy_softc *, int); +int mii_phy_tick(struct phy_softc *); + +const struct mii_phydesc * mii_phy_match(const struct phy_attach_args *pa, + const struct mii_phydesc *mpd); +const struct mii_phydesc * mii_phy_match_gen(const struct phy_attach_args *pa, + const struct mii_phydesc *mpd, size_t endlen); +int mii_phy_dev_probe(device_t dev, const struct mii_phydesc *mpd, int mrv); + +void ukswitch_status(struct phy_softc *); +#endif /* _KERNEL */ + +#endif /* _DEV_SWITCH_SWITCHVAR_H_ */ +/* $NetBSD: miivar.h,v 1.8 1999/04/23 04:24:32 thorpej Exp $ */ + +/*- + * Copyright (c) 1998, 1999 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility, + * NASA Ames Research Center. + * + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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: head/sys/dev/mii/miivar.h 204646 2010-03-03 17:55:51Z joel $ + */ + +#ifndef _DEV_SWITCH_SWITCHVAR_H_ +#define _DEV_SWITCH_SWITCHVAR_H_ + +#include + +/* + * Media Independent Interface configuration defintions. + */ + +struct phy_softc; + +/* + * switchbus ivars + */ +struct switchbus_ivars { + struct ifnet *ifp; + ifm_change_cb_t ifmedia_upd; + ifm_stat_cb_t ifmedia_sts; + int mii_flags; +}; + +/* + * Callbacks from MII layer into network interface device driver. + */ +typedef int (*mii_readreg_t)(struct device *, int, int); +typedef void (*mii_writereg_t)(struct device *, int, int, int); +typedef void (*mii_statchg_t)(struct device *); + +/* + * A network interface driver has one of these structures in its softc. + * It is the interface from the network interface driver to the MII + * layer. + */ +struct miibus_softc { + struct ifnet *mii_ifp; /* pointer back to network interface */ + + /* + * For network interfaces with multiple PHYs, a list of all + * PHYs is required so they can all be notified when a media + * request is made. + */ + LIST_HEAD(mii_listhead, phy_softc) mii_phys; + int mii_instance; + + /* + * PHY drivers fills this in with active media status. + */ + int mii_media_status; + int mii_media_active; + + /* + * Calls from MII layer into network interface driver. + */ + mii_readreg_t mii_readreg; + mii_writereg_t mii_writereg; + mii_statchg_t mii_statchg; +}; + +/* + * This call is used by the MII layer to call into the PHY driver + * to perform a `service request'. + */ +typedef int (*phy_downcall_t)(struct phy_softc *, struct miibus_softc *, int); + +/* + * Requests that can be made to the downcall. + */ +#define MII_TICK 1 /* once-per-second tick */ +#define MII_MEDIACHG 2 /* user changed media; perform the switch */ +#define MII_POLLSTAT 3 /* user requested media status; fill it in */ + +/* + * Each PHY driver's softc has one of these as the first member. + * XXX This would be better named "phy_softc", but this is the name + * XXX BSDI used, and we would like to have the same interface. + */ +struct phy_softc { + struct ifmedia phy_media; /* media information */ + device_t phy_dev; /* generic device glue */ + device_t phy_pdev; /* parent device glue */ + + LIST_ENTRY(phy_softc) phy_list; /* entry on parent's PHY list */ + + int phy_num; /* our MII address */ + int phy_inst; /* instance for ifmedia */ + + phy_downcall_t phy_service; /* our downcall */ + struct miibus_softc *phy_pdata; /* pointer to parent's miibus_softc */ + + int phy_flags; /* misc. flags; see below */ + int phy_capabilities; /* capabilities from BMSR */ + int phy_extcapabilities; /* extended capabilities */ + int phy_ticks; /* MII_TICK counter */ + int phy_anegticks; /* ticks before retrying aneg */ + int phy_media_active; /* active media */ + int phy_media_status; /* active status */ +}; + +/* switch phy flags */ +#define SW_STRIPVTAG 0x0001 +#define SW_ADDVTAG 0x0002 +#define SW_VLANBYPASS 0x0400 +#define SW_WANPORT 0x0800 + +/* mii_flags */ +#define MIIF_INITDONE 0x0001 /* has been initialized (mii_data) */ +#define MIIF_NOISOLATE 0x0002 /* do not isolate the PHY */ +#define MIIF_NOLOOP 0x0004 /* no loopback capability */ +#define MIIF_AUTOTSLEEP 0x0010 /* use tsleep(), not callout() */ +#define MIIF_HAVEFIBER 0x0020 /* from parent: has fiber interface */ +#define MIIF_HAVE_GTCR 0x0040 /* has 100base-T2/1000base-T CR */ +#define MIIF_IS_1000X 0x0080 /* is a 1000BASE-X device */ +#define MIIF_DOPAUSE 0x0100 /* advertise PAUSE capability */ +#define MIIF_IS_HPNA 0x0200 /* is a HomePNA device */ +#define MIIF_FORCEANEG 0x0400 /* force auto-negotiation */ + +/* Default mii_anegticks values */ +#define MII_ANEGTICKS 5 +#define MII_ANEGTICKS_GIGE 17 + +#define MIIF_INHERIT_MASK (MIIF_NOISOLATE|MIIF_NOLOOP|MIIF_AUTOTSLEEP) + +/* + * Special `locators' passed to mii_attach(). If one of these is not + * an `any' value, we look for *that* PHY and configure it. If both + * are not `any', that is an error, and mii_attach() will panic. + */ +#define MII_OFFSET_ANY -1 +#define MII_PHY_ANY -1 + +/* ioctl flags */ +#define IFM_HAVE_PHY 1 + +/* + * Used to attach a PHY to a parent. + */ +struct phy_attach_args { + int phy_num; /* MII address */ + int phy_id1; /* PHY ID register 1 */ + int phy_id2; /* PHY ID register 2 */ + int phy_capmask; /* capability mask from BMSR */ +}; + +/* + * Used to match a PHY. + */ +struct mii_phydesc { + u_int32_t mpd_oui; /* the PHY's OUI */ + u_int32_t mpd_model; /* the PHY's model */ + const char *mpd_name; /* the PHY's name */ +}; +#define MII_PHY_DESC(a, b) { MII_OUI_ ## a, MII_MODEL_ ## a ## _ ## b, \ + MII_STR_ ## a ## _ ## b } +#define MII_PHY_END { 0, 0, NULL } + +/* + * An array of these structures map MII media types to BMCR/ANAR settings. + */ +struct mii_media { + int mm_bmcr; /* BMCR settings for this media */ + int mm_anar; /* ANAR settings for this media */ + int mm_gtcr; /* 100base-T2 or 1000base-T CR */ +}; + +#define MII_MEDIA_NONE 0 +#define MII_MEDIA_10_T 1 +#define MII_MEDIA_10_T_FDX 2 +#define MII_MEDIA_100_T4 3 +#define MII_MEDIA_100_TX 4 +#define MII_MEDIA_100_TX_FDX 5 +#define MII_MEDIA_1000_X 6 +#define MII_MEDIA_1000_X_FDX 7 +#define MII_MEDIA_1000_T 8 +#define MII_MEDIA_1000_T_FDX 9 +#define MII_NMEDIA 10 + +#ifdef _KERNEL + +#define PHY_READ(p, r) \ + SWITCHBUS_READREG((p)->phy_pdev, (p)->phy_num, (r)) + +#define PHY_WRITE(p, r, v) \ + SWITCHBUS_WRITEREG((p)->phy_pdev, (p)->phy_num, (r), (v)) + +enum miibus_device_ivars { + SWITCHBUS_IVAR_FLAGS +}; + +extern devclass_t switchbus_devclass; +extern driver_t switchbus_driver; + +int switchbus_probe(device_t); +int switchbus_attach(device_t); +int switchbus_detach(device_t); + +int switch_ioctl(struct ifnet *, struct ifreq *, struct miibus_softc *, + u_long); +int switch_service(struct phy_softc *, struct miibus_softc *, int); + +int mii_attach(device_t, device_t *, struct ifnet *, ifm_change_cb_t, + ifm_stat_cb_t, int, int, int, int); +int mii_anar(int); +void mii_down(struct miibus_softc *); +int mii_mediachg(struct miibus_softc *); +void mii_tick(struct miibus_softc *); +void mii_pollstat(struct miibus_softc *); +int mii_phy_probe(device_t, device_t *, ifm_change_cb_t, ifm_stat_cb_t); +void mii_phy_add_media(struct phy_softc *); +struct phy_softc *mii_get_phy(struct miibus_softc *, int); + +int mii_phy_auto(struct phy_softc *); +int mii_phy_detach(device_t dev); +void mii_phy_down(struct phy_softc *); +void mii_phy_reset(struct phy_softc *); +void mii_phy_setmedia(struct phy_softc *sc); +void mii_phy_update(struct phy_softc *, int); +int mii_phy_tick(struct phy_softc *); + +const struct mii_phydesc * mii_phy_match(const struct phy_attach_args *pa, + const struct mii_phydesc *mpd); +const struct mii_phydesc * mii_phy_match_gen(const struct phy_attach_args *pa, + const struct mii_phydesc *mpd, size_t endlen); +int mii_phy_dev_probe(device_t dev, const struct mii_phydesc *mpd, int mrv); + +void ukswitch_status(struct phy_softc *); +#endif /* _KERNEL */ + +#endif /* _DEV_SWITCH_SWITCHVAR_H_ */ diff --git a/sys/dev/switch/ukswitch.c b/sys/dev/switch/ukswitch.c new file mode 100644 index 0000000..6222803 --- /dev/null +++ b/sys/dev/switch/ukswitch.c @@ -0,0 +1,210 @@ +/* $NetBSD: ukphy.c,v 1.2 1999/04/23 04:24:32 thorpej Exp $ */ + +/*- + * Copyright (c) 1998, 1999 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility, + * NASA Ames Research Center, and by Frank van der Linden. + * + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + */ + +/*- + * Copyright (c) 1997 Manuel Bouyer. 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 ``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 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. + */ + +#include +__FBSDID("$FreeBSD$"); + +/* + * driver for generic unknown switch PHYs + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include "switchbus_if.h" + +static int ukswitch_probe(device_t); +static int ukswitch_attach(device_t); +static int ukswitch_detach(device_t); +static int ukswitch_phy_get_flags(device_t, int, int *); + +struct ukswitch_softc { + struct phy_softc sc_phy; + __uint32_t sc_flags; /* switch port flags */ + device_t sc_pdev; /* parent device - miibus */ +}; + +static device_method_t ukswitch_methods[] = { + /* device interface */ + DEVMETHOD(device_probe, ukswitch_probe), + DEVMETHOD(device_attach, ukswitch_attach), + DEVMETHOD(device_detach, ukswitch_detach), + DEVMETHOD(device_shutdown, bus_generic_shutdown), + + /* switch bus interface */ + DEVMETHOD(switchbus_get_flags, ukswitch_phy_get_flags), + + { 0, 0 } +}; + +static devclass_t ukswitch_devclass; + +static driver_t ukswitch_driver = { + "ukswitch", + ukswitch_methods, + sizeof(struct ukswitch_softc) +}; + +DRIVER_MODULE(ukswitch, switchbus, ukswitch_driver, ukswitch_devclass, 0, 0); + +static int +ukswitch_probe(device_t dev) +{ + + /* + * We know something is here, so always match at a low priority. + */ + device_set_desc(dev, "Generic IEEE 802.3u media interface"); + return (BUS_PROBE_GENERIC); +} + +static int +ukswitch_attach(device_t dev) +{ + struct ukswitch_softc *sc; + struct phy_softc *phy; + struct phy_attach_args *pa; + struct switchbus_ivars *ivars; + struct miibus_softc *mii; + + sc = device_get_softc(dev); + pa = device_get_ivars(dev); + sc->sc_pdev = device_get_parent(dev); + mii = device_get_softc(sc->sc_pdev); + + phy = &sc->sc_phy; + LIST_INSERT_HEAD(&mii->mii_phys, phy, phy_list); + +// if (bootverbose) + device_printf(dev, "OUI 0x%06x, model 0x%04x, rev. %d\n", + MII_OUI(pa->phy_id1, pa->phy_id2), + MII_MODEL(pa->phy_id2), MII_REV(pa->phy_id2)); + + phy->phy_pdev = sc->sc_pdev; + phy->phy_dev = dev; + phy->phy_pdata = mii; + phy->phy_num = pa->phy_num; + phy->phy_inst = mii->mii_instance; + phy->phy_service = switch_service; + phy->phy_flags |= MIIF_NOISOLATE; + + mii->mii_instance++; + + mii_phy_reset(phy); + + ivars = device_get_ivars(sc->sc_pdev); + ifmedia_init(&phy->phy_media, IFM_IMASK, ivars->ifmedia_upd, + ivars->ifmedia_sts); + + phy->phy_capabilities = PHY_READ(phy, MII_BMSR) & pa->phy_capmask; + if (phy->phy_capabilities & BMSR_EXTSTAT) + phy->phy_extcapabilities = PHY_READ(phy, MII_EXTSR); + device_printf(dev, " "); + mii_phy_add_media(phy); + printf("\n"); + + SWITCHBUS_MEDIAINIT(sc->sc_pdev); + mii_phy_setmedia(phy); + + return (0); +} + +static int +ukswitch_detach(device_t dev) +{ + struct ukswitch_softc *sc; + struct phy_softc *phy; + + sc = device_get_softc(dev); + phy = &sc->sc_phy; + + mii_phy_down(phy); + ifmedia_removeall(&phy->phy_media); + phy->phy_pdev = NULL; + LIST_REMOVE(phy, phy_list); + + return (0); +} + +static int +ukswitch_phy_get_flags(device_t dev, int phy_num, int *flags) +{ + struct ukswitch_softc *sc; + struct phy_softc *phy; + struct miibus_softc *mii; + + sc = device_get_softc(dev); + mii = device_get_softc(sc->sc_pdev); + phy = mii_get_phy(mii, phy_num); + if (phy == NULL) + return (EINVAL); + + *flags = sc->sc_flags | SW_VLANBYPASS; + + return (0); +} + diff --git a/sys/dev/switch/ukswitch_subr.c b/sys/dev/switch/ukswitch_subr.c new file mode 100644 index 0000000..cd1273b --- /dev/null +++ b/sys/dev/switch/ukswitch_subr.c @@ -0,0 +1,121 @@ +/* $NetBSD: ukphy_subr.c,v 1.2 1998/11/05 04:08:02 thorpej Exp $ */ + +/*- + * Copyright (c) 1998 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility, + * NASA Ames Research Center, and by Frank van der Linden. + * + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + */ + +#include +__FBSDID("$FreeBSD: head/sys/dev/mii/ukphy_subr.c 204646 2010-03-03 17:55:51Z joel $"); + +/* + * Subroutines shared by the ukphy driver and other PHY drivers. + */ + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include "switchbus_if.h" + +/* + * Media status subroutine. If a PHY driver does media detection simply + * by decoding the NWay autonegotiation, use this routine. + */ +void +ukswitch_status(struct phy_softc *phy) +{ + struct ifmedia_entry *ife = phy->phy_media.ifm_cur; + int bmsr, bmcr, anlpar, gtcr, gtsr; + + phy->phy_media_status = IFM_AVALID; + phy->phy_media_active = IFM_ETHER; + + bmsr = PHY_READ(phy, MII_BMSR) | PHY_READ(phy, MII_BMSR); + if (bmsr & BMSR_LINK) + phy->phy_media_status |= IFM_ACTIVE; + + bmcr = PHY_READ(phy, MII_BMCR); + if (bmcr & BMCR_ISO) { + phy->phy_media_active |= IFM_NONE; + phy->phy_media_status = 0; + return; + } + + if (bmcr & BMCR_LOOP) + phy->phy_media_active |= IFM_LOOP; + + if (bmcr & BMCR_AUTOEN) { + /* + * NWay autonegotiation takes the highest-order common + * bit of the ANAR and ANLPAR (i.e. best media advertised + * both by us and our link partner). + */ + if ((bmsr & BMSR_ACOMP) == 0) { + /* Erg, still trying, I guess... */ + phy->phy_media_active |= IFM_NONE; + return; + } + + anlpar = PHY_READ(phy, MII_ANAR) & PHY_READ(phy, MII_ANLPAR); + if ((phy->phy_flags & MIIF_HAVE_GTCR) != 0 && + (phy->phy_extcapabilities & + (EXTSR_1000THDX | EXTSR_1000TFDX)) != 0) { + gtcr = PHY_READ(phy, MII_100T2CR); + gtsr = PHY_READ(phy, MII_100T2SR); + } else + gtcr = gtsr = 0; + + if ((gtcr & GTCR_ADV_1000TFDX) && (gtsr & GTSR_LP_1000TFDX)) + phy->phy_media_active |= IFM_1000_T|IFM_FDX; + else if ((gtcr & GTCR_ADV_1000THDX) && + (gtsr & GTSR_LP_1000THDX)) + phy->phy_media_active |= IFM_1000_T; + else if (anlpar & ANLPAR_TX_FD) + phy->phy_media_active |= IFM_100_TX|IFM_FDX; + else if (anlpar & ANLPAR_T4) + phy->phy_media_active |= IFM_100_T4; + else if (anlpar & ANLPAR_TX) + phy->phy_media_active |= IFM_100_TX; + else if (anlpar & ANLPAR_10_FD) + phy->phy_media_active |= IFM_10_T|IFM_FDX; + else if (anlpar & ANLPAR_10) + phy->phy_media_active |= IFM_10_T; + else + phy->phy_media_active |= IFM_NONE; + } else + phy->phy_media_active = ife->ifm_media; +} diff --git a/sys/mips/atheros/ar71xx_chip.c b/sys/mips/atheros/ar71xx_chip.c index ec179a1..b557bbd 100644 --- a/sys/mips/atheros/ar71xx_chip.c +++ b/sys/mips/atheros/ar71xx_chip.c @@ -140,51 +140,73 @@ ar71xx_chip_device_stopped(uint32_t mask) return ((reg & mask) == mask); } +static __inline void +ar71xx_chip_set_mii_speed(uint32_t reg, uint32_t ctrl) +{ + uint32_t val; + + val = ATH_READ_REG(reg); + val &= ~(MII_CTRL_SPEED_MASK << MII_CTRL_SPEED_SHIFT); + val |= (ctrl & MII_CTRL_SPEED_MASK) << MII_CTRL_SPEED_SHIFT; + ATH_WRITE_REG(reg, val); +} + /* Speed is either 10, 100 or 1000 */ static void -ar71xx_chip_set_pll_ge0(int speed) +ar71xx_chip_set_pll(int speed, int unit) { - uint32_t pll; + uint32_t ctrl, ctrl_reg, pll_reg, pll, pll_reg_shift; - switch(speed) { + switch (speed) { case 10: pll = PLL_ETH_INT_CLK_10; + ctrl = MII_CTRL_SPEED_10; break; case 100: pll = PLL_ETH_INT_CLK_100; + ctrl = MII_CTRL_SPEED_100; break; case 1000: pll = PLL_ETH_INT_CLK_1000; + ctrl = MII_CTRL_SPEED_1000; break; default: - printf("ar71xx_chip_set_pll_ge0: invalid speed %d\n", speed); + printf("%s: invalid speed %d\n", __func__, speed); return; } - ar71xx_write_pll(AR71XX_PLL_SEC_CONFIG, AR71XX_PLL_ETH_INT0_CLK, pll, AR71XX_PLL_ETH0_SHIFT); -} - -static void -ar71xx_chip_set_pll_ge1(int speed) -{ - uint32_t pll; - - switch(speed) { - case 10: - pll = PLL_ETH_INT_CLK_10; - break; - case 100: - pll = PLL_ETH_INT_CLK_100; + switch (unit) { + case 0: + ctrl_reg = AR71XX_MII0_CTRL; + pll_reg = AR71XX_PLL_ETH_INT0_CLK; + pll_reg_shift = AR71XX_PLL_ETH0_SHIFT; break; - case 1000: - pll = PLL_ETH_INT_CLK_1000; + case 1: + ctrl_reg = AR71XX_MII1_CTRL; + pll_reg = AR71XX_PLL_ETH_INT1_CLK; + pll_reg_shift = AR71XX_PLL_ETH1_SHIFT; break; default: - printf("ar71xx_chip_set_pll_ge1: invalid speed %d\n", speed); + printf("%s: invalid unit %d\n", __func__, unit); return; } - ar71xx_write_pll(AR71XX_PLL_SEC_CONFIG, AR71XX_PLL_ETH_INT1_CLK, pll, AR71XX_PLL_ETH1_SHIFT); + ar71xx_write_pll(AR71XX_PLL_SEC_CONFIG, pll_reg, pll, pll_reg_shift); + ar71xx_chip_set_mii_speed(ctrl_reg, ctrl); +} + +static void +ar71xx_chip_set_pll_ge0(int speed) +{ + + ar71xx_chip_set_pll(speed, 0); +} + +static void +ar71xx_chip_set_pll_ge1(int speed) +{ + + ar71xx_chip_set_pll(speed, 1); } static void diff --git a/sys/mips/atheros/ar71xx_machdep.c b/sys/mips/atheros/ar71xx_machdep.c index c8abc74..8a6ded2 100644 --- a/sys/mips/atheros/ar71xx_machdep.c +++ b/sys/mips/atheros/ar71xx_machdep.c @@ -66,6 +66,7 @@ uint32_t ar711_base_mac[ETHER_ADDR_LEN]; /* 4KB static data aread to keep a copy of the bootload env until the dynamic kenv is setup */ char boot1_env[4096]; +char board_model[64]; /* * We get a string in from Redboot with the all the arguments together, @@ -144,7 +145,7 @@ platform_start(__register_t a0 __unused, __register_t a1 __unused, __register_t a2 __unused, __register_t a3 __unused) { uint64_t platform_counter_freq; - int argc, i, count = 0; + int argc, i, board_mem, count = 0; char **argv, **envp, *var; vm_offset_t kernend; @@ -173,6 +174,19 @@ platform_start(__register_t a0 __unused, __register_t a1 __unused, } } + /* Parse cmd arguments */ + if (MIPS_IS_VALID_PTR(argv)) { + for (i = 0; i < argc; i++) { + if (strncmp(argv[i], "mem=", 4) == 0) { + if (sscanf(argv[i] + 4, "%dM", &board_mem) == 1) + realmem = btoc(board_mem * 1024 * 1024); + } else if (strncmp(argv[i], "board=", 6) == 0) { + strlcpy(board_model, argv[i] + 6, + sizeof(board_model)); + } + } + } + /* * Just wild guess. RedBoot let us down and didn't reported * memory size diff --git a/sys/mips/atheros/ar71xxreg.h b/sys/mips/atheros/ar71xxreg.h index da5fc5d..83ecd39 100644 --- a/sys/mips/atheros/ar71xxreg.h +++ b/sys/mips/atheros/ar71xxreg.h @@ -270,6 +270,13 @@ /* * GigE adapters region */ +#define AR71XX_MII0_CTRL 0x18070000 +#define AR71XX_MII1_CTRL 0x18070004 +#define MII_CTRL_SPEED_SHIFT 4 +#define MII_CTRL_SPEED_MASK 3 +#define MII_CTRL_SPEED_10 0 +#define MII_CTRL_SPEED_100 1 +#define MII_CTRL_SPEED_1000 2 #define AR71XX_MAC0_BASE 0x19000000 #define AR71XX_MAC1_BASE 0x1A000000 /* diff --git a/sys/mips/atheros/if_arge.c b/sys/mips/atheros/if_arge.c index a12ace8..5a04e57 100644 --- a/sys/mips/atheros/if_arge.c +++ b/sys/mips/atheros/if_arge.c @@ -67,15 +67,15 @@ __FBSDID("$FreeBSD$"); #include #include -#include +#include #include #include MODULE_DEPEND(arge, ether, 1, 1, 1); -MODULE_DEPEND(arge, miibus, 1, 1, 1); +MODULE_DEPEND(arge, switchbus, 1, 1, 1); -#include "miibus_if.h" +#include "switchbus_if.h" #include #include @@ -97,10 +97,12 @@ static int arge_ioctl(struct ifnet *, u_long, caddr_t); static void arge_init(void *); static void arge_init_locked(struct arge_softc *); static void arge_link_task(void *, int); +static void arge_stat_task(void *, int); static void arge_set_pll(struct arge_softc *, int, int); -static int arge_miibus_readreg(device_t, int, int); -static void arge_miibus_statchg(device_t); -static int arge_miibus_writereg(device_t, int, int, int); +static int arge_switchbus_readreg(device_t, int, int); +static void arge_switchbus_statchg(device_t); +static void arge_switchbus_linkchg(device_t); +static int arge_switchbus_writereg(device_t, int, int, int); static int arge_probe(device_t); static void arge_reset_dma(struct arge_softc *); static int arge_resume(device_t); @@ -121,12 +123,6 @@ static void arge_intr(void *); static int arge_intr_filter(void *); static void arge_tick(void *); -/* - * ifmedia callbacks for multiPHY MAC - */ -void arge_multiphy_mediastatus(struct ifnet *, struct ifmediareq *); -int arge_multiphy_mediachange(struct ifnet *); - static void arge_dmamap_cb(void *, bus_dma_segment_t *, int, int); static int arge_dma_alloc(struct arge_softc *); static void arge_dma_free(struct arge_softc *); @@ -147,9 +143,10 @@ static device_method_t arge_methods[] = { DEVMETHOD(bus_driver_added, bus_generic_driver_added), /* MII interface */ - DEVMETHOD(miibus_readreg, arge_miibus_readreg), - DEVMETHOD(miibus_writereg, arge_miibus_writereg), - DEVMETHOD(miibus_statchg, arge_miibus_statchg), + DEVMETHOD(switchbus_readreg, arge_switchbus_readreg), + DEVMETHOD(switchbus_writereg, arge_switchbus_writereg), + DEVMETHOD(switchbus_statchg, arge_switchbus_statchg), + DEVMETHOD(switchbus_linkchg, arge_switchbus_linkchg), { 0, 0 } }; @@ -163,7 +160,7 @@ static driver_t arge_driver = { static devclass_t arge_devclass; DRIVER_MODULE(arge, nexus, arge_driver, arge_devclass, 0, 0); -DRIVER_MODULE(miibus, arge, miibus_driver, miibus_devclass, 0, 0); +DRIVER_MODULE(switchbus, arge, switchbus_driver, switchbus_devclass, 0, 0); /* * RedBoot passes MAC address to entry point as environment @@ -171,10 +168,11 @@ DRIVER_MODULE(miibus, arge, miibus_driver, miibus_devclass, 0, 0); */ extern uint32_t ar711_base_mac[ETHER_ADDR_LEN]; -static struct mtx miibus_mtx; +extern char board_model[64]; -MTX_SYSINIT(miibus_mtx, &miibus_mtx, "arge mii lock", MTX_DEF); +static struct mtx switchbus_mtx; +MTX_SYSINIT(switchbus_mtx, &switchbus_mtx, "arge mii lock", MTX_DEF); /* * Flushes all @@ -182,6 +180,7 @@ MTX_SYSINIT(miibus_mtx, &miibus_mtx, "arge mii lock", MTX_DEF); static void arge_flush_ddr(struct arge_softc *sc) { + if (sc->arge_mac_unit == 0) ar71xx_device_flush_ddr_ge0(); else @@ -262,8 +261,10 @@ arge_attach(device_t dev) if (hint == 1000) sc->arge_media_type = IFM_1000_T; - else + else if (hint == 100) sc->arge_media_type = IFM_100_TX; + else + sc->arge_media_type = 0; if (resource_int_value(device_get_name(dev), device_get_unit(dev), "fduplex", &hint) != 0) @@ -278,8 +279,10 @@ arge_attach(device_t dev) mtx_init(&sc->arge_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK, MTX_DEF); + callout_init_mtx(&sc->arge_link_callout, &sc->arge_mtx, 0); callout_init_mtx(&sc->arge_stat_callout, &sc->arge_mtx, 0); TASK_INIT(&sc->arge_link_task, 0, arge_link_task, sc); + TASK_INIT(&sc->arge_stat_task, 0, arge_stat_task, sc); /* Map control/status registers. */ sc->arge_rid = 0; @@ -354,8 +357,15 @@ arge_attach(device_t dev) eaddr[5] = (rnd >> 8) & 0xff; } - if (sc->arge_mac_unit != 0) - eaddr[5] += sc->arge_mac_unit; + if (strncmp(board_model, "450", 3) == 0 || + strncmp(board_model, "433", 3) == 0 || + strncmp(board_model, "493", 3) == 0) { + if (sc->arge_mac_unit == 0) + eaddr[5] += 1; + } else { + if (sc->arge_mac_unit != 0) + eaddr[5] += sc->arge_mac_unit; + } if (arge_dma_alloc(sc) != 0) { error = ENXIO; @@ -413,37 +423,27 @@ arge_attach(device_t dev) * Check if we have single-PHY MAC or multi-PHY */ phys_total = 0; - for (i = 0; i < ARGE_NPHY; i++) - if (phymask & (1 << i)) - phys_total ++; + for (i = 0; i < ARGE_NPHY; i++) { + if (phymask & (1 << i)) { + + /* Do MII setup. */ + error = mii_attach(dev, &sc->arge_switchbus, ifp, + arge_ifmedia_upd, arge_ifmedia_sts, + BMSR_DEFCAPMASK, i, MII_OFFSET_ANY, 0); + if (error != 0) { + device_printf(dev, "attaching PHYs failed\n"); + goto fail; + } + + phys_total++; + } + } if (phys_total == 0) { error = EINVAL; goto fail; } - if (phys_total == 1) { - /* Do MII setup. */ - error = mii_attach(dev, &sc->arge_miibus, ifp, - arge_ifmedia_upd, arge_ifmedia_sts, BMSR_DEFCAPMASK, - MII_PHY_ANY, MII_OFFSET_ANY, 0); - if (error != 0) { - device_printf(dev, "attaching PHYs failed\n"); - goto fail; - } - } - else { - ifmedia_init(&sc->arge_ifmedia, 0, - arge_multiphy_mediachange, - arge_multiphy_mediastatus); - ifmedia_add(&sc->arge_ifmedia, - IFM_ETHER | sc->arge_media_type | sc->arge_duplex_mode, - 0, NULL); - ifmedia_set(&sc->arge_ifmedia, - IFM_ETHER | sc->arge_media_type | sc->arge_duplex_mode); - arge_set_pll(sc, sc->arge_media_type, sc->arge_duplex_mode); - } - /* Call MI attach routine. */ ether_ifattach(ifp, eaddr); @@ -486,12 +486,13 @@ arge_detach(device_t dev) arge_stop(sc); ARGE_UNLOCK(sc); + taskqueue_drain(taskqueue_swi, &sc->arge_stat_task); taskqueue_drain(taskqueue_swi, &sc->arge_link_task); ether_ifdetach(ifp); } - if (sc->arge_miibus) - device_delete_child(dev, sc->arge_miibus); + if (sc->arge_switchbus) + device_delete_child(dev, sc->arge_switchbus); bus_generic_detach(dev); @@ -544,17 +545,13 @@ arge_shutdown(device_t dev) } static int -arge_miibus_readreg(device_t dev, int phy, int reg) +arge_switchbus_readreg(device_t dev, int phy, int reg) { - struct arge_softc * sc = device_get_softc(dev); int i, result; - uint32_t addr = (phy << MAC_MII_PHY_ADDR_SHIFT) - | (reg & MAC_MII_REG_MASK); - - if ((sc->arge_phymask & (1 << phy)) == 0) - return (0); + uint32_t addr = + (phy << MAC_MII_PHY_ADDR_SHIFT) | (reg & MAC_MII_REG_MASK); - mtx_lock(&miibus_mtx); + mtx_lock(&switchbus_mtx); ARGE_MII_WRITE(AR71XX_MAC_MII_CMD, MAC_MII_CMD_WRITE); ARGE_MII_WRITE(AR71XX_MAC_MII_ADDR, addr); ARGE_MII_WRITE(AR71XX_MAC_MII_CMD, MAC_MII_CMD_READ); @@ -565,7 +562,7 @@ arge_miibus_readreg(device_t dev, int phy, int reg) DELAY(5); if (i < 0) { - mtx_unlock(&miibus_mtx); + mtx_unlock(&switchbus_mtx); dprintf("%s timedout\n", __func__); /* XXX: return ERRNO istead? */ return (-1); @@ -573,7 +570,7 @@ arge_miibus_readreg(device_t dev, int phy, int reg) result = ARGE_MII_READ(AR71XX_MAC_MII_STATUS) & MAC_MII_STATUS_MASK; ARGE_MII_WRITE(AR71XX_MAC_MII_CMD, MAC_MII_CMD_WRITE); - mtx_unlock(&miibus_mtx); + mtx_unlock(&switchbus_mtx); dprintf("%s: phy=%d, reg=%02x, value[%08x]=%04x\n", __func__, phy, reg, addr, result); @@ -582,21 +579,16 @@ arge_miibus_readreg(device_t dev, int phy, int reg) } static int -arge_miibus_writereg(device_t dev, int phy, int reg, int data) +arge_switchbus_writereg(device_t dev, int phy, int reg, int data) { - struct arge_softc * sc = device_get_softc(dev); int i; uint32_t addr = (phy << MAC_MII_PHY_ADDR_SHIFT) | (reg & MAC_MII_REG_MASK); - - if ((sc->arge_phymask & (1 << phy)) == 0) - return (-1); - dprintf("%s: phy=%d, reg=%02x, value=%04x\n", __func__, phy, reg, data); - mtx_lock(&miibus_mtx); + mtx_lock(&switchbus_mtx); ARGE_MII_WRITE(AR71XX_MAC_MII_ADDR, addr); ARGE_MII_WRITE(AR71XX_MAC_MII_CONTROL, data); @@ -605,7 +597,7 @@ arge_miibus_writereg(device_t dev, int phy, int reg, int data) MAC_MII_INDICATOR_BUSY) && (i--)) DELAY(5); - mtx_unlock(&miibus_mtx); + mtx_unlock(&switchbus_mtx); if (i < 0) { dprintf("%s timedout\n", __func__); @@ -617,9 +609,9 @@ arge_miibus_writereg(device_t dev, int phy, int reg, int data) } static void -arge_miibus_statchg(device_t dev) +arge_switchbus_linkchg(device_t dev) { - struct arge_softc *sc; + struct arge_softc *sc; sc = device_get_softc(dev); taskqueue_enqueue(taskqueue_swi, &sc->arge_link_task); @@ -629,14 +621,13 @@ static void arge_link_task(void *arg, int pending) { struct arge_softc *sc; - struct mii_data *mii; + struct miibus_softc *mii; struct ifnet *ifp; - uint32_t media, duplex; sc = (struct arge_softc *)arg; + mii = device_get_softc(sc->arge_switchbus); ARGE_LOCK(sc); - mii = device_get_softc(sc->arge_miibus); ifp = sc->arge_ifp; if (mii == NULL || ifp == NULL || (ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) { @@ -644,18 +635,53 @@ arge_link_task(void *arg, int pending) return; } - if (mii->mii_media_status & IFM_ACTIVE) { + if (mii->mii_media_status & IFM_AVALID) { + if (mii->mii_media_status & IFM_ACTIVE) + sc->arge_link_status = 1; + else + sc->arge_link_status = 0; + } + ARGE_UNLOCK(sc); +} - media = IFM_SUBTYPE(mii->mii_media_active); +static void +arge_switchbus_statchg(device_t dev) +{ + struct arge_softc *sc; - if (media != IFM_NONE) { - sc->arge_link_status = 1; - duplex = mii->mii_media_active & IFM_GMASK; - arge_set_pll(sc, media, duplex); - } - } else - sc->arge_link_status = 0; + sc = device_get_softc(dev); + taskqueue_enqueue(taskqueue_swi, &sc->arge_stat_task); +} + +static void +arge_stat_task(void *arg, int pending) +{ + struct arge_softc *sc; + struct miibus_softc *mii; + struct ifnet *ifp; + uint32_t media, duplex; + + sc = (struct arge_softc *)arg; + mii = device_get_softc(sc->arge_switchbus); + ARGE_LOCK(sc); + ifp = sc->arge_ifp; + if (mii == NULL || ifp == NULL || + (ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) { + ARGE_UNLOCK(sc); + return; + } + + media = IFM_SUBTYPE(mii->mii_media_active); + if (media != IFM_NONE) { + sc->arge_link_status = 1; + duplex = mii->mii_media_active & IFM_GMASK; + if (sc->arge_media_type) + arge_set_pll(sc, sc->arge_media_type, + sc->arge_duplex_mode); + else + arge_set_pll(sc, media, duplex); + } ARGE_UNLOCK(sc); } @@ -761,7 +787,7 @@ static void arge_init_locked(struct arge_softc *sc) { struct ifnet *ifp = sc->arge_ifp; - struct mii_data *mii; + struct miibus_softc *mii; ARGE_LOCK_ASSERT(sc); @@ -780,24 +806,16 @@ arge_init_locked(struct arge_softc *sc) arge_reset_dma(sc); - - if (sc->arge_miibus) { - sc->arge_link_status = 0; - mii = device_get_softc(sc->arge_miibus); - mii_mediachg(mii); - } - else { - /* - * Sun always shines over multiPHY interface - */ - sc->arge_link_status = 1; - } + /* Force the miibus to update the media status */ + sc->arge_link_status = 0; + mii = device_get_softc(sc->arge_switchbus); + mii_mediachg(mii); ifp->if_drv_flags |= IFF_DRV_RUNNING; ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; - if (sc->arge_miibus) - callout_reset(&sc->arge_stat_callout, hz, arge_tick, sc); + callout_reset(&sc->arge_link_callout, hz, arge_tick, sc); + callout_reset(&sc->arge_stat_callout, hz, arge_tick, sc); ARGE_WRITE(sc, AR71XX_DMA_TX_DESC, ARGE_TX_RING_ADDR(sc, 0)); ARGE_WRITE(sc, AR71XX_DMA_RX_DESC, ARGE_RX_RING_ADDR(sc, 0)); @@ -990,8 +1008,8 @@ arge_stop(struct arge_softc *sc) ifp = sc->arge_ifp; ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); - if (sc->arge_miibus) - callout_stop(&sc->arge_stat_callout); + callout_stop(&sc->arge_link_callout); + callout_stop(&sc->arge_stat_callout); /* mask out interrupts */ ARGE_WRITE(sc, AR71XX_DMA_INTR, 0); @@ -1003,9 +1021,9 @@ arge_stop(struct arge_softc *sc) static int arge_ioctl(struct ifnet *ifp, u_long command, caddr_t data) { - struct arge_softc *sc = ifp->if_softc; + struct arge_softc *sc = ifp->if_softc; struct ifreq *ifr = (struct ifreq *) data; - struct mii_data *mii; + struct miibus_softc *mii = device_get_softc(sc->arge_switchbus); int error; #ifdef DEVICE_POLLING int mask; @@ -1038,14 +1056,14 @@ arge_ioctl(struct ifnet *ifp, u_long command, caddr_t data) /* XXX: implement SIOCDELMULTI */ error = 0; break; - case SIOCGIFMEDIA: - case SIOCSIFMEDIA: - if (sc->arge_miibus) { - mii = device_get_softc(sc->arge_miibus); - error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, command); - } - else - error = ifmedia_ioctl(ifp, ifr, &sc->arge_ifmedia, command); + case SIOCAIFSWVLAN: + case SIOCDIFSWVLAN: + case SIOCGIFSWVLAN: + case SIOCGIFSWFLAGS: + case SIOCSIFSWFLAGS: + case SIOCGIFSWMEDIA: + case SIOCSIFSWMEDIA: + error = switch_ioctl(ifp, ifr, mii, command); break; case SIOCSIFCAP: /* XXX: Check other capabilities */ @@ -1085,18 +1103,14 @@ arge_ioctl(struct ifnet *ifp, u_long command, caddr_t data) static int arge_ifmedia_upd(struct ifnet *ifp) { - struct arge_softc *sc; - struct mii_data *mii; - struct mii_softc *miisc; + struct arge_softc *sc; + struct miibus_softc *mii; int error; sc = ifp->if_softc; + mii = device_get_softc(sc->arge_switchbus); + ARGE_LOCK(sc); - mii = device_get_softc(sc->arge_miibus); - if (mii->mii_instance) { - LIST_FOREACH(miisc, &mii->mii_phys, mii_list) - mii_phy_reset(miisc); - } error = mii_mediachg(mii); ARGE_UNLOCK(sc); @@ -1109,13 +1123,31 @@ arge_ifmedia_upd(struct ifnet *ifp) static void arge_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr) { - struct arge_softc *sc = ifp->if_softc; - struct mii_data *mii; + struct arge_softc *sc = ifp->if_softc; + struct miibus_softc *mii; + struct phy_softc *phy; + + mii = device_get_softc(sc->arge_switchbus); - mii = device_get_softc(sc->arge_miibus); ARGE_LOCK(sc); mii_pollstat(mii); ARGE_UNLOCK(sc); + + if (ifmr->ifm_flags & IFM_HAVE_PHY) { + /* return the phy status */ + phy = LIST_FIRST(&mii->mii_phys); + for (; phy != NULL; phy = LIST_NEXT(phy, phy_list)) + if (ifmr->ifm_phy == phy->phy_num) + break; + if (phy != NULL) { + ifmr->ifm_active = phy->phy_media_active; + ifmr->ifm_status = phy->phy_media_status; + return; + } + /* fallthrough */ + } + + /* return the interface status */ ifmr->ifm_active = mii->mii_media_active; ifmr->ifm_status = mii->mii_media_status; } @@ -1747,7 +1779,7 @@ arge_intr(void *arg) * RX overrun disables the receiver. * Clear indication and re-enable rx. */ - if ( status & DMA_INTR_RX_OVERFLOW) { + if (status & DMA_INTR_RX_OVERFLOW) { ARGE_WRITE(sc, AR71XX_DMA_RX_STATUS, DMA_RX_STATUS_OVERFLOW); ARGE_WRITE(sc, AR71XX_DMA_RX_CONTROL, DMA_RX_CONTROL_EN); } @@ -1777,51 +1809,17 @@ arge_intr(void *arg) ARGE_WRITE(sc, AR71XX_DMA_INTR, DMA_INTR_ALL); } - static void arge_tick(void *xsc) { struct arge_softc *sc = xsc; - struct mii_data *mii; + struct miibus_softc *mii; ARGE_LOCK_ASSERT(sc); - if (sc->arge_miibus) { - mii = device_get_softc(sc->arge_miibus); - mii_tick(mii); - callout_reset(&sc->arge_stat_callout, hz, arge_tick, sc); - } -} - -int -arge_multiphy_mediachange(struct ifnet *ifp) -{ - struct arge_softc *sc = ifp->if_softc; - struct ifmedia *ifm = &sc->arge_ifmedia; - struct ifmedia_entry *ife = ifm->ifm_cur; - - if (IFM_TYPE(ifm->ifm_media) != IFM_ETHER) - return (EINVAL); - - if (IFM_SUBTYPE(ife->ifm_media) == IFM_AUTO) { - device_printf(sc->arge_dev, - "AUTO is not supported for multiphy MAC"); - return (EINVAL); - } - - /* - * Ignore everything - */ - return (0); -} - -void -arge_multiphy_mediastatus(struct ifnet *ifp, struct ifmediareq *ifmr) -{ - struct arge_softc *sc = ifp->if_softc; - - ifmr->ifm_status = IFM_AVALID | IFM_ACTIVE; - ifmr->ifm_active = IFM_ETHER | sc->arge_media_type | - sc->arge_duplex_mode; + mii = device_get_softc(sc->arge_switchbus); + mii_tick(mii); + callout_reset(&sc->arge_link_callout, hz, arge_tick, sc); + callout_reset(&sc->arge_stat_callout, hz, arge_tick, sc); } diff --git a/sys/mips/atheros/if_argevar.h b/sys/mips/atheros/if_argevar.h index 4c336af..4324541 100644 --- a/sys/mips/atheros/if_argevar.h +++ b/sys/mips/atheros/if_argevar.h @@ -137,12 +137,14 @@ struct arge_softc { int arge_rid; struct resource *arge_irq; void *arge_intrhand; - device_t arge_miibus; + device_t arge_switchbus; bus_dma_tag_t arge_parent_tag; bus_dma_tag_t arge_tag; struct mtx arge_mtx; + struct callout arge_link_callout; struct callout arge_stat_callout; struct task arge_link_task; + struct task arge_stat_task; struct arge_chain_data arge_cdata; struct arge_ring_data arge_rdata; int arge_link_status; diff --git a/sys/mips/conf/RSPRO b/sys/mips/conf/RSPRO new file mode 100644 index 0000000..f851132 --- /dev/null +++ b/sys/mips/conf/RSPRO @@ -0,0 +1,121 @@ +# +# RSPRO -- Kernel configuration file for FreeBSD/mips for Ubiquiti RSPRO +# +# $FreeBSD$ +# + +ident RSPRO +makeoptions KERNLOADADDR=0x80050000 +options HZ=1000 + +hints "RSPRO.hints" +include "../atheros/std.ar71xx" + +makeoptions DEBUG=-g #Build kernel with gdb(1) debug symbols +makeoptions MODULES_OVERRIDE="" + +options ALT_BREAK_TO_DEBUGGER +options DDB +options KDB +options KTRACE + +options SCHED_4BSD #4BSD scheduler +options INET #InterNETworking +options NFSCLIENT #Network Filesystem Client +options NFS_ROOT #NFS usable as /, requires NFSCLIENT +options PSEUDOFS #Pseudo-filesystem framework +options _KPOSIX_PRIORITY_SCHEDULING #Posix P1003_1B real-time extensions + +# options NFS_LEGACYRPC +# Debugging for use in -current +options DEADLKRES +options INVARIANTS +options INVARIANT_SUPPORT +options WITNESS +options WITNESS_SKIPSPIN +options FFS #Berkeley Fast Filesystem +options SOFTUPDATES #Enable FFS soft updates support +options UFS_ACL #Support for access control lists +options UFS_DIRHASH #Improve performance on big directories + +options BOOTP +options BOOTP_NFSROOT +options BOOTP_NFSV3 +options BOOTP_WIRED_TO=arge0 +options BOOTP_COMPAT +options ROOTDEVNAME=\"nfs:192.168.10.1:/mnt/bsd\" +#options ROOTDEVNAME=\"ufs:/dev/da1p2\" +#options ROOTDEVNAME=\"ufs:/dev/gpt/root\" + +device pci + +# Wireless NIC cards +options IEEE80211_DEBUG +options IEEE80211_SUPPORT_MESH +options IEEE80211_SUPPORT_TDMA +options IEEE80211_AMPDU_AGE # age frames in AMPDU reorder q's +device wlan # 802.11 support +device wlan_wep # 802.11 WEP support +device wlan_ccmp # 802.11 CCMP support +device wlan_tkip # 802.11 TKIP support +device wlan_xauth + +device ath # Atheros pci/cardbus NIC's +options ATH_DEBUG +device ath_hal +option AH_SUPPORT_AR5416 +option AH_RXCFG_SDMAMW_4BYTES # See NOTES for details of this WAR +device ath_rate_sample + +device switch +device arge + +device usb +options USB_HOST_ALIGN=32 +options USB_EHCI_BIG_ENDIAN_DESC # handle big-endian byte order +# options USB_DEBUG +device ohci +device ehci + +device spibus +device ar71xx_spi +device pcf2123_rtc +device mx25l +device geom_redboot + +device ar71xx_wdog + +device uart + +device loop +device ether +device vlan +device md +device bpf +device random +device if_bridge + +device umass +device scbus +device da + +options GEOM_PART_GPT +options GEOM_LABEL + +device gpio +device gpioled +device gpioiic +device iicbus +device iicbb +device iic + +# network gateway - firewall +options DUMMYNET +options LIBALIAS +options IPDIVERT +options IPSTEALTH +options IPFIREWALL +options IPFIREWALL_NAT +options IPFIREWALL_VERBOSE +options IPFIREWALL_FORWARD +options IPFIREWALL_DEFAULT_TO_ACCEPT diff --git a/sys/mips/conf/RSPRO.hints b/sys/mips/conf/RSPRO.hints new file mode 100644 index 0000000..0196e2a --- /dev/null +++ b/sys/mips/conf/RSPRO.hints @@ -0,0 +1,80 @@ +# $FreeBSD: head/sys/mips/conf/AR71XX.hints 202839 2010-01-22 22:14:12Z gonzo $ +hint.apb.0.at="nexus0" +hint.apb.0.irq=4 + +# uart0 +hint.uart.0.at="apb0" +# see atheros/uart_cpu_ar71xx.c why +3 +hint.uart.0.maddr=0x18020003 +hint.uart.0.msize=0x18 +hint.uart.0.irq=3 + +# ohci +hint.ohci.0.at="apb0" +hint.ohci.0.maddr=0x1c000000 +hint.ohci.0.msize=0x01000000 +hint.ohci.0.irq=6 + +# ehci +hint.ehci.0.at="nexus0" +hint.ehci.0.maddr=0x1b000000 +hint.ehci.0.msize=0x01000000 +hint.ehci.0.irq=1 + +# pci +hint.pcib.0.at="nexus0" +hint.pcib.0.irq=0 + +# arge0 +hint.arge.0.at="nexus0" +hint.arge.0.maddr=0x19000000 +hint.arge.0.msize=0x1000 +hint.arge.0.irq=2 +# PHY4 = 1 << 4 +hint.arge.0.phymask=0x10 + +# arge1 +hint.arge.1.at="nexus0" +hint.arge.1.maddr=0x1a000000 +hint.arge.1.msize=0x1000 +hint.arge.1.irq=3 +# PHY1, PHY2, PHY3 +hint.arge.1.phymask=0x0e +hint.arge.1.media=1000 +hint.arge.1.fduplex=1 +# RSPRO _really_ need the above two lines ! + +# SPIBUS +hint.spi.0.at="nexus0" +hint.spi.0.maddr=0x1f000000 +hint.spi.0.msize=0x10 + +# SPI flash +hint.mx25l.0.at="spibus0" +hint.mx25l.0.cs=1 + +# RTC shares the same bus with mx25l. +# CE low for flash, CE high for RTC +hint.rtc.0.at="spibus0" +hint.rtc.0.cs=0 + +# Watchdog +hint.ar71xx_wdog.0.at="nexus0" + +# GPIO +hint.gpio.0.at="apb0" +hint.gpio.0.maddr=0x18040000 +hint.gpio.0.msize=0x1000 +hint.gpio.0.irq=2 + +# RF led - pin 2 +hint.gpioled.0.at="gpiobus0" +hint.gpioled.0.name="rfled" +hint.gpioled.0.pins=0x0004 + +# gpio i2c +# Pin 1 - SCL +# Pin 2 - SDA +hint.gpioiic.0.at="gpiobus0" +hint.gpioiic.0.pins=0x0003 + diff --git a/sys/net/if.h b/sys/net/if.h index d291da8..10ebebe 100644 --- a/sys/net/if.h +++ b/sys/net/if.h @@ -349,12 +349,29 @@ struct ifmediareq { char ifm_name[IFNAMSIZ]; /* if name, e.g. "en0" */ int ifm_current; /* current media options */ int ifm_mask; /* don't care mask */ + int ifm_phy; /* phy number */ + int ifm_flags; /* request flags */ int ifm_status; /* media status */ int ifm_active; /* active options */ int ifm_count; /* # entries in ifm_ulist array */ int *ifm_ulist; /* media words */ }; +struct ifswmediareq { + char ifswm_name[IFNAMSIZ]; /* if name, e.g. "en0" */ + int ifswm_phy; /* phy number for this request */ + struct ifreq ifswm_ifreq; /* media change request */ +}; + +struct ifswitchreq { + char ifsw_name[IFNAMSIZ]; /* if name, e.g. "en0" */ + int ifsw_phy; /* switch port */ + int ifsw_vlan; /* vlan to add/del */ + int ifsw_flags; /* port flags */ + int ifsw_count; /* # entries in ifsw_vlist array */ + int *ifsw_vlist; /* vlan list */ +}; + struct ifdrv { char ifd_name[IFNAMSIZ]; /* if name, e.g. "en0" */ unsigned long ifd_cmd; diff --git a/sys/sys/sockio.h b/sys/sys/sockio.h index 4c1c483..573a170 100644 --- a/sys/sys/sockio.h +++ b/sys/sys/sockio.h @@ -125,4 +125,12 @@ #define SIOCDIFGROUP _IOW('i', 137, struct ifgroupreq) /* delete ifgroup */ #define SIOCGIFGMEMB _IOWR('i', 138, struct ifgroupreq) /* get members */ +#define SIOCGIFSWMEDIA _IOWR('i', 143, struct ifmediareq) /* get sw media */ +#define SIOCSIFSWMEDIA _IOWR('i', 144, struct ifswmediareq) /* set sw media */ +#define SIOCAIFSWVLAN _IOWR('i', 145, struct ifswitchreq) /* add sw vlan */ +#define SIOCDIFSWVLAN _IOWR('i', 146, struct ifswitchreq) /* remove sw vlan */ +#define SIOCGIFSWVLAN _IOWR('i', 147, struct ifswitchreq) /* get port vlans */ +#define SIOCSIFSWFLAGS _IOW('i', 148, struct ifswitchreq) /* set port flags */ +#define SIOCGIFSWFLAGS _IOWR('i', 149, struct ifswitchreq) /* get port flags */ + #endif /* !_SYS_SOCKIO_H_ */ diff --git a/sys/tools/miidevs2h.awk b/sys/tools/miidevs2h.awk index 01d3a5c..c5f0de2 100644 --- a/sys/tools/miidevs2h.awk +++ b/sys/tools/miidevs2h.awk @@ -97,6 +97,10 @@ BEGIN { nmodels = nouis = 0 hfile="miidevs.h" } +{ + if (FILENAME ~ /switchdevs/) + hfile="switchdevs.h" +} NR == 1 { VERSION = $0 gsub("\\$", "", VERSION)