1114577Sakiyama/*-
2114577Sakiyama * Copyright (c) 2001-2003, Shunsuke Akiyama <akiyama@FreeBSD.org>.
3114577Sakiyama * All rights reserved.
4114577Sakiyama *
5114577Sakiyama * Redistribution and use in source and binary forms, with or without
6114577Sakiyama * modification, are permitted provided that the following conditions
7114577Sakiyama * are met:
8114577Sakiyama * 1. Redistributions of source code must retain the above copyright
9114577Sakiyama *    notice, this list of conditions and the following disclaimer.
10114577Sakiyama * 2. Redistributions in binary form must reproduce the above copyright
11114577Sakiyama *    notice, this list of conditions and the following disclaimer in the
12114577Sakiyama *    documentation and/or other materials provided with the distribution.
13114577Sakiyama *
14114577Sakiyama * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15114577Sakiyama * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16114577Sakiyama * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17114577Sakiyama * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18114577Sakiyama * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19114577Sakiyama * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20114577Sakiyama * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21114577Sakiyama * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22114577Sakiyama * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23114577Sakiyama * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24114577Sakiyama * SUCH DAMAGE.
25114577Sakiyama *
26114577Sakiyama */
27114577Sakiyama
28119418Sobrien#include <sys/cdefs.h>
29119418Sobrien__FBSDID("$FreeBSD$");
30119418Sobrien
31114577Sakiyama/*
32114577Sakiyama * driver for RealTek RTL8150 internal PHY
33114577Sakiyama */
34114577Sakiyama
35114577Sakiyama#include <sys/param.h>
36114577Sakiyama#include <sys/systm.h>
37114577Sakiyama#include <sys/kernel.h>
38114577Sakiyama#include <sys/malloc.h>
39129876Sphk#include <sys/module.h>
40114577Sakiyama#include <sys/socket.h>
41114577Sakiyama#include <sys/bus.h>
42114577Sakiyama
43114577Sakiyama#include <net/if.h>
44114577Sakiyama#include <net/if_arp.h>
45114577Sakiyama#include <net/if_media.h>
46114577Sakiyama
47114577Sakiyama#include <dev/mii/mii.h>
48114577Sakiyama#include <dev/mii/miivar.h>
49114577Sakiyama#include "miidevs.h"
50114577Sakiyama
51226154Smarius#include <dev/usb/net/ruephyreg.h>
52114577Sakiyama
53114577Sakiyama#include "miibus_if.h"
54114577Sakiyama
55114577Sakiyamastatic int ruephy_probe(device_t);
56114577Sakiyamastatic int ruephy_attach(device_t);
57114577Sakiyama
58114577Sakiyamastatic device_method_t ruephy_methods[] = {
59114577Sakiyama	/* device interface */
60114577Sakiyama	DEVMETHOD(device_probe,		ruephy_probe),
61114577Sakiyama	DEVMETHOD(device_attach,	ruephy_attach),
62114577Sakiyama	DEVMETHOD(device_detach,	mii_phy_detach),
63114577Sakiyama	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
64227908Smarius	DEVMETHOD_END
65114577Sakiyama};
66114577Sakiyama
67114577Sakiyamastatic devclass_t ruephy_devclass;
68114577Sakiyama
69114577Sakiyamastatic driver_t ruephy_driver = {
70233774Shselasky	.name = "ruephy",
71233774Shselasky	.methods = ruephy_methods,
72233774Shselasky	.size = sizeof(struct mii_softc)
73114577Sakiyama};
74114577Sakiyama
75114577SakiyamaDRIVER_MODULE(ruephy, miibus, ruephy_driver, ruephy_devclass, 0, 0);
76114577Sakiyama
77114577Sakiyamastatic int ruephy_service(struct mii_softc *, struct mii_data *, int);
78114577Sakiyamastatic void ruephy_reset(struct mii_softc *);
79114577Sakiyamastatic void ruephy_status(struct mii_softc *);
80114577Sakiyama
81165989Smarius/*
82165989Smarius * The RealTek RTL8150 internal PHY doesn't have vendor/device ID
83165989Smarius * registers; rue(4) fakes up a return value of all zeros.
84165989Smarius */
85165989Smariusstatic const struct mii_phydesc ruephys[] = {
86165989Smarius	{ 0, 0, "RealTek RTL8150 internal media interface" },
87165989Smarius	MII_PHY_END
88165989Smarius};
89165989Smarius
90221407Smariusstatic const struct mii_phy_funcs ruephy_funcs = {
91221407Smarius	ruephy_service,
92221407Smarius	ruephy_status,
93221407Smarius	ruephy_reset
94221407Smarius};
95221407Smarius
96114577Sakiyamastatic int
97114577Sakiyamaruephy_probe(device_t dev)
98114577Sakiyama{
99114577Sakiyama
100165989Smarius	if (strcmp(device_get_name(device_get_parent(device_get_parent(dev))),
101165989Smarius	    "rue") == 0)
102165989Smarius		return (mii_phy_dev_probe(dev, ruephys, BUS_PROBE_DEFAULT));
103165989Smarius	return (ENXIO);
104114577Sakiyama}
105114577Sakiyama
106114577Sakiyamastatic int
107114577Sakiyamaruephy_attach(device_t dev)
108114577Sakiyama{
109114577Sakiyama
110221407Smarius	mii_phy_dev_attach(dev, MIIF_NOISOLATE | MIIF_NOMANPAUSE,
111221407Smarius	    &ruephy_funcs, 1);
112114577Sakiyama	return (0);
113114577Sakiyama}
114114577Sakiyama
115114577Sakiyamastatic int
116114577Sakiyamaruephy_service(struct mii_softc *sc, struct mii_data *mii, int cmd)
117114577Sakiyama{
118114577Sakiyama	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
119114577Sakiyama	int reg;
120114577Sakiyama
121114577Sakiyama	switch (cmd) {
122114577Sakiyama	case MII_POLLSTAT:
123114577Sakiyama		break;
124114577Sakiyama
125114577Sakiyama	case MII_MEDIACHG:
126114577Sakiyama		/*
127114577Sakiyama		 * If the interface is not up, don't do anything.
128114577Sakiyama		 */
129114577Sakiyama		if ((mii->mii_ifp->if_flags & IFF_UP) == 0)
130114577Sakiyama			break;
131114577Sakiyama
132165989Smarius		mii_phy_setmedia(sc);
133114577Sakiyama		break;
134114577Sakiyama
135114577Sakiyama	case MII_TICK:
136114577Sakiyama		/*
137114577Sakiyama		 * Is the interface even up?
138114577Sakiyama		 */
139114577Sakiyama		if ((mii->mii_ifp->if_flags & IFF_UP) == 0)
140114577Sakiyama			return (0);
141114577Sakiyama
142114577Sakiyama		/*
143114577Sakiyama		 * Only used for autonegotiation.
144114577Sakiyama		 */
145114577Sakiyama		if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO)
146114577Sakiyama			break;
147114577Sakiyama
148114577Sakiyama		/*
149114577Sakiyama		 * Check to see if we have link.  If we do, we don't
150114577Sakiyama		 * need to restart the autonegotiation process.  Read
151114577Sakiyama		 * the MSR twice in case it's latched.
152114577Sakiyama		 */
153114577Sakiyama		reg = PHY_READ(sc, RUEPHY_MII_MSR) |
154165989Smarius		    PHY_READ(sc, RUEPHY_MII_MSR);
155114577Sakiyama		if (reg & RUEPHY_MSR_LINK)
156114577Sakiyama			break;
157114577Sakiyama
158213364Smarius		/* Only retry autonegotiation every mii_anegticks seconds. */
159213364Smarius		if (sc->mii_ticks <= sc->mii_anegticks)
160128870Sandre			break;
161114577Sakiyama
162114577Sakiyama		sc->mii_ticks = 0;
163221407Smarius		PHY_RESET(sc);
164114577Sakiyama		if (mii_phy_auto(sc) == EJUSTRETURN)
165114577Sakiyama			return (0);
166114577Sakiyama		break;
167114577Sakiyama	}
168114577Sakiyama
169114577Sakiyama	/* Update the media status. */
170221407Smarius	PHY_STATUS(sc);
171114577Sakiyama
172114577Sakiyama	/* Callback if something changed. */
173114577Sakiyama	mii_phy_update(sc, cmd);
174114577Sakiyama
175114577Sakiyama	return (0);
176114577Sakiyama}
177114577Sakiyama
178114577Sakiyamastatic void
179114577Sakiyamaruephy_reset(struct mii_softc *sc)
180114577Sakiyama{
181114577Sakiyama
182114577Sakiyama	mii_phy_reset(sc);
183114577Sakiyama
184114577Sakiyama	/*
185114577Sakiyama	 * XXX RealTek RTL8150 PHY doesn't set the BMCR properly after
186114577Sakiyama	 * XXX reset, which breaks autonegotiation.
187114577Sakiyama	 */
188114577Sakiyama	PHY_WRITE(sc, MII_BMCR, (BMCR_S100 | BMCR_AUTOEN | BMCR_FDX));
189114577Sakiyama}
190114577Sakiyama
191114577Sakiyamastatic void
192114577Sakiyamaruephy_status(struct mii_softc *phy)
193114577Sakiyama{
194114577Sakiyama	struct mii_data *mii = phy->mii_pdata;
195165989Smarius	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
196114577Sakiyama	int bmsr, bmcr, msr;
197114577Sakiyama
198114577Sakiyama	mii->mii_media_status = IFM_AVALID;
199114577Sakiyama	mii->mii_media_active = IFM_ETHER;
200114577Sakiyama
201114577Sakiyama	msr = PHY_READ(phy, RUEPHY_MII_MSR) | PHY_READ(phy, RUEPHY_MII_MSR);
202114577Sakiyama	if (msr & RUEPHY_MSR_LINK)
203114577Sakiyama		mii->mii_media_status |= IFM_ACTIVE;
204114577Sakiyama
205114577Sakiyama	bmcr = PHY_READ(phy, MII_BMCR);
206114577Sakiyama	if (bmcr & BMCR_ISO) {
207114577Sakiyama		mii->mii_media_active |= IFM_NONE;
208114577Sakiyama		mii->mii_media_status = 0;
209114577Sakiyama		return;
210114577Sakiyama	}
211114577Sakiyama
212114577Sakiyama	bmsr = PHY_READ(phy, MII_BMSR) | PHY_READ(phy, MII_BMSR);
213114577Sakiyama	if (bmcr & BMCR_AUTOEN) {
214114577Sakiyama		if ((bmsr & BMSR_ACOMP) == 0) {
215114577Sakiyama			/* Erg, still trying, I guess... */
216114577Sakiyama			mii->mii_media_active |= IFM_NONE;
217114577Sakiyama			return;
218114577Sakiyama		}
219114577Sakiyama
220114577Sakiyama		if (msr & RUEPHY_MSR_SPEED100)
221114577Sakiyama			mii->mii_media_active |= IFM_100_TX;
222114577Sakiyama		else
223114577Sakiyama			mii->mii_media_active |= IFM_10_T;
224114577Sakiyama
225114577Sakiyama		if (msr & RUEPHY_MSR_DUPLEX)
226221407Smarius			mii->mii_media_active |=
227221407Smarius			    IFM_FDX | mii_phy_flowstatus(phy);
228213384Smarius		else
229213384Smarius			mii->mii_media_active |= IFM_HDX;
230114577Sakiyama	} else
231165989Smarius		mii->mii_media_active = ife->ifm_media;
232114577Sakiyama}
233