Add driver for Attansic PHYs.
authorSepherosa Ziehau <sephe@dragonflybsd.org>
Mon, 29 Dec 2008 12:08:10 +0000 (20:08 +0800)
committerSepherosa Ziehau <sephe@dragonflybsd.org>
Mon, 29 Dec 2008 12:11:55 +0000 (20:11 +0800)
Obtained-from: FreeBSD (yongari@freebsd.org)

sys/conf/files
sys/dev/netif/mii_layer/Makefile
sys/dev/netif/mii_layer/atphy.c [new file with mode: 0644]
sys/dev/netif/mii_layer/atphyreg.h [new file with mode: 0644]

index 053b72e..8ee7bfa 100644 (file)
@@ -319,6 +319,7 @@ dev/netif/mii_layer/rgephy.c        optional miibus
 dev/netif/mii_layer/ip1000phy.c        optional miibus
 dev/netif/mii_layer/truephy.c  optional miibus
 dev/netif/mii_layer/jmphy.c    optional miibus
+dev/netif/mii_layer/atphy.c    optional miibus
 dev/netif/mii_layer/miibus_if.m                optional miibus
 dev/raid/mlx/mlx_disk.c                optional mlx
 dev/raid/mlx/mlx_pci.c         optional mlx
index 85dbde1..375517c 100644 (file)
@@ -6,7 +6,7 @@ SRCS    = mii.c mii_physubr.c ukphy.c ukphy_subr.c bus_if.h pci_if.h
 SRCS   += miibus_if.h device_if.h miibus_if.c e1000phy.c exphy.c nsphy.c
 SRCS   += mlphy.c tlphy.c rlphy.c amphy.c dcphy.c pnphy.c nsgphy.c
 SRCS   += pnaphy.c brgphy.c xmphy.c inphy.c lxtphy.c qsphy.c acphy.c
-SRCS   += ruephy.c rgephy.c ciphy.c ip1000phy.c truephy.c jmphy.c
+SRCS   += ruephy.c rgephy.c ciphy.c ip1000phy.c truephy.c jmphy.c atphy.c
 
 .include "./Makefile.miidevs"
 
