/* * Copyright 2003 by Peter Grehan. All rights reserved. * Copyright (C) 1998, 1999, 2000 Tsubai Masanari. 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. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * 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. * * From: * NetBSD: if_bm.c,v 1.9.2.1 2000/11/01 15:02:49 tv Exp */ /* * BMAC/BMAC+ Macio cell 10/100 ethernet driver */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include MODULE_DEPEND(bm, macio, 1, 1, 1); MODULE_DEPEND(bm, ether, 1, 1, 1); MODULE_DEPEND(bm, miibus, 1, 1, 1); /* "controller miibus0" required. See GENERIC if you get errors here. */ #include "miibus_if.h" #include static int bm_probe (device_t); static int bm_attach (device_t); static int bm_detach (device_t); static void bm_shutdown (device_t); static void bm_start (struct ifnet *); static int bm_ioctl (struct ifnet *, u_long, caddr_t); static void bm_init (struct ifnet *); static void bm_watchdog (struct ifnet *); static int bm_ifmedia_upd (struct ifnet *); static void bm_ifmedia_sts (struct ifnet *, struct ifmediareq *); static void bm_miicsr_dwrite (struct bm_softc *, u_int16_t); static void bm_mii_writebit (struct bm_softc *, int); static int bm_mii_readbit (struct bm_softc *); static void bm_mii_sync (struct bm_softc *); static void bm_mii_send (struct bm_softc *, u_int32_t, int); static int bm_mii_readreg (struct bm_softc *, struct bm_mii_frame *); static int bm_mii_writereg (struct bm_softc *, struct bm_mii_frame *); static int bm_miibus_readreg (device_t, int, int); static int bm_miibus_writereg (device_t, int, int, int); static void bm_miibus_statchg (device_t); static device_method_t bm_methods[] = { /* Device interface */ DEVMETHOD(device_probe, bm_probe), DEVMETHOD(device_attach, bm_attach), DEVMETHOD(device_detach, bm_detach), DEVMETHOD(device_shutdown, bm_shutdown), /* bus interface, for miibus */ DEVMETHOD(bus_print_child, bus_generic_print_child), DEVMETHOD(bus_driver_added, bus_generic_driver_added), /* MII interface */ DEVMETHOD(miibus_readreg, bm_miibus_readreg), DEVMETHOD(miibus_writereg, bm_miibus_writereg), DEVMETHOD(miibus_statchg, bm_miibus_statchg), { 0, 0 } }; static driver_t bm_macio_driver = { "bm", bm_methods, sizeof(struct bm_softc) }; static devclass_t bm_devclass; DRIVER_MODULE(bm, macio, bm_macio_driver, bm_devclass, 0, 0); DRIVER_MODULE(miibus, bm, miibus_driver, miibus_devclass, 0, 0); /* * MII internal routines */ /* * Write to the MII csr, introducing a delay to allow valid * MII clock pulses to be formed */ static void bm_miicsr_dwrite(struct bm_softc *sc, u_int16_t val) { CSR_WRITE_2(sc, BM_MII_CSR, val); /* * Assume this is a clock toggle and generate a 1us delay * to cover both MII's 160ns high/low minimum and 400ns * cycle miniumum */ DELAY(1); } /* * Write a bit to the MII bus. */ static void bm_mii_writebit(struct bm_softc *sc, int bit) { u_int16_t regval; regval = BM_MII_OENABLE; if (bit) regval |= BM_MII_DATAOUT; bm_miicsr_dwrite(sc, regval); bm_miicsr_dwrite(sc, regval | BM_MII_CLK); bm_miicsr_dwrite(sc, regval); } /* * Read a bit from the MII bus. */ static int bm_mii_readbit(struct bm_softc *sc) { u_int16_t regval, bitin; /* ~BM_MII_OENABLE */ regval = 0; bm_miicsr_dwrite(sc, regval); bm_miicsr_dwrite(sc, regval | BM_MII_CLK); bm_miicsr_dwrite(sc, regval); bitin = CSR_READ_2(sc, BM_MII_CSR) & BM_MII_DATAIN; return (bitin == BM_MII_DATAIN); } /* * Sync the PHYs by setting data bit and strobing the clock 32 times. */ static void bm_mii_sync(struct bm_softc *sc) { int i; u_int16_t regval; regval = BM_MII_OENABLE | BM_MII_DATAOUT; bm_miicsr_dwrite(sc, regval); for (i = 0; i < 32; i++) { bm_miicsr_dwrite(sc, regval | BM_MII_CLK); bm_miicsr_dwrite(sc, regval); } } /* * Clock a series of bits through the MII. */ static void bm_mii_send(struct bm_softc *sc, u_int32_t bits, int cnt) { int i; for (i = (0x1 << (cnt - 1)); i; i >>= 1) bm_mii_writebit(sc, bits & i); } /* * Read a PHY register through the MII. */ static int bm_mii_readreg(struct bm_softc *sc, struct bm_mii_frame *frame) { int i, ack, bit; BM_LOCK(sc); /* * Set up frame for RX. */ frame->mii_stdelim = BM_MII_STARTDELIM; frame->mii_opcode = BM_MII_READOP; frame->mii_turnaround = 0; frame->mii_data = 0; /* * Sync the PHYs */ bm_mii_sync(sc); /* * Send command/address info */ bm_mii_send(sc, frame->mii_stdelim, 2); bm_mii_send(sc, frame->mii_opcode, 2); bm_mii_send(sc, frame->mii_phyaddr, 5); bm_mii_send(sc, frame->mii_regaddr, 5); /* * Check for ack. */ ack = bm_mii_readbit(sc); /* * Now try reading data bits. If the ack failed, we still * need to clock through 16 cycles to keep the PHY(s) in sync. */ for (i = 0x8000; i; i >>= 1) { bit = bm_mii_readbit(sc); if (!ack && bit) frame->mii_data |= i; } /* * Skip through idle bit-times */ bm_mii_writebit(sc, 0); bm_mii_writebit(sc, 0); BM_UNLOCK(sc); if (ack) return (1); return (0); } /* * Write to a PHY register through the MII. */ static int bm_mii_writereg(struct bm_softc *sc, struct bm_mii_frame *frame) { BM_LOCK(sc); /* * Set up frame for tx */ frame->mii_stdelim = BM_MII_STARTDELIM; frame->mii_opcode = BM_MII_WRITEOP; frame->mii_turnaround = BM_MII_TURNAROUND; /* * Sync the phy and start the bitbang write sequence */ bm_mii_sync(sc); bm_mii_send(sc, frame->mii_stdelim, 2); bm_mii_send(sc, frame->mii_opcode, 2); bm_mii_send(sc, frame->mii_phyaddr, 5); bm_mii_send(sc, frame->mii_regaddr, 5); bm_mii_send(sc, frame->mii_turnaround, 2); bm_mii_send(sc, frame->mii_data, 16); /* * Idle bit. */ bm_mii_writebit(sc, 0); BM_UNLOCK(sc); return (0); } /* * MII bus i/f */ static int bm_miibus_readreg(device_t dev, int phy, int reg) { struct bm_softc *sc; struct bm_mii_frame frame; sc = device_get_softc(dev); bzero(&frame, sizeof(frame)); frame.mii_phyaddr = phy; frame.mii_regaddr = reg; bm_mii_readreg(sc, &frame); return(frame.mii_data); } static int bm_miibus_writereg(device_t dev, int phy, int reg, int data) { struct bm_softc *sc; struct bm_mii_frame frame; sc = device_get_softc(dev); bzero(&frame, sizeof(frame)); frame.mii_phyaddr = phy; frame.mii_regaddr = reg; frame.mii_data = data; bm_mii_writereg(sc, &frame); return (0); } static void bm_miibus_statchg(device_t dev) { printf("bm mii statchg!\n"); } /* * ifmedia/mii callbacks */ static int bm_ifmedia_upd(struct ifnet *ifp) { printf("bm_ifmedia_upd\n"); return (0); } static void bm_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifm) { printf("bm_ifmedia_sts\n"); return; } /* * Macio probe/attach */ static int bm_probe(device_t dev) { char *dname = macio_get_name(dev); char *dcompat = macio_get_compat(dev); /* * BMAC+ cells have a name of "ethernet" and * a compatible property of "bmac+" */ if (strcmp(dname, "bmac") == 0) { device_set_desc(dev, "Apple BMAC Ethernet Adaptor"); } else if (strcmp(dcompat, "bmac+") == 0) { device_set_desc(dev, "Apple BMAC+ Ethernet Adaptor"); } else return (ENXIO); return (0); } static int bm_attach(device_t dev) { phandle_t node; u_char *eaddr; int rid; struct bm_softc *sc = device_get_softc(dev); sc->bm_dev = dev; mtx_init(&sc->bm_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK, MTX_DEF | MTX_RECURSE); ifmedia_init(&sc->bm_ifmedia, 0, bm_ifmedia_upd, bm_ifmedia_sts); rid = 0; sc->bm_memr = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid, 0, ~0, 1, RF_ACTIVE); if (sc->bm_memr == NULL) { device_printf(dev, "Could not alloc mem resource!\n"); return (ENXIO); } sc->bm_btag = rman_get_bustag(sc->bm_memr); sc->bm_bhandle = rman_get_bushandle(sc->bm_memr); /* reset the adapter ? */ /* alloc interrupt */ /* * Get the ethernet address from OpenFirmware */ node = macio_get_node(dev); eaddr = sc->bm_arpcom.ac_enaddr; OF_getprop(node, "local-mac-address", eaddr, ETHER_ADDR_LEN); device_printf(dev, "Ethernet address: %6D\n", eaddr, ":"); mii_phy_probe(dev, &sc->bm_miibus, bm_ifmedia_upd, bm_ifmedia_sts); #if 0 ifp = &sc->bm_arpcom.ac_if; ifp->if_softc = sc; ifp->if_unit = device_get_unit(dev); ifp->if_name = "bm"; ifp->if_mtu = ETHERMTU; ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; ifp->if_ioctl = bm_ioctl; ifp->if_output = ether_output; ifp->if_start = bm_start; ifp->if_watchdog = bm_watchdog; ifp->if_init = bm_init; ifp->if_baudrate = 100000000; ifp->if_snd.ifq_maxlen = ; /* * Attach the interface. */ ether_ifattach(ifp, sc->bm_arpcom.ac_enaddr); #endif return (0); } static int bm_detach(device_t dev) { return (0); } static void bm_shutdown(device_t dev) { } #ifdef notdef static void bm_testpacket(struct bm_softc *sc) { struct mbuf *m; struct ifnet *ifp; ifp = &sc->arpcom.ac_if; MGETHDR(m, M_DONTWAIT, MT_DATA); if (m == NULL) return; bcopy(&sc->arpcom.ac_enaddr, mtod(m, struct ether_header *)->ether_dhost, ETHER_ADDR_LEN); bcopy(&sc->arpcom.ac_enaddr, mtod(m, struct ether_header *)->ether_shost, ETHER_ADDR_LEN); mtod(m, struct ether_header *)->ether_type = htons(3); mtod(m, unsigned char *)[14] = 0; mtod(m, unsigned char *)[15] = 0; mtod(m, unsigned char *)[16] = 0xE3; m->m_len = m->m_pkthdr.len = sizeof(struct ether_header) + 3; IF_ENQUEUE(&ifp->if_snd, m); bm_start(ifp); return; } #endif