1216828Syongari/*-
2216828Syongari * Copyright (c) 2010, Pyun YongHyeon <yongari@FreeBSD.org>
3216828Syongari * All rights reserved.
4216828Syongari *
5216828Syongari * Redistribution and use in source and binary forms, with or without
6216828Syongari * modification, are permitted provided that the following conditions
7216828Syongari * are met:
8216828Syongari * 1. Redistributions of source code must retain the above copyright
9216828Syongari *    notice unmodified, this list of conditions, and the following
10216828Syongari *    disclaimer.
11216828Syongari * 2. Redistributions in binary form must reproduce the above copyright
12216828Syongari *    notice, this list of conditions and the following disclaimer in the
13216828Syongari *    documentation and/or other materials provided with the distribution.
14216828Syongari *
15216828Syongari * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16216828Syongari * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17216828Syongari * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18216828Syongari * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19216828Syongari * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20216828Syongari * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21216828Syongari * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22216828Syongari * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23216828Syongari * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24216828Syongari * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25216828Syongari * SUCH DAMAGE.
26216828Syongari */
27216828Syongari
28216828Syongari#include <sys/cdefs.h>
29216828Syongari__FBSDID("$FreeBSD: releng/11.0/sys/dev/mii/rdcphy.c 257184 2013-10-26 18:40:17Z glebius $");
30216828Syongari
31216828Syongari/*
32216828Syongari * Driver for the RDC Semiconductor R6040 10/100 PHY.
33216828Syongari */
34216828Syongari
35216828Syongari#include <sys/param.h>
36216828Syongari#include <sys/systm.h>
37216828Syongari#include <sys/kernel.h>
38216828Syongari#include <sys/module.h>
39216828Syongari#include <sys/socket.h>
40216828Syongari#include <sys/bus.h>
41216828Syongari
42216828Syongari#include <net/if.h>
43216828Syongari#include <net/if_media.h>
44216828Syongari
45216828Syongari#include <dev/mii/mii.h>
46216828Syongari#include <dev/mii/miivar.h>
47216828Syongari#include "miidevs.h"
48216828Syongari
49216828Syongari#include <dev/mii/rdcphyreg.h>
50216828Syongari
51216828Syongari#include "miibus_if.h"
52216828Syongari
53216828Syongaristatic device_probe_t	rdcphy_probe;
54216828Syongaristatic device_attach_t	rdcphy_attach;
55216828Syongari
56216828Syongaristruct rdcphy_softc {
57216828Syongari	struct mii_softc mii_sc;
58216828Syongari	int mii_link_tick;
59216828Syongari#define	RDCPHY_MANNEG_TICK	3
60216828Syongari};
61216828Syongari
62216828Syongaristatic device_method_t rdcphy_methods[] = {
63216828Syongari	/* device interface */
64216828Syongari	DEVMETHOD(device_probe,		rdcphy_probe),
65216828Syongari	DEVMETHOD(device_attach,	rdcphy_attach),
66216828Syongari	DEVMETHOD(device_detach,	mii_phy_detach),
67216828Syongari	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
68227848Smarius	DEVMETHOD_END
69216828Syongari};
70216828Syongari
71216828Syongaristatic devclass_t rdcphy_devclass;
72216828Syongari
73216828Syongaristatic driver_t rdcphy_driver = {
74216828Syongari	"rdcphy",
75216828Syongari	rdcphy_methods,
76216828Syongari	sizeof(struct rdcphy_softc)
77216828Syongari};
78216828Syongari
79216828SyongariDRIVER_MODULE(rdcphy, miibus, rdcphy_driver, rdcphy_devclass, 0, 0);
80216828Syongari
81216828Syongaristatic int	rdcphy_service(struct mii_softc *, struct mii_data *, int);
82216828Syongaristatic void	rdcphy_status(struct mii_softc *);
83216828Syongari
84216828Syongaristatic const struct mii_phydesc rdcphys[] = {
85216828Syongari	MII_PHY_DESC(RDC, R6040),
86216828Syongari	MII_PHY_END
87216828Syongari};
88216828Syongari
89221407Smariusstatic const struct mii_phy_funcs rdcphy_funcs = {
90221407Smarius	rdcphy_service,
91221407Smarius	rdcphy_status,
92221407Smarius	mii_phy_reset
93221407Smarius};
94221407Smarius
95216828Syongaristatic int
96216828Syongarirdcphy_probe(device_t dev)
97216828Syongari{
98216828Syongari
99216828Syongari	return (mii_phy_dev_probe(dev, rdcphys, BUS_PROBE_DEFAULT));
100216828Syongari}
101216828Syongari
102216828Syongaristatic int
103216828Syongarirdcphy_attach(device_t dev)
104216828Syongari{
105216828Syongari
106221407Smarius	mii_phy_dev_attach(dev, MIIF_NOMANPAUSE, &rdcphy_funcs, 1);
107216828Syongari	return (0);
108216828Syongari}
109216828Syongari
110216828Syongaristatic int
111216828Syongarirdcphy_service(struct mii_softc *sc, struct mii_data *mii, int cmd)
112216828Syongari{
113216828Syongari	struct rdcphy_softc *rsc;
114216828Syongari	struct ifmedia_entry *ife;
115216828Syongari
116216828Syongari	rsc = (struct rdcphy_softc *)sc;
117216828Syongari	ife = mii->mii_media.ifm_cur;
118216828Syongari
119216828Syongari	switch (cmd) {
120216828Syongari	case MII_POLLSTAT:
121216828Syongari		break;
122216828Syongari
123216828Syongari	case MII_MEDIACHG:
124216828Syongari		mii_phy_setmedia(sc);
125216828Syongari		switch (IFM_SUBTYPE(ife->ifm_media)) {
126216828Syongari		case IFM_100_TX:
127216828Syongari		case IFM_10_T:
128216828Syongari			/*
129216828Syongari			 * Report fake lost link event to parent
130216828Syongari			 * driver.  This will stop MAC of parent
131216828Syongari			 * driver and make it possible to reconfigure
132216828Syongari			 * MAC after completion of link establishment.
133216828Syongari			 * Note, the parent MAC seems to require
134216828Syongari			 * restarting MAC when underlying any PHY
135216828Syongari			 * configuration was changed even if the
136216828Syongari			 * resolved speed/duplex was not changed at
137216828Syongari			 * all.
138216828Syongari			 */
139216828Syongari			mii->mii_media_status = 0;
140216828Syongari			mii->mii_media_active = IFM_ETHER | IFM_NONE;
141216828Syongari			rsc->mii_link_tick = RDCPHY_MANNEG_TICK;
142216828Syongari			/* Immediately report link down. */
143216828Syongari			mii_phy_update(sc, MII_MEDIACHG);
144216828Syongari			return (0);
145216828Syongari		default:
146216828Syongari			break;
147216828Syongari		}
148216828Syongari		break;
149216828Syongari
150216828Syongari	case MII_TICK:
151216828Syongari		if (mii_phy_tick(sc) == EJUSTRETURN)
152216828Syongari			return (0);
153216828Syongari		if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO) {
154216828Syongari			/*
155216828Syongari			 * It seems the PHY hardware does not correctly
156216828Syongari			 * report link status changes when manual link
157216828Syongari			 * configuration is in progress.  It is also
158216828Syongari			 * possible for the PHY to complete establishing
159216828Syongari			 * a link within one second such that mii(4)
160216828Syongari			 * did not notice the link change.  To workaround
161216828Syongari			 * the issue, emulate lost link event and wait
162216828Syongari			 * for 3 seconds when manual link configuration
163216828Syongari			 * is in progress.  3 seconds would be long
164216828Syongari			 * enough to absorb transient link flips.
165216828Syongari			 */
166216828Syongari			if (rsc->mii_link_tick > 0) {
167216828Syongari				rsc->mii_link_tick--;
168216828Syongari				return (0);
169216828Syongari			}
170216828Syongari		}
171216828Syongari		break;
172216828Syongari	}
173216828Syongari
174216828Syongari	/* Update the media status. */
175221407Smarius	PHY_STATUS(sc);
176216828Syongari
177216828Syongari	/* Callback if something changed. */
178216828Syongari	mii_phy_update(sc, cmd);
179216828Syongari	return (0);
180216828Syongari}
181216828Syongari
182216828Syongaristatic void
183216828Syongarirdcphy_status(struct mii_softc *sc)
184216828Syongari{
185216828Syongari	struct mii_data *mii;
186216828Syongari	struct ifmedia_entry *ife;
187216828Syongari	int bmsr, bmcr, physts;
188216828Syongari
189216828Syongari	mii = sc->mii_pdata;
190216828Syongari	ife = mii->mii_media.ifm_cur;
191216828Syongari
192216828Syongari	mii->mii_media_status = IFM_AVALID;
193216828Syongari	mii->mii_media_active = IFM_ETHER;
194216828Syongari
195216828Syongari	bmsr = PHY_READ(sc, MII_BMSR) | PHY_READ(sc, MII_BMSR);
196216828Syongari	physts = PHY_READ(sc, MII_RDCPHY_STATUS);
197216828Syongari
198216828Syongari	if ((physts & STATUS_LINK_UP) != 0)
199216828Syongari		mii->mii_media_status |= IFM_ACTIVE;
200216828Syongari
201216828Syongari	bmcr = PHY_READ(sc, MII_BMCR);
202216828Syongari	if ((bmcr & BMCR_ISO) != 0) {
203216828Syongari		mii->mii_media_active |= IFM_NONE;
204216828Syongari		mii->mii_media_status = 0;
205216828Syongari		return;
206216828Syongari	}
207216828Syongari
208216828Syongari	if ((bmcr & BMCR_LOOP) != 0)
209216828Syongari		mii->mii_media_active |= IFM_LOOP;
210216828Syongari
211216828Syongari	if ((bmcr & BMCR_AUTOEN) != 0) {
212216828Syongari		if ((bmsr & BMSR_ACOMP) == 0) {
213216828Syongari			/* Erg, still trying, I guess... */
214216828Syongari			mii->mii_media_active |= IFM_NONE;
215216828Syongari			return;
216216828Syongari		}
217216828Syongari	}
218216828Syongari
219216828Syongari	switch (physts & STATUS_SPEED_MASK) {
220216828Syongari	case STATUS_SPEED_100:
221216828Syongari		mii->mii_media_active |= IFM_100_TX;
222216828Syongari		break;
223216828Syongari	case STATUS_SPEED_10:
224216828Syongari		mii->mii_media_active |= IFM_10_T;
225216828Syongari		break;
226216828Syongari	default:
227216828Syongari		mii->mii_media_active |= IFM_NONE;
228216828Syongari		return;
229216828Syongari	}
230216828Syongari	if ((physts & STATUS_FULL_DUPLEX) != 0)
231216828Syongari		mii->mii_media_active |= IFM_FDX | mii_phy_flowstatus(sc);
232216828Syongari	else
233216828Syongari		mii->mii_media_active |= IFM_HDX;
234216828Syongari}
235