diff --git a/sys/dev/netif/mii_layer/atphy.c b/sys/dev/netif/mii_layer/atphy.c
new file mode 100644 (file)
index 0000000..57f968d
--- /dev/null
@@ -0,0 +1,415 @@
+/*-
+ * Copyright (c) 2008, Pyun YongHyeon <yongari@FreeBSD.org>
+ * 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 unmodified, this list of conditions, and the following
+ *    disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: src/sys/dev/mii/atphy.c,v 1.3 2008/10/25 06:39:17 yongari Exp $
+ */
+
+/*
+ * Driver for the Attansic/Atheros F1 10/100/1000 PHY.
+ */
+
+#include <sys/param.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+
+#include <net/if.h>
+#include <net/if_media.h>
+
+#include <dev/netif/mii_layer/mii.h>
+#include <dev/netif/mii_layer/miivar.h>
+#include <dev/netif/mii_layer/miidevs.h>
+#include <dev/netif/mii_layer/atphyreg.h>
+
+#include "miibus_if.h"
+
+static int     atphy_probe(device_t);
+static int     atphy_attach(device_t);
+static int     atphy_service(struct mii_softc *, struct mii_data *, int);
+static void    atphy_status(struct mii_softc *);
+static void    atphy_reset(struct mii_softc *);
+static uint16_t        atphy_anar(struct ifmedia_entry *);
+static void    atphy_auto(struct mii_softc *);
+
+static device_method_t atphy_methods[] = {
+       /* Device interface. */
+       DEVMETHOD(device_probe,         atphy_probe),
+       DEVMETHOD(device_attach,        atphy_attach),
+       DEVMETHOD(device_detach,        ukphy_detach),
+       DEVMETHOD(device_shutdown,      bus_generic_shutdown),
+       { 0, 0 }
+};
+
+static const struct mii_phydesc atphys[] = {
+       MII_PHYDESC(ATHEROS,    F1),
+       MII_PHYDESC(ATHEROS,    F2),
+       MII_PHYDESC_NULL
+};
+
+static devclass_t atphy_devclass;
+
+static driver_t atphy_driver = {
+       "atphy",
+       atphy_methods,
+       sizeof(struct mii_softc)
+};
+
+DRIVER_MODULE(atphy, miibus, atphy_driver, atphy_devclass, 0, 0);
+
+static int
+atphy_probe(device_t dev)
+{
+       struct mii_attach_args *ma = device_get_ivars(dev);
+       const struct mii_phydesc *mpd;
+
+       mpd = mii_phy_match(ma, atphys);
+       if (mpd != NULL) {
+               device_set_desc(dev, mpd->mpd_name);
+               return 0;
+       }
+       return ENXIO;
+}
+
+static int
+atphy_attach(device_t dev)
+{
+       struct mii_softc *sc;
+       struct mii_attach_args *ma;
+       struct mii_data *mii;
+
+       sc = device_get_softc(dev);
+       ma = device_get_ivars(dev);
+
+       mii_softc_init(sc, ma);
+       sc->mii_dev = device_get_parent(dev);
+       mii = device_get_softc(sc->mii_dev);
+       LIST_INSERT_HEAD(&mii->mii_phys, sc, mii_list);
+
+       sc->mii_inst = mii->mii_instance;
+       sc->mii_service = atphy_service;
+       sc->mii_pdata = mii;
+       sc->mii_anegticks = MII_ANEGTICKS_GIGE;
+
+       mii->mii_instance++;
+
+       atphy_reset(sc);
+
+       sc->mii_capabilities = PHY_READ(sc, MII_BMSR) & ma->mii_capmask;
+       if (sc->mii_capabilities & BMSR_EXTSTAT)
+               sc->mii_extcapabilities = PHY_READ(sc, MII_EXTSR);
+
+       device_printf(dev, " ");
+       if ((sc->mii_capabilities & BMSR_MEDIAMASK) == 0 &&
+           (sc->mii_extcapabilities & EXTSR_MEDIAMASK) == 0)
+               kprintf("no media present");
+       else
+               mii_phy_add_media(sc);
+       kprintf("\n");
+
+       MIIBUS_MEDIAINIT(sc->mii_dev);
+       return 0;
+}
+
+static int
+atphy_service(struct mii_softc *sc, struct mii_data *mii, int cmd)
+{
+       struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
+       uint16_t anar, bmcr, bmsr;
+
+       switch (cmd) {
+       case MII_POLLSTAT:
+               /*
+                * If we're not polling our PHY instance, just return.
+                */
+               if (IFM_INST(ife->ifm_media) != sc->mii_inst)
+                       return 0;
+               break;
+
+       case MII_MEDIACHG:
+               /*
+                * If the media indicates a different PHY instance,
+                * isolate ourselves.
+                */
+               if (IFM_INST(ife->ifm_media) != sc->mii_inst) {
+                       bmcr = PHY_READ(sc, MII_BMCR);
+                       PHY_WRITE(sc, MII_BMCR, bmcr | BMCR_ISO);
+                       return 0;
+               }
+
+               /*
+                * If the interface is not up, don't do anything.
+                */
+               if ((mii->mii_ifp->if_flags & IFF_UP) == 0)
+                       break;
+
+               if (IFM_SUBTYPE(ife->ifm_media) == IFM_AUTO ||
+                   IFM_SUBTYPE(ife->ifm_media) == IFM_1000_T) {
+                       atphy_auto(sc);
+                       break;
+               }
+
+               bmcr = 0;
+               switch (IFM_SUBTYPE(ife->ifm_media)) {
+               case IFM_100_TX:
+                       bmcr = BMCR_S100;
+                       break;
+
+               case IFM_10_T:
+                       bmcr = BMCR_S10;
+                       break;
+
+               case IFM_NONE:
+                       bmcr = PHY_READ(sc, MII_BMCR);
+                       /*
+                        * XXX
+                        * Due to an unknown reason powering down PHY resulted
+                        * in unexpected results such as inaccessbility of
+                        * hardware of freshly rebooted system.  Disable
+                        * powering down PHY until I got more information for
+                        * Attansic/Atheros PHY hardwares.
+                        */
+                       PHY_WRITE(sc, MII_BMCR, bmcr | BMCR_ISO);
+                       goto done;
+
+               default:
+                       return EINVAL;
+               }
+
+               anar = atphy_anar(ife);
+               if ((ife->ifm_media & IFM_GMASK) & IFM_FDX) {
+                       bmcr |= BMCR_FDX;
+                       /* Enable pause. */
+                       anar |= (3 << 10);
+               }
+
+               if (sc->mii_extcapabilities & (EXTSR_1000TFDX | EXTSR_1000THDX))
+                       PHY_WRITE(sc, MII_100T2CR, 0);
+               PHY_WRITE(sc, MII_ANAR, anar | ANAR_CSMA);
+
+               /*
+                * Reset the PHY so all changes take effect.
+                */
+               PHY_WRITE(sc, MII_BMCR, bmcr | BMCR_RESET | BMCR_AUTOEN |
+                   BMCR_STARTNEG);
+done:
+               break;
+
+       case MII_TICK:
+               /*
+                * If we're not currently selected, just return.
+                */
+               if (IFM_INST(ife->ifm_media) != sc->mii_inst)
+                       return 0;
+
+               /*
+                * Is the interface even up?
+                */
+               if ((mii->mii_ifp->if_flags & IFF_UP) == 0)
+                       return 0;
+
+               /*
+                * Only used for autonegotiation.
+                */
+               if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO) {
+                       sc->mii_ticks = 0;
+                       break;
+               }
+
+               /*
+                * check for link.
+                * Read the status register twice; BMSR_LINK is latch-low.
+                */
+               bmsr = PHY_READ(sc, MII_BMSR) | PHY_READ(sc, MII_BMSR);
+               if (bmsr & BMSR_LINK) {
+                       sc->mii_ticks = 0;
+                       break;
+               }
+
+               /* Announce link loss right after it happens. */
+               if (sc->mii_ticks++ == 0)
+                       break;
+               if (sc->mii_ticks <= sc->mii_anegticks)
+                       return 0;
+
+               sc->mii_ticks = 0;
+               atphy_auto(sc);
+               break;
+       }
+
+       /* Update the media status. */
+       atphy_status(sc);
+
+       /* Callback if something changed. */
+       mii_phy_update(sc, cmd);
+       return 0;
+}
+
+static void
+atphy_status(struct mii_softc *sc)
+{
+       struct mii_data *mii = sc->mii_pdata;
+       uint32_t bmsr, bmcr, ssr;
+
+       mii->mii_media_status = IFM_AVALID;
+       mii->mii_media_active = IFM_ETHER;
+
+       bmsr = PHY_READ(sc, MII_BMSR) | PHY_READ(sc, MII_BMSR);
+       if (bmsr & BMSR_LINK)
+               mii->mii_media_status |= IFM_ACTIVE;
+
+       bmcr = PHY_READ(sc, MII_BMCR);
+       if (bmcr & BMCR_ISO) {
+               mii->mii_media_active |= IFM_NONE;
+               mii->mii_media_status = 0;
+               return;
+       }
+
+       if (bmcr & BMCR_LOOP)
+               mii->mii_media_active |= IFM_LOOP;
+
+       ssr = PHY_READ(sc, ATPHY_SSR);
+       if ((ssr & ATPHY_SSR_SPD_DPLX_RESOLVED) == 0) {
+               /* Erg, still trying, I guess... */
+               mii->mii_media_active |= IFM_NONE;
+               return;
+       }
+
+       switch (ssr & ATPHY_SSR_SPEED_MASK) {
+       case ATPHY_SSR_1000MBS:
+               mii->mii_media_active |= IFM_1000_T;
+               /*
+                * atphy(4) got a valid link so reset mii_ticks.
+                * Resetting mii_ticks is needed in order to
+                * detect link loss after auto-negotiation.
+                */
+               sc->mii_ticks = 0;
+               break;
+
+       case ATPHY_SSR_100MBS:
+               mii->mii_media_active |= IFM_100_TX;
+               sc->mii_ticks = 0;
+               break;
+
+       case ATPHY_SSR_10MBS:
+               mii->mii_media_active |= IFM_10_T;
+               sc->mii_ticks = 0;
+               break;
+
+       default:
+               mii->mii_media_active |= IFM_NONE;
+               return;
+       }
+
+       if (ssr & ATPHY_SSR_DUPLEX)
+               mii->mii_media_active |= IFM_FDX;
+       else
+               mii->mii_media_active |= IFM_HDX;
+
+       /* XXX Master/Slave, Flow-control */
+}
+
+static void
+atphy_reset(struct mii_softc *sc)
+{
+       uint32_t reg;
+       int i;
+
+       /* Take PHY out of power down mode. */
+       PHY_WRITE(sc, 29, 0x29);
+       PHY_WRITE(sc, 30, 0);
+
+       reg = PHY_READ(sc, ATPHY_SCR);
+       /* Enable automatic crossover. */
+       reg |= ATPHY_SCR_AUTO_X_MODE;
+       /* Disable power down. */
+       reg &= ~ATPHY_SCR_MAC_PDOWN;
+       /* Enable CRS on Tx. */
+       reg |= ATPHY_SCR_ASSERT_CRS_ON_TX;
+       /* Auto correction for reversed cable polarity. */
+       reg |= ATPHY_SCR_POLARITY_REVERSAL;
+       PHY_WRITE(sc, ATPHY_SCR, reg);
+
+       /* Workaround F1 bug to reset phy. */
+#if 0
+       atphy_auto(sc);
+#else
+       PHY_WRITE(sc, MII_BMCR, BMCR_RESET | BMCR_AUTOEN | BMCR_STARTNEG);
+#endif
+
+       for (i = 0; i < 1000; i++) {
+               DELAY(1);
+               if ((PHY_READ(sc, MII_BMCR) & BMCR_RESET) == 0)
+                       break;
+       }
+}
+
+static uint16_t
+atphy_anar(struct ifmedia_entry *ife)
+{
+       uint16_t anar = 0;
+
+       switch (IFM_SUBTYPE(ife->ifm_media)) {
+       case IFM_AUTO:
+               anar |= ANAR_TX_FD | ANAR_TX | ANAR_10_FD | ANAR_10;
+               return anar;
+
+       case IFM_1000_T:
+               return anar;
+
+       case IFM_100_TX:
+               anar |= ANAR_TX;
+               break;
+
+       case IFM_10_T:
+               anar |= ANAR_10;
+               break;
+
+       default:
+               return 0;
+       }
+
+       if ((ife->ifm_media & IFM_GMASK) & IFM_FDX) {
+               if (IFM_SUBTYPE(ife->ifm_media) == IFM_100_TX)
+                       anar |= ANAR_TX_FD;
+               else
+                       anar |= ANAR_10_FD;
+       }
+       return anar;
+}
+
+static void
+atphy_auto(struct mii_softc *sc)
+{
+       uint16_t anar;
+
+       anar = BMSR_MEDIA_TO_ANAR(sc->mii_capabilities);
+       PHY_WRITE(sc, MII_ANAR, anar | (3 << 10) | ANAR_CSMA);
+
+       if (sc->mii_extcapabilities & (EXTSR_1000TFDX | EXTSR_1000THDX)) {
+               PHY_WRITE(sc, MII_100T2CR, GTCR_ADV_1000TFDX |
+                   GTCR_ADV_1000THDX);
+       }
+       PHY_WRITE(sc, MII_BMCR, BMCR_RESET | BMCR_AUTOEN | BMCR_STARTNEG);
+}
diff --git a/sys/dev/netif/mii_layer/atphyreg.h b/sys/dev/netif/mii_layer/atphyreg.h
new file mode 100644 (file)
index 0000000..6b97c4d
--- /dev/null
@@ -0,0 +1,63 @@
+/*-
+ * Copyright (c) 2008, Pyun YongHyeon
+ * 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 unmodified, this list of conditions, and the following
+ *    disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: src/sys/dev/mii/atphyreg.h,v 1.1 2008/05/19 01:12:10 yongari Exp $
+ */
+
+#ifndef        _DEV_MII_ATPHYREG_H_
+#define        _DEV_MII_ATPHYREG_H_
+
+/*
+ * Registers for the Attansic/Atheros Gigabit PHY.
+ */
+
+/* Special Control Register */
+#define ATPHY_SCR                      0x10
+#define ATPHY_SCR_JABBER_DISABLE       0x0001
+#define ATPHY_SCR_POLARITY_REVERSAL    0x0002
+#define ATPHY_SCR_SQE_TEST             0x0004
+#define ATPHY_SCR_MAC_PDOWN            0x0008
+#define ATPHY_SCR_CLK125_DISABLE       0x0010
+#define ATPHY_SCR_MDI_MANUAL_MODE      0x0000
+#define ATPHY_SCR_MDIX_MANUAL_MODE     0x0020
+#define ATPHY_SCR_AUTO_X_1000T         0x0040
+#define ATPHY_SCR_AUTO_X_MODE          0x0060
+#define ATPHY_SCR_10BT_EXT_ENABLE      0x0080
+#define ATPHY_SCR_MII_5BIT_ENABLE      0x0100
+#define ATPHY_SCR_SCRAMBLER_DISABLE    0x0200
+#define ATPHY_SCR_FORCE_LINK_GOOD      0x0400
+#define ATPHY_SCR_ASSERT_CRS_ON_TX     0x0800
+
+/* Special Status Register. */
+#define ATPHY_SSR                      0x11
+#define ATPHY_SSR_SPD_DPLX_RESOLVED    0x0800
+#define ATPHY_SSR_DUPLEX               0x2000
+#define ATPHY_SSR_SPEED_MASK           0xC000
+#define ATPHY_SSR_10MBS                        0x0000
+#define ATPHY_SSR_100MBS               0x4000
+#define ATPHY_SSR_1000MBS              0x8000
+
+#endif /* _DEV_MII_ATPHYREG_H_ */