amphy.c revision 221407
1218885Sdim/*-
2218885Sdim * Copyright (c) 1997, 1998, 1999
3218885Sdim *	Bill Paul <wpaul@ee.columbia.edu>.  All rights reserved.
4218885Sdim *
5218885Sdim * Redistribution and use in source and binary forms, with or without
6218885Sdim * modification, are permitted provided that the following conditions
7218885Sdim * are met:
8218885Sdim * 1. Redistributions of source code must retain the above copyright
9218885Sdim *    notice, this list of conditions and the following disclaimer.
10218885Sdim * 2. Redistributions in binary form must reproduce the above copyright
11218885Sdim *    notice, this list of conditions and the following disclaimer in the
12218885Sdim *    documentation and/or other materials provided with the distribution.
13218885Sdim * 3. All advertising materials mentioning features or use of this software
14218885Sdim *    must display the following acknowledgement:
15218885Sdim *	This product includes software developed by Bill Paul.
16218885Sdim * 4. Neither the name of the author nor the names of any co-contributors
17218885Sdim *    may be used to endorse or promote products derived from this software
18218885Sdim *    without specific prior written permission.
19218885Sdim *
20218885Sdim * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
21218885Sdim * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22218885Sdim * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23218885Sdim * ARE DISCLAIMED.  IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD
24218885Sdim * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25218885Sdim * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26218885Sdim * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27218885Sdim * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28218885Sdim * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29218885Sdim * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
30218885Sdim * THE POSSIBILITY OF SUCH DAMAGE.
31218885Sdim */
32218885Sdim
33218885Sdim#include <sys/cdefs.h>
34218885Sdim__FBSDID("$FreeBSD: head/sys/dev/mii/amphy.c 221407 2011-05-03 19:51:29Z marius $");
35218885Sdim
36218885Sdim/*
37218885Sdim * driver for AMD AM79c873 PHYs
38218885Sdim * This driver also works for Davicom DM910{1,2} PHYs, which appear
39218885Sdim * to be AM79c873 workalikes.
40218885Sdim */
41218885Sdim
42218885Sdim#include <sys/param.h>
43218885Sdim#include <sys/systm.h>
44218885Sdim#include <sys/kernel.h>
45218885Sdim#include <sys/module.h>
46218885Sdim#include <sys/socket.h>
47218885Sdim#include <sys/bus.h>
48218885Sdim
49218885Sdim#include <net/if.h>
50218885Sdim#include <net/if_media.h>
51218885Sdim
52218885Sdim#include <dev/mii/mii.h>
53218885Sdim#include <dev/mii/miivar.h>
54218885Sdim#include "miidevs.h"
55218885Sdim
56218885Sdim#include <dev/mii/amphyreg.h>
57218885Sdim
58218885Sdim#include "miibus_if.h"
59218885Sdim
60218885Sdimstatic int amphy_probe(device_t);
61218885Sdimstatic int amphy_attach(device_t);
62218885Sdim
63218885Sdimstatic device_method_t amphy_methods[] = {
64218885Sdim	/* device interface */
65218885Sdim	DEVMETHOD(device_probe,		amphy_probe),
66218885Sdim	DEVMETHOD(device_attach,	amphy_attach),
67218885Sdim	DEVMETHOD(device_detach,	mii_phy_detach),
68218885Sdim	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
69218885Sdim	{ 0, 0 }
70221345Sdim};
71218885Sdim
72218885Sdimstatic devclass_t amphy_devclass;
73218885Sdim
74221345Sdimstatic driver_t amphy_driver = {
75218885Sdim	"amphy",
76218885Sdim	amphy_methods,
77218885Sdim	sizeof(struct mii_softc)
78218885Sdim};
79218885Sdim
80218885SdimDRIVER_MODULE(amphy, miibus, amphy_driver, amphy_devclass, 0, 0);
81218885Sdim
82218885Sdimstatic int	amphy_service(struct mii_softc *, struct mii_data *, int);
83218885Sdimstatic void	amphy_status(struct mii_softc *);
84218885Sdim
85218885Sdimstatic const struct mii_phydesc amphys[] = {
86218885Sdim	MII_PHY_DESC(xxDAVICOM, DM9102),
87218885Sdim	MII_PHY_DESC(xxDAVICOM, DM9101),
88218885Sdim	MII_PHY_DESC(yyDAVICOM, DM9101),
89218885Sdim	MII_PHY_END
90218885Sdim};
91218885Sdim
92218885Sdimstatic const struct mii_phy_funcs amphy_funcs = {
93218885Sdim	amphy_service,
94218885Sdim	amphy_status,
95218885Sdim	mii_phy_reset
96218885Sdim};
97218885Sdim
98218885Sdimstatic int
99218885Sdimamphy_probe(device_t dev)
100218885Sdim{
101218885Sdim
102218885Sdim	return (mii_phy_dev_probe(dev, amphys, BUS_PROBE_DEFAULT));
103218885Sdim}
104218885Sdim
105218885Sdimstatic int
106218885Sdimamphy_attach(device_t dev)
107218885Sdim{
108218885Sdim
109218885Sdim	mii_phy_dev_attach(dev, MIIF_NOMANPAUSE, &amphy_funcs, 1);
110221345Sdim	return (0);
111218885Sdim}
112218885Sdim
113218885Sdimstatic int
114218885Sdimamphy_service(struct mii_softc *sc, struct mii_data *mii, int cmd)
115218885Sdim{
116218885Sdim
117218885Sdim	switch (cmd) {
118218885Sdim	case MII_POLLSTAT:
119218885Sdim		break;
120218885Sdim
121218885Sdim	case MII_MEDIACHG:
122218885Sdim		/*
123218885Sdim		 * If the interface is not up, don't do anything.
124221345Sdim		 */
125218885Sdim		if ((mii->mii_ifp->if_flags & IFF_UP) == 0)
126218885Sdim			break;
127218885Sdim
128218885Sdim		mii_phy_setmedia(sc);
129218885Sdim		break;
130218885Sdim
131218885Sdim	case MII_TICK:
132218885Sdim		if (mii_phy_tick(sc) == EJUSTRETURN)
133218885Sdim			return (0);
134218885Sdim		break;
135218885Sdim	}
136218885Sdim
137221345Sdim	/* Update the media status. */
138218885Sdim	PHY_STATUS(sc);
139218885Sdim
140221345Sdim	/* Callback if something changed. */
141218885Sdim	mii_phy_update(sc, cmd);
142218885Sdim	return (0);
143218885Sdim}
144218885Sdim
145218885Sdimstatic void
146218885Sdimamphy_status(struct mii_softc *sc)
147218885Sdim{
148218885Sdim	struct mii_data *mii = sc->mii_pdata;
149218885Sdim	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
150218885Sdim	int bmsr, bmcr, par, anlpar;
151218885Sdim
152218885Sdim	mii->mii_media_status = IFM_AVALID;
153218885Sdim	mii->mii_media_active = IFM_ETHER;
154218885Sdim
155218885Sdim	bmsr = PHY_READ(sc, MII_BMSR) |
156218885Sdim	    PHY_READ(sc, MII_BMSR);
157218885Sdim	if (bmsr & BMSR_LINK)
158218885Sdim		mii->mii_media_status |= IFM_ACTIVE;
159218885Sdim
160218885Sdim	bmcr = PHY_READ(sc, MII_BMCR);
161218885Sdim	if (bmcr & BMCR_ISO) {
162218885Sdim		mii->mii_media_active |= IFM_NONE;
163218885Sdim		mii->mii_media_status = 0;
164218885Sdim		return;
165218885Sdim	}
166218885Sdim
167218885Sdim	if (bmcr & BMCR_LOOP)
168218885Sdim		mii->mii_media_active |= IFM_LOOP;
169218885Sdim
170218885Sdim	if (bmcr & BMCR_AUTOEN) {
171218885Sdim		/*
172218885Sdim		 * The PAR status bits are only valid if autonegotiation
173218885Sdim		 * has completed (or it's disabled).
174218885Sdim		 */
175218885Sdim		if ((bmsr & BMSR_ACOMP) == 0) {
176218885Sdim			/* Erg, still trying, I guess... */
177218885Sdim			mii->mii_media_active |= IFM_NONE;
178218885Sdim			return;
179221345Sdim		}
180218885Sdim
181221345Sdim		if (PHY_READ(sc, MII_ANER) & ANER_LPAN) {
182221345Sdim			anlpar = PHY_READ(sc, MII_ANAR) &
183218885Sdim			    PHY_READ(sc, MII_ANLPAR);
184221345Sdim			if (anlpar & ANLPAR_TX_FD)
185221345Sdim				mii->mii_media_active |= IFM_100_TX|IFM_FDX;
186221345Sdim			else if (anlpar & ANLPAR_T4)
187218885Sdim				mii->mii_media_active |= IFM_100_T4|IFM_HDX;
188218885Sdim			else if (anlpar & ANLPAR_TX)
189221345Sdim				mii->mii_media_active |= IFM_100_TX|IFM_HDX;
190221345Sdim			else if (anlpar & ANLPAR_10_FD)
191221345Sdim				mii->mii_media_active |= IFM_10_T|IFM_FDX;
192221345Sdim			else if (anlpar & ANLPAR_10)
193221345Sdim				mii->mii_media_active |= IFM_10_T|IFM_HDX;
194221345Sdim			else
195218885Sdim				mii->mii_media_active |= IFM_NONE;
196218885Sdim			return;
197218885Sdim		}
198218885Sdim
199218885Sdim		/*
200218885Sdim		 * Link partner is not capable of autonegotiation.
201218885Sdim		 */
202218885Sdim		par = PHY_READ(sc, MII_AMPHY_DSCSR);
203218885Sdim		if (par & DSCSR_100FDX)
204218885Sdim			mii->mii_media_active |= IFM_100_TX|IFM_FDX;
205218885Sdim		else if (par & DSCSR_100HDX)
206218885Sdim			mii->mii_media_active |= IFM_100_TX|IFM_HDX;
207218885Sdim		else if (par & DSCSR_10FDX)
208218885Sdim			mii->mii_media_active |= IFM_10_T|IFM_HDX;
209218885Sdim		else if (par & DSCSR_10HDX)
210218885Sdim			mii->mii_media_active |= IFM_10_T|IFM_HDX;
211218885Sdim		if ((mii->mii_media_active & IFM_FDX) != 0)
212218885Sdim			mii->mii_media_active |= mii_phy_flowstatus(sc);
213221345Sdim	} else
214218885Sdim		mii->mii_media_active = ife->ifm_media;
215221345Sdim}
216221345Sdim