mii.c revision 84333
150120Swpaul/*	$NetBSD: mii.c,v 1.12 1999/08/03 19:41:49 drochner Exp $	*/
250120Swpaul
350120Swpaul/*-
450120Swpaul * Copyright (c) 1998 The NetBSD Foundation, Inc.
550120Swpaul * All rights reserved.
650120Swpaul *
750120Swpaul * This code is derived from software contributed to The NetBSD Foundation
850120Swpaul * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
950120Swpaul * NASA Ames Research Center.
1050120Swpaul *
1150120Swpaul * Redistribution and use in source and binary forms, with or without
1250120Swpaul * modification, are permitted provided that the following conditions
1350120Swpaul * are met:
1450120Swpaul * 1. Redistributions of source code must retain the above copyright
1550120Swpaul *    notice, this list of conditions and the following disclaimer.
1650120Swpaul * 2. Redistributions in binary form must reproduce the above copyright
1750120Swpaul *    notice, this list of conditions and the following disclaimer in the
1850120Swpaul *    documentation and/or other materials provided with the distribution.
1950120Swpaul * 3. All advertising materials mentioning features or use of this software
2050120Swpaul *    must display the following acknowledgement:
2150120Swpaul *	This product includes software developed by the NetBSD
2250120Swpaul *	Foundation, Inc. and its contributors.
2350120Swpaul * 4. Neither the name of The NetBSD Foundation nor the names of its
2450120Swpaul *    contributors may be used to endorse or promote products derived
2550120Swpaul *    from this software without specific prior written permission.
2650120Swpaul *
2750120Swpaul * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
2850120Swpaul * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
2950120Swpaul * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
3050120Swpaul * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
3150120Swpaul * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
3250120Swpaul * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
3350120Swpaul * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
3450120Swpaul * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
3550120Swpaul * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
3650120Swpaul * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
3750120Swpaul * POSSIBILITY OF SUCH DAMAGE.
3850120Swpaul */
3950120Swpaul
4050120Swpaul/*
4150120Swpaul * MII bus layer, glues MII-capable network interface drivers to sharable
4250120Swpaul * PHY drivers.  This exports an interface compatible with BSD/OS 3.0's,
4350120Swpaul * plus some NetBSD extensions.
4450120Swpaul */
4550120Swpaul
4650120Swpaul#include <sys/param.h>
4750120Swpaul#include <sys/systm.h>
4850120Swpaul#include <sys/socket.h>
4950120Swpaul#include <sys/malloc.h>
5050120Swpaul#include <sys/module.h>
5150120Swpaul#include <sys/bus.h>
5250120Swpaul
5350120Swpaul#include <net/if.h>
5450120Swpaul#include <net/if_media.h>
5550120Swpaul
5650120Swpaul#include <dev/mii/mii.h>
5750120Swpaul#include <dev/mii/miivar.h>
5850120Swpaul
5959757SpeterMODULE_VERSION(miibus, 1);
6059757Speter
6150120Swpaul#include "miibus_if.h"
6250120Swpaul
6350120Swpaul#if !defined(lint)
6450120Swpaulstatic const char rcsid[] =
6550959Speter  "$FreeBSD: head/sys/dev/mii/mii.c 84333 2001-10-01 22:57:57Z mjacob $";
6650120Swpaul#endif
6750120Swpaul
6850120Swpaulstatic int miibus_readreg	__P((device_t, int, int));
6950120Swpaulstatic int miibus_writereg	__P((device_t, int, int, int));
7050120Swpaulstatic void miibus_statchg	__P((device_t));
7184140Sjlemonstatic void miibus_linkchg	__P((device_t));
7250120Swpaulstatic void miibus_mediainit	__P((device_t));
7350120Swpaul
7450120Swpaulstatic device_method_t miibus_methods[] = {
7550120Swpaul	/* device interface */
7650120Swpaul	DEVMETHOD(device_probe,		miibus_probe),
7750120Swpaul	DEVMETHOD(device_attach,	miibus_attach),
7850120Swpaul	DEVMETHOD(device_detach,	miibus_detach),
7950120Swpaul	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
8050120Swpaul
8150120Swpaul	/* bus interface */
8250120Swpaul	DEVMETHOD(bus_print_child,	bus_generic_print_child),
8350120Swpaul	DEVMETHOD(bus_driver_added,	bus_generic_driver_added),
8450120Swpaul
8550120Swpaul	/* MII interface */
8650120Swpaul	DEVMETHOD(miibus_readreg,	miibus_readreg),
8750120Swpaul	DEVMETHOD(miibus_writereg,	miibus_writereg),
8850120Swpaul	DEVMETHOD(miibus_statchg,	miibus_statchg),
8984140Sjlemon	DEVMETHOD(miibus_linkchg,	miibus_linkchg),
9050120Swpaul	DEVMETHOD(miibus_mediainit,	miibus_mediainit),
9150120Swpaul
9250120Swpaul	{ 0, 0 }
9350120Swpaul};
9450120Swpaul
9550120Swpauldevclass_t miibus_devclass;
9650120Swpaul
9750120Swpauldriver_t miibus_driver = {
9850120Swpaul	"miibus",
9950120Swpaul	miibus_methods,
10050120Swpaul	sizeof(struct mii_data)
10150120Swpaul};
10250120Swpaul
10350120Swpaul/*
10450120Swpaul * Helper function used by network interface drivers, attaches PHYs
10550120Swpaul * to the network interface driver parent.
10650120Swpaul */
10750120Swpaul
10850120Swpaulint miibus_probe(dev)
10950120Swpaul	device_t		dev;
11050120Swpaul{
11150120Swpaul	struct mii_attach_args	ma, *args;
11250120Swpaul	struct mii_data		*mii;
11350577Swpaul	device_t		child = NULL, parent;
11450120Swpaul	int			bmsr, capmask = 0xFFFFFFFF;
11550120Swpaul
11650120Swpaul	mii = device_get_softc(dev);
11750120Swpaul	parent = device_get_parent(dev);
11850120Swpaul	LIST_INIT(&mii->mii_phys);
11950120Swpaul
12050120Swpaul	for (ma.mii_phyno = 0; ma.mii_phyno < MII_NPHY; ma.mii_phyno++) {
12150120Swpaul		/*
12250120Swpaul		 * Check to see if there is a PHY at this address.  Note,
12350120Swpaul		 * many braindead PHYs report 0/0 in their ID registers,
12450120Swpaul		 * so we test for media in the BMSR.
12550120Swpaul	 	 */
12650120Swpaul		bmsr = MIIBUS_READREG(parent, ma.mii_phyno, MII_BMSR);
12750120Swpaul		if (bmsr == 0 || bmsr == 0xffff ||
12850120Swpaul		    (bmsr & BMSR_MEDIAMASK) == 0) {
12950120Swpaul			/* Assume no PHY at this address. */
13050120Swpaul			continue;
13150120Swpaul		}
13250120Swpaul
13350120Swpaul		/*
13450120Swpaul		 * Extract the IDs. Braindead PHYs will be handled by
13550120Swpaul		 * the `ukphy' driver, as we have no ID information to
13650120Swpaul		 * match on.
13750120Swpaul	 	 */
13850120Swpaul		ma.mii_id1 = MIIBUS_READREG(parent, ma.mii_phyno,
13950120Swpaul		    MII_PHYIDR1);
14050120Swpaul		ma.mii_id2 = MIIBUS_READREG(parent, ma.mii_phyno,
14150120Swpaul		    MII_PHYIDR2);
14250120Swpaul
14350120Swpaul		ma.mii_data = mii;
14450120Swpaul		ma.mii_capmask = capmask;
14550120Swpaul
14650120Swpaul		args = malloc(sizeof(struct mii_attach_args),
14750120Swpaul		    M_DEVBUF, M_NOWAIT);
14850120Swpaul		bcopy((char *)&ma, (char *)args, sizeof(ma));
14954073Smdodd		child = device_add_child(dev, NULL, -1);
15054073Smdodd		device_set_ivars(child, args);
15150120Swpaul	}
15250120Swpaul
15350391Swpaul	if (child == NULL)
15450120Swpaul		return(ENXIO);
15550120Swpaul
15650120Swpaul	device_set_desc(dev, "MII bus");
15750120Swpaul
15850120Swpaul	return(0);
15950120Swpaul}
16050120Swpaul
16150120Swpaulint miibus_attach(dev)
16250120Swpaul	device_t		dev;
16350120Swpaul{
16450120Swpaul	void			**v;
16550120Swpaul	ifm_change_cb_t		ifmedia_upd;
16650120Swpaul	ifm_stat_cb_t		ifmedia_sts;
16750120Swpaul	struct mii_data		*mii;
16850120Swpaul
16950120Swpaul	mii = device_get_softc(dev);
17084333Smjacob	/*
17184333Smjacob	 * Note that each NIC's softc must start with an ifnet structure.
17284333Smjacob	 */
17350120Swpaul	mii->mii_ifp = device_get_softc(device_get_parent(dev));
17450120Swpaul	v = device_get_ivars(dev);
17550120Swpaul	ifmedia_upd = v[0];
17650120Swpaul	ifmedia_sts = v[1];
17750120Swpaul	ifmedia_init(&mii->mii_media, IFM_IMASK, ifmedia_upd, ifmedia_sts);
17850120Swpaul	bus_generic_attach(dev);
17950120Swpaul
18050120Swpaul	return(0);
18150120Swpaul}
18250120Swpaul
18350120Swpaulint miibus_detach(dev)
18450120Swpaul	device_t		dev;
18550120Swpaul{
18650120Swpaul	struct mii_data		*mii;
18750120Swpaul
18850120Swpaul	bus_generic_detach(dev);
18950120Swpaul	mii = device_get_softc(dev);
19050120Swpaul	ifmedia_removeall(&mii->mii_media);
19150120Swpaul	mii->mii_ifp = NULL;
19250120Swpaul
19350120Swpaul	return(0);
19450120Swpaul}
19550120Swpaul
19650120Swpaulstatic int miibus_readreg(dev, phy, reg)
19750120Swpaul	device_t		dev;
19850120Swpaul	int			phy, reg;
19950120Swpaul{
20050120Swpaul	device_t		parent;
20150120Swpaul
20250120Swpaul	parent = device_get_parent(dev);
20350120Swpaul	return(MIIBUS_READREG(parent, phy, reg));
20450120Swpaul}
20550120Swpaul
20650120Swpaulstatic int miibus_writereg(dev, phy, reg, data)
20750120Swpaul	device_t		dev;
20850120Swpaul	int			phy, reg, data;
20950120Swpaul{
21050120Swpaul	device_t		parent;
21150120Swpaul
21250120Swpaul	parent = device_get_parent(dev);
21350120Swpaul	return(MIIBUS_WRITEREG(parent, phy, reg, data));
21450120Swpaul}
21550120Swpaul
21650120Swpaulstatic void miibus_statchg(dev)
21750120Swpaul	device_t		dev;
21850120Swpaul{
21950120Swpaul	device_t		parent;
22050120Swpaul
22150120Swpaul	parent = device_get_parent(dev);
22250120Swpaul	MIIBUS_STATCHG(parent);
22350120Swpaul	return;
22450120Swpaul}
22550120Swpaul
22684140Sjlemonstatic void
22784140Sjlemonmiibus_linkchg(dev)
22884140Sjlemon	device_t dev;
22984140Sjlemon{
23084140Sjlemon	struct mii_data *mii;
23184140Sjlemon	struct ifnet *ifp;
23284140Sjlemon	device_t parent;
23384140Sjlemon	int link;
23484140Sjlemon
23584140Sjlemon	parent = device_get_parent(dev);
23684140Sjlemon	MIIBUS_LINKCHG(parent);
23784140Sjlemon
23884140Sjlemon	mii = device_get_softc(dev);
23984333Smjacob	/*
24084333Smjacob	 * Note that each NIC's softc must start with an ifnet structure.
24184333Smjacob	 */
24284140Sjlemon	ifp = device_get_softc(parent);
24384140Sjlemon
24484140Sjlemon	if (mii->mii_media_status & IFM_AVALID) {
24584140Sjlemon		if (mii->mii_media_status & IFM_ACTIVE)
24684140Sjlemon			link = NOTE_LINKUP;
24784140Sjlemon		else
24884140Sjlemon			link = NOTE_LINKDOWN;
24984140Sjlemon	} else {
25084140Sjlemon		link = NOTE_LINKINV;
25184140Sjlemon	}
25284140Sjlemon
25384140Sjlemon	KNOTE(&ifp->if_klist, link);
25484140Sjlemon}
25584140Sjlemon
25650120Swpaulstatic void miibus_mediainit(dev)
25750120Swpaul	device_t		dev;
25850120Swpaul{
25950120Swpaul	struct mii_data		*mii;
26050120Swpaul	struct ifmedia_entry	*m;
26150120Swpaul	int			media = 0;
26250120Swpaul
26350577Swpaul	/* Poke the parent in case it has any media of its own to add. */
26450577Swpaul	MIIBUS_MEDIAINIT(device_get_parent(dev));
26550577Swpaul
26650120Swpaul	mii = device_get_softc(dev);
26772012Sphk	LIST_FOREACH(m, &mii->mii_media.ifm_list, ifm_list) {
26850120Swpaul		media = m->ifm_media;
26950120Swpaul		if (media == (IFM_ETHER|IFM_AUTO))
27050120Swpaul			break;
27150120Swpaul	}
27250120Swpaul
27350120Swpaul	ifmedia_set(&mii->mii_media, media);
27450120Swpaul
27550120Swpaul	return;
27650120Swpaul}
27750120Swpaul
27850120Swpaulint mii_phy_probe(dev, child, ifmedia_upd, ifmedia_sts)
27950120Swpaul	device_t		dev;
28050120Swpaul	device_t		*child;
28150120Swpaul	ifm_change_cb_t		ifmedia_upd;
28250120Swpaul	ifm_stat_cb_t		ifmedia_sts;
28350120Swpaul{
28450120Swpaul	void			**v;
28550120Swpaul	int			bmsr, i;
28650120Swpaul
28750120Swpaul	v = malloc(sizeof(vm_offset_t) * 2, M_DEVBUF, M_NOWAIT);
28884333Smjacob	if (v == 0) {
28984333Smjacob		return (ENOMEM);
29084333Smjacob	}
29150120Swpaul	v[0] = ifmedia_upd;
29250120Swpaul	v[1] = ifmedia_sts;
29354073Smdodd	*child = device_add_child(dev, "miibus", -1);
29454073Smdodd	device_set_ivars(*child, v);
29550120Swpaul
29650120Swpaul	for (i = 0; i < MII_NPHY; i++) {
29750120Swpaul		bmsr = MIIBUS_READREG(dev, i, MII_BMSR);
29850120Swpaul                if (bmsr == 0 || bmsr == 0xffff ||
29950120Swpaul                    (bmsr & BMSR_MEDIAMASK) == 0) {
30050120Swpaul                        /* Assume no PHY at this address. */
30150120Swpaul                        continue;
30250120Swpaul                } else
30350120Swpaul			break;
30450120Swpaul	}
30550120Swpaul
30650120Swpaul	if (i == MII_NPHY) {
30750120Swpaul		device_delete_child(dev, *child);
30850120Swpaul		*child = NULL;
30950120Swpaul		return(ENXIO);
31050120Swpaul	}
31150120Swpaul
31250120Swpaul	bus_generic_attach(dev);
31350120Swpaul
31450120Swpaul	return(0);
31550120Swpaul}
31650120Swpaul
31750120Swpaul/*
31850120Swpaul * Media changed; notify all PHYs.
31950120Swpaul */
32050120Swpaulint
32150120Swpaulmii_mediachg(mii)
32250120Swpaul	struct mii_data *mii;
32350120Swpaul{
32450120Swpaul	struct mii_softc *child;
32550120Swpaul	int rv;
32650120Swpaul
32750120Swpaul	mii->mii_media_status = 0;
32850120Swpaul	mii->mii_media_active = IFM_NONE;
32950120Swpaul
33072012Sphk	LIST_FOREACH(child, &mii->mii_phys, mii_list) {
33150120Swpaul		rv = (*child->mii_service)(child, mii, MII_MEDIACHG);
33250120Swpaul		if (rv)
33350120Swpaul			return (rv);
33450120Swpaul	}
33550120Swpaul	return (0);
33650120Swpaul}
33750120Swpaul
33850120Swpaul/*
33950120Swpaul * Call the PHY tick routines, used during autonegotiation.
34050120Swpaul */
34150120Swpaulvoid
34250120Swpaulmii_tick(mii)
34350120Swpaul	struct mii_data *mii;
34450120Swpaul{
34550120Swpaul	struct mii_softc *child;
34650120Swpaul
34772012Sphk	LIST_FOREACH(child, &mii->mii_phys, mii_list)
34850120Swpaul		(void) (*child->mii_service)(child, mii, MII_TICK);
34950120Swpaul}
35050120Swpaul
35150120Swpaul/*
35250120Swpaul * Get media status from PHYs.
35350120Swpaul */
35450120Swpaulvoid
35550120Swpaulmii_pollstat(mii)
35650120Swpaul	struct mii_data *mii;
35750120Swpaul{
35850120Swpaul	struct mii_softc *child;
35950120Swpaul
36050120Swpaul	mii->mii_media_status = 0;
36150120Swpaul	mii->mii_media_active = IFM_NONE;
36250120Swpaul
36372012Sphk	LIST_FOREACH(child, &mii->mii_phys, mii_list)
36450120Swpaul		(void) (*child->mii_service)(child, mii, MII_POLLSTAT);
36550120Swpaul}
366