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: releng/11.0/sys/dev/usb/net/ruephy.c 257184 2013-10-26 18:40:17Z glebius $");
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:
126165989Smarius		mii_phy_setmedia(sc);
127114577Sakiyama		break;
128114577Sakiyama
129114577Sakiyama	case MII_TICK:
130114577Sakiyama		/*
131114577Sakiyama		 * Only used for autonegotiation.
132114577Sakiyama		 */
133114577Sakiyama		if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO)
134114577Sakiyama			break;
135114577Sakiyama
136114577Sakiyama		/*
137114577Sakiyama		 * Check to see if we have link.  If we do, we don't
138114577Sakiyama		 * need to restart the autonegotiation process.  Read
139114577Sakiyama		 * the MSR twice in case it's latched.
140114577Sakiyama		 */
141114577Sakiyama		reg = PHY_READ(sc, RUEPHY_MII_MSR) |
142165989Smarius		    PHY_READ(sc, RUEPHY_MII_MSR);
143114577Sakiyama		if (reg & RUEPHY_MSR_LINK)
144114577Sakiyama			break;
145114577Sakiyama
146213364Smarius		/* Only retry autonegotiation every mii_anegticks seconds. */
147213364Smarius		if (sc->mii_ticks <= sc->mii_anegticks)
148128870Sandre			break;
149114577Sakiyama
150114577Sakiyama		sc->mii_ticks = 0;
151221407Smarius		PHY_RESET(sc);
152114577Sakiyama		if (mii_phy_auto(sc) == EJUSTRETURN)
153114577Sakiyama			return (0);
154114577Sakiyama		break;
155114577Sakiyama	}
156114577Sakiyama
157114577Sakiyama	/* Update the media status. */
158221407Smarius	PHY_STATUS(sc);
159114577Sakiyama
160114577Sakiyama	/* Callback if something changed. */
161114577Sakiyama	mii_phy_update(sc, cmd);
162114577Sakiyama
163114577Sakiyama	return (0);
164114577Sakiyama}
165114577Sakiyama
166114577Sakiyamastatic void
167114577Sakiyamaruephy_reset(struct mii_softc *sc)
168114577Sakiyama{
169114577Sakiyama
170114577Sakiyama	mii_phy_reset(sc);
171114577Sakiyama
172114577Sakiyama	/*
173114577Sakiyama	 * XXX RealTek RTL8150 PHY doesn't set the BMCR properly after
174114577Sakiyama	 * XXX reset, which breaks autonegotiation.
175114577Sakiyama	 */
176114577Sakiyama	PHY_WRITE(sc, MII_BMCR, (BMCR_S100 | BMCR_AUTOEN | BMCR_FDX));
177114577Sakiyama}
178114577Sakiyama
179114577Sakiyamastatic void
180114577Sakiyamaruephy_status(struct mii_softc *phy)
181114577Sakiyama{
182114577Sakiyama	struct mii_data *mii = phy->mii_pdata;
183165989Smarius	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
184114577Sakiyama	int bmsr, bmcr, msr;
185114577Sakiyama
186114577Sakiyama	mii->mii_media_status = IFM_AVALID;
187114577Sakiyama	mii->mii_media_active = IFM_ETHER;
188114577Sakiyama
189114577Sakiyama	msr = PHY_READ(phy, RUEPHY_MII_MSR) | PHY_READ(phy, RUEPHY_MII_MSR);
190114577Sakiyama	if (msr & RUEPHY_MSR_LINK)
191114577Sakiyama		mii->mii_media_status |= IFM_ACTIVE;
192114577Sakiyama
193114577Sakiyama	bmcr = PHY_READ(phy, MII_BMCR);
194114577Sakiyama	if (bmcr & BMCR_ISO) {
195114577Sakiyama		mii->mii_media_active |= IFM_NONE;
196114577Sakiyama		mii->mii_media_status = 0;
197114577Sakiyama		return;
198114577Sakiyama	}
199114577Sakiyama
200114577Sakiyama	bmsr = PHY_READ(phy, MII_BMSR) | PHY_READ(phy, MII_BMSR);
201114577Sakiyama	if (bmcr & BMCR_AUTOEN) {
202114577Sakiyama		if ((bmsr & BMSR_ACOMP) == 0) {
203114577Sakiyama			/* Erg, still trying, I guess... */
204114577Sakiyama			mii->mii_media_active |= IFM_NONE;
205114577Sakiyama			return;
206114577Sakiyama		}
207114577Sakiyama
208114577Sakiyama		if (msr & RUEPHY_MSR_SPEED100)
209114577Sakiyama			mii->mii_media_active |= IFM_100_TX;
210114577Sakiyama		else
211114577Sakiyama			mii->mii_media_active |= IFM_10_T;
212114577Sakiyama
213114577Sakiyama		if (msr & RUEPHY_MSR_DUPLEX)
214221407Smarius			mii->mii_media_active |=
215221407Smarius			    IFM_FDX | mii_phy_flowstatus(phy);
216213384Smarius		else
217213384Smarius			mii->mii_media_active |= IFM_HDX;
218114577Sakiyama	} else
219165989Smarius		mii->mii_media_active = ife->ifm_media;
220114577Sakiyama}
221