Date: Mon, 04 Dec 2000 18:59:01 +0000 From: Ian Dowse <iedowse@maths.tcd.ie> To: freebsd-mobile@freebsd.org Cc: iedowse@maths.tcd.ie Subject: MII support for Netgear FA410TX cards Message-ID: <200012041859.aa04022@salmon.maths.tcd.ie>
next in thread | raw e-mail | index | archive | help
Below is a patch which adds MII support to the "ed" pccard driver
in order to suport the newer Netgear FA410TX cards. This is mainly
based on the fa_select.c program that was written for Linux to
program the PHY in these cards. It's against -STABLE, but I'll
probably do a -CURRENT version soon.
I have both the new and old models of the FA410TX here, so I was
able to check that the old version continues to work. In fact, the
old version has a MII bus too, but a different PHY that generally
comes up in a usable mode when the card is powered up. I do not,
however, have access to other PCCARD devices which detect as
"Linksys", so I don't know if the patch breaks support for them.
For reference, the PHYs are:
Old card: ukphy0: OUI 0x1e0400, model 0x0000, rev. 3
New card: ukphy0: OUI 0x0010dd, model 0x0001, rev. 2
I'd be interested to hear if this patch works as expected, and
whether I'm doing anything strange or bad - I haven't done a lot
of driver programming...
Ian
Index: if_ed.c
===================================================================
RCS file: /home/iedowse/CVS/src/sys/dev/ed/if_ed.c,v
retrieving revision 1.173.2.8
diff -u -r1.173.2.8 if_ed.c
--- if_ed.c 2000/09/10 08:45:11 1.173.2.8
+++ if_ed.c 2000/12/04 15:51:24
@@ -41,6 +41,7 @@
#include <sys/systm.h>
#include <sys/sockio.h>
#include <sys/mbuf.h>
+#include <sys/kernel.h>
#include <sys/socket.h>
#include <sys/syslog.h>
@@ -56,7 +57,11 @@
#include <net/if_arp.h>
#include <net/if_dl.h>
#include <net/if_mib.h>
+#include <net/if_media.h>
+#include <dev/mii/mii.h>
+#include <dev/mii/miivar.h>
+
#include <net/bpf.h>
#include "opt_bdg.h"
#ifdef BRIDGE
@@ -74,6 +79,7 @@
static void ed_start __P((struct ifnet *));
static void ed_reset __P((struct ifnet *));
static void ed_watchdog __P((struct ifnet *));
+static void ed_tick __P((void *));
static void ds_getmcaf __P((struct ed_softc *, u_int32_t *));
@@ -1617,6 +1623,8 @@
{
struct ifnet *ifp = &sc->arpcom.ac_if;
+ callout_handle_init(&sc->tick_ch);
+
/*
* Set interface to stopped condition (reset)
*/
@@ -1726,6 +1734,8 @@
{
int n = 5000;
+ untimeout(ed_tick, sc, sc->tick_ch);
+
if (sc->gone)
return;
/*
@@ -1760,6 +1770,26 @@
ed_reset(ifp);
}
+static void
+ed_tick(arg)
+ void *arg;
+{
+ struct ed_softc *sc = arg;
+ struct mii_data *mii;
+ int s;
+
+ if (sc->gone)
+ return;
+
+ s = splimp();
+ if (sc->miibus != NULL) {
+ mii = device_get_softc(sc->miibus);
+ mii_tick(mii);
+ }
+ sc->tick_ch = timeout(ed_tick, sc, hz);
+ splx(s);
+}
+
/*
* Initialize device.
*/
@@ -1912,6 +1942,8 @@
*/
ed_start(ifp);
+ sc->tick_ch = timeout(ed_tick, sc, hz);
+
(void) splx(s);
}
@@ -2521,6 +2553,7 @@
caddr_t data;
{
struct ed_softc *sc = ifp->if_softc;
+ struct ifreq *ifr = (struct ifreq *)data;
int s, error = 0;
if (sc == NULL || sc->gone) {
@@ -2581,6 +2614,19 @@
*/
ed_setrcr(sc);
error = 0;
+ break;
+
+ case SIOCGIFMEDIA:
+ case SIOCSIFMEDIA:
+ if (sc->miibus != NULL) {
+ struct mii_data *mii;
+
+ mii = device_get_softc(sc->miibus);
+ error = ifmedia_ioctl(ifp, ifr, &mii->mii_media,
+ command);
+ break;
+ }
+ error = EINVAL;
break;
default:
Index: if_ed_pccard.c
===================================================================
RCS file: /home/iedowse/CVS/src/sys/dev/ed/if_ed_pccard.c,v
retrieving revision 1.9.2.4
diff -u -r1.9.2.4 if_ed_pccard.c
--- if_ed_pccard.c 2000/09/10 08:45:11 1.9.2.4
+++ if_ed_pccard.c 2000/12/04 15:50:18
@@ -44,7 +44,13 @@
#include <net/if.h>
#include <net/if_arp.h>
#include <net/if_mib.h>
+#include <net/if_media.h>
+#include <dev/mii/mii.h>
+#include <dev/mii/miivar.h>
+
+#include "miibus_if.h"
+
#include <dev/ed/if_edreg.h>
#include <dev/ed/if_edvar.h>
#include <dev/pccard/pccardvar.h>
@@ -59,17 +65,35 @@
static int ed_pccard_probe(device_t);
static int ed_pccard_attach(device_t);
static int ed_pccard_detach(device_t);
+static void ed_pccard_child_detached(device_t dev, device_t child);
static void ax88190_geteprom(struct ed_softc *);
static int ed_pccard_memwrite(device_t dev, off_t offset, u_char byte);
static int ed_pccard_memread(device_t dev, off_t offset, u_char *buf, int size);
+static int ed_pccard_miibus_readreg(device_t dev, int phy, int reg);
+static void ed_pccard_miibus_writereg(device_t dev, int phy, int reg,
+ int data);
+static int ed_pccard_ifmedia_upd(struct ifnet *ifp);
+static void ed_pccard_ifmedia_sts(struct ifnet *ifp,
+ struct ifmediareq *ifmr);
+static void ed_pccard_fa410_mii_reset(struct ed_softc *sc);
+static u_int32_t ed_pccard_fa410_mii_readbits(struct ed_softc *sc, int nbits);
+static void ed_pccard_fa410_mii_writebits(struct ed_softc *sc, u_int32_t val,
+ int nbits);
+
static device_method_t ed_pccard_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, ed_pccard_probe),
DEVMETHOD(device_attach, ed_pccard_attach),
DEVMETHOD(device_detach, ed_pccard_detach),
+ DEVMETHOD(bus_child_detached, ed_pccard_child_detached),
+
+ /* MII interface */
+ DEVMETHOD(miibus_readreg, ed_pccard_miibus_readreg),
+ DEVMETHOD(miibus_writereg, ed_pccard_miibus_writereg),
+
{ 0, 0 }
};
@@ -82,6 +106,7 @@
static devclass_t ed_pccard_devclass;
DRIVER_MODULE(ed, pccard, ed_pccard_driver, ed_pccard_devclass, 0, 0);
+DRIVER_MODULE(miibus, ed, miibus_driver, miibus_devclass, 0, 0);
/*
* ed_pccard_detach - unload the driver and clear the table.
@@ -106,6 +131,16 @@
ifp->if_flags &= ~IFF_RUNNING;
ether_ifdetach(ifp, ETHER_BPF_SUPPORTED);
sc->gone = 1;
+
+ /*
+ * We rely on ed_pccard_child_detached to set miibus to NULL if
+ * the miibus has alredy been deleted, as is usually the case.
+ */
+ if (sc->miibus != NULL) {
+ device_delete_child(dev, sc->miibus);
+ bus_generic_detach(dev);
+ }
+
bus_teardown_intr(dev, sc->irq_res, sc->irq_handle);
ed_release_resources(dev);
return (0);
@@ -186,6 +221,7 @@
int i;
u_char sum;
u_char ether_addr[ETHER_ADDR_LEN];
+ int probe_mii = 0;
if (sc->port_used > 0)
ed_alloc_port(dev, sc->port_rid, sc->port_used);
@@ -201,7 +237,9 @@
return (error);
}
- if (ed_get_Linksys(sc) == 0) {
+ if (ed_get_Linksys(sc))
+ probe_mii = 1;
+ else {
pccard_get_ether(dev, ether_addr);
for (i = 0, sum = 0; i < ETHER_ADDR_LEN; i++)
sum |= ether_addr[i];
@@ -210,6 +248,13 @@
}
error = ed_attach(sc, device_get_unit(dev), flags);
+
+ if (probe_mii) {
+ ed_pccard_fa410_mii_reset(sc);
+ mii_phy_probe(dev, &sc->miibus, ed_pccard_ifmedia_upd,
+ ed_pccard_ifmedia_sts);
+ }
+
return (error);
}
@@ -312,4 +357,184 @@
d = makedev(CARD_MAJOR, devi->slt->slotnum);
return devsw(d)->d_read(d, &uios, 0);
+}
+
+static void
+ed_pccard_child_detached(dev, child)
+ device_t dev;
+ device_t child;
+{
+ struct ed_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ if (child == sc->miibus)
+ sc->miibus = NULL;
+}
+
+#define FA410_MIISET(sc, x) ed_asic_outb(sc, ED_FA410_MIIBUS, \
+ ed_asic_inb(sc, ED_FA410_MIIBUS) | (x))
+#define FA410_MIICLR(sc, x) ed_asic_outb(sc, ED_FA410_MIIBUS, \
+ ed_asic_inb(sc, ED_FA410_MIIBUS) & ~(x))
+
+static void
+ed_pccard_fa410_mii_reset(sc)
+ struct ed_softc *sc;
+{
+ /*
+ * The Linux fa_select.c program performs something close to
+ * these operations before talking to the MII bus. I've no idea
+ * if they are necessary...
+ */
+ ed_asic_outb(sc, ED_FA410_MIIBUS, 0);
+ DELAY(10);
+ FA410_MIISET(sc, ED_FA410_MII_RESET2);
+ DELAY(10);
+ FA410_MIISET(sc, ED_FA410_MII_RESET1);
+ DELAY(10);
+ FA410_MIICLR(sc, ED_FA410_MII_RESET1);
+ DELAY(10);
+ FA410_MIICLR(sc, ED_FA410_MII_RESET2);
+ DELAY(10);
+}
+
+static void
+ed_pccard_fa410_mii_writebits(sc, val, nbits)
+ struct ed_softc *sc;
+ u_int32_t val;
+ int nbits;
+{
+ int i;
+
+ FA410_MIISET(sc, ED_FA410_MII_DIROUT);
+
+ for (i = nbits - 1; i >= 0; i--) {
+ if ((val >> i) & 1)
+ FA410_MIISET(sc, ED_FA410_MII_DATAOUT);
+ else
+ FA410_MIICLR(sc, ED_FA410_MII_DATAOUT);
+ DELAY(10);
+ FA410_MIISET(sc, ED_FA410_MII_CLK);
+ DELAY(10);
+ FA410_MIICLR(sc, ED_FA410_MII_CLK);
+ DELAY(10);
+ }
+}
+
+static u_int32_t
+ed_pccard_fa410_mii_readbits(sc, nbits)
+ struct ed_softc *sc;
+ int nbits;
+{
+ int i;
+ u_int32_t val = 0;
+
+ FA410_MIICLR(sc, ED_FA410_MII_DIROUT);
+
+ for (i = nbits - 1; i >= 0; i--) {
+ FA410_MIISET(sc, ED_FA410_MII_CLK);
+ DELAY(10);
+ val <<= 1;
+ if (ed_asic_inb(sc, ED_FA410_MIIBUS) & ED_FA410_MII_DATATIN)
+ val++;
+ FA410_MIICLR(sc, ED_FA410_MII_CLK);
+ DELAY(10);
+ }
+
+ return val;
+}
+
+static int
+ed_pccard_miibus_readreg(dev, phy, reg)
+ device_t dev;
+ int phy, reg;
+{
+ struct ed_softc *sc;
+ int val;
+ int failed;
+
+ sc = device_get_softc(dev);
+
+ if (sc->gone)
+ return 0;
+
+ ed_pccard_fa410_mii_writebits(sc, 0xffffffff, 32);
+ ed_pccard_fa410_mii_writebits(sc, 0x6, 4);
+ ed_pccard_fa410_mii_writebits(sc, phy, 5);
+ ed_pccard_fa410_mii_writebits(sc, reg, 5);
+
+ failed = ed_pccard_fa410_mii_readbits(sc, 1);
+
+ val = ed_pccard_fa410_mii_readbits(sc, 16);
+
+ ed_pccard_fa410_mii_readbits(sc, 1);
+
+ return failed ? 0 : val;
+}
+
+static void
+ed_pccard_miibus_writereg(dev, phy, reg, data)
+ device_t dev;
+ int phy, reg, data;
+{
+ struct ed_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ if (sc->gone)
+ return;
+
+ ed_pccard_fa410_mii_writebits(sc, 0xffffffff, 32);
+
+ ed_pccard_fa410_mii_writebits(sc, ED_MII_STARTDELIM,
+ ED_MII_STARTDELIM_BITS);
+ ed_pccard_fa410_mii_writebits(sc, ED_MII_WRITEOP, ED_MII_OP_BITS);
+ ed_pccard_fa410_mii_writebits(sc, phy, ED_MII_PHY_BITS);
+ ed_pccard_fa410_mii_writebits(sc, reg, ED_MII_REG_BITS);
+ ed_pccard_fa410_mii_writebits(sc, ED_MII_TURNAROUND,
+ ED_MII_TURNAROUND_BITS);
+ ed_pccard_fa410_mii_writebits(sc, data, ED_MII_DATA_BITS);
+ ed_pccard_fa410_mii_writebits(sc, ED_MII_IDLE, ED_MII_IDLE_BITS);
+}
+
+static int
+ed_pccard_ifmedia_upd(ifp)
+ struct ifnet *ifp;
+{
+ struct ed_softc *sc;
+ struct mii_data *mii;
+
+ sc = ifp->if_softc;
+
+ if (sc->gone || sc->miibus == NULL)
+ return ENXIO;
+
+ mii = device_get_softc(sc->miibus);
+
+ if (mii->mii_instance) {
+ struct mii_softc *miisc;
+ for (miisc = LIST_FIRST(&mii->mii_phys); miisc != NULL;
+ miisc = LIST_NEXT(miisc, mii_list))
+ mii_phy_reset(miisc);
+ }
+ return mii_mediachg(mii);
+}
+
+static void
+ed_pccard_ifmedia_sts(ifp, ifmr)
+ struct ifnet *ifp;
+ struct ifmediareq *ifmr;
+{
+ struct ed_softc *sc;
+ struct mii_data *mii;
+
+ sc = ifp->if_softc;
+
+ if (sc->gone || sc->miibus == NULL)
+ return;
+
+ mii = device_get_softc(sc->miibus);
+ mii_pollstat(mii);
+ ifmr->ifm_active = mii->mii_media_active;
+ ifmr->ifm_status = mii->mii_media_status;
}
Index: if_edreg.h
===================================================================
RCS file: /home/iedowse/CVS/src/sys/dev/ed/if_edreg.h,v
retrieving revision 1.27.2.1
diff -u -r1.27.2.1 if_edreg.h
--- if_edreg.h 2000/09/10 08:45:11 1.27.2.1
+++ if_edreg.h 2000/12/04 15:44:01
@@ -1112,3 +1112,35 @@
#define ED_AX88190_IOBASE0 0x3ca
#define ED_AX88190_IOBASE1 0x3cc
+
+/*
+ * Definitions for Netgear FA410TX with MII-based PHYs.
+ */
+#define ED_FA410_MIIBUS 0x0c
+
+#define ED_MII_STARTDELIM 0x01
+#define ED_MII_READOP 0x02
+#define ED_MII_WRITEOP 0x01
+#define ED_MII_TURNAROUND 0x02
+#define ED_MII_IDLE 0x01
+
+#define ED_MII_STARTDELIM_BITS 2
+#define ED_MII_OP_BITS 2
+#define ED_MII_PHY_BITS 5
+#define ED_MII_REG_BITS 5
+#define ED_MII_TURNAROUND_BITS 2
+#define ED_MII_DATA_BITS 16
+#define ED_MII_IDLE_BITS 1
+
+/*
+ * These definitions are guesses based on the Linux fa_select.c program
+ * which was floating around on the internet.
+ */
+#define ED_FA410_MII_RESET1 0x04
+#define ED_FA410_MII_RESET2 0x08
+
+#define ED_FA410_MII_DATATIN 0x10
+#define ED_FA410_MII_DIROUT 0x20
+#define ED_FA410_MII_DATAOUT 0x40
+#define ED_FA410_MII_CLK 0x80
+
Index: if_edvar.h
===================================================================
RCS file: /home/iedowse/CVS/src/sys/dev/ed/if_edvar.h,v
retrieving revision 1.4.2.2
diff -u -r1.4.2.2 if_edvar.h
--- if_edvar.h 2000/09/10 08:45:11 1.4.2.2
+++ if_edvar.h 2000/12/03 16:58:07
@@ -47,6 +47,8 @@
int irq_rid; /* resource id for irq */
struct resource* irq_res; /* resource for irq */
void* irq_handle; /* handle for irq handler */
+ device_t miibus; /* MII bus for cards with MII. */
+ struct callout_handle tick_ch; /* Callout handle for ed_tick */
int nic_offset; /* NIC (DS8390) I/O bus address offset */
int asic_offset; /* ASIC I/O bus address offset */
To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-mobile" in the body of the message
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200012041859.aa04022>
