smscphy.c revision 331722
1/*-
2 * Copyright (c) 2006 Benno Rice.  All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
14 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
15 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
16 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
17 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
18 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
19 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
20 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
22 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23 */
24
25#include <sys/cdefs.h>
26__FBSDID("$FreeBSD: stable/11/sys/dev/mii/smscphy.c 331722 2018-03-29 02:50:57Z eadler $");
27
28/*
29 * Driver for the SMSC LAN8710A
30 */
31
32#include <sys/param.h>
33#include <sys/systm.h>
34#include <sys/kernel.h>
35#include <sys/socket.h>
36#include <sys/errno.h>
37#include <sys/module.h>
38#include <sys/bus.h>
39#include <sys/malloc.h>
40
41#include <machine/bus.h>
42
43#include <net/if.h>
44#include <net/if_media.h>
45
46#include <dev/mii/mii.h>
47#include <dev/mii/miivar.h>
48#include "miidevs.h"
49
50#include "miibus_if.h"
51
52static int	smscphy_probe(device_t);
53static int	smscphy_attach(device_t);
54
55static int	smscphy_service(struct mii_softc *, struct mii_data *, int);
56static void	smscphy_auto(struct mii_softc *, int);
57static void	smscphy_status(struct mii_softc *);
58
59static device_method_t smscphy_methods[] = {
60	/* device interface */
61	DEVMETHOD(device_probe,		smscphy_probe),
62	DEVMETHOD(device_attach,	smscphy_attach),
63	DEVMETHOD(device_detach,	mii_phy_detach),
64	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
65	DEVMETHOD_END
66};
67
68static devclass_t smscphy_devclass;
69
70static driver_t smscphy_driver = {
71	"smscphy",
72	smscphy_methods,
73	sizeof(struct mii_softc)
74};
75
76DRIVER_MODULE(smscphy, miibus, smscphy_driver, smscphy_devclass, 0, 0);
77
78static const struct mii_phydesc smscphys[] = {
79	MII_PHY_DESC(SMC, LAN8710A),
80	MII_PHY_DESC(SMC, LAN8700),
81	MII_PHY_END
82};
83
84static const struct mii_phy_funcs smscphy_funcs = {
85	smscphy_service,
86	smscphy_status,
87	mii_phy_reset
88};
89
90static int
91smscphy_probe(device_t dev)
92{
93
94	return (mii_phy_dev_probe(dev, smscphys, BUS_PROBE_DEFAULT));
95}
96
97static int
98smscphy_attach(device_t dev)
99{
100	struct mii_softc *sc;
101	const struct mii_phy_funcs *mpf;
102
103	sc = device_get_softc(dev);
104	mpf = &smscphy_funcs;
105	mii_phy_dev_attach(dev, MIIF_NOISOLATE | MIIF_NOMANPAUSE, mpf, 1);
106	mii_phy_setmedia(sc);
107
108	return (0);
109}
110
111static int
112smscphy_service(struct mii_softc *sc, struct mii_data *mii, int cmd)
113{
114        struct	ifmedia_entry *ife;
115        int	reg;
116
117	ife = mii->mii_media.ifm_cur;
118
119        switch (cmd) {
120        case MII_POLLSTAT:
121                break;
122
123        case MII_MEDIACHG:
124		switch (IFM_SUBTYPE(ife->ifm_media)) {
125		case IFM_AUTO:
126			smscphy_auto(sc, ife->ifm_media);
127			break;
128
129		default:
130                	mii_phy_setmedia(sc);
131			break;
132		}
133
134                break;
135
136        case MII_TICK:
137		if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO) {
138			break;
139		}
140
141		/* I have no idea why BMCR_ISO gets set. */
142		reg = PHY_READ(sc, MII_BMCR);
143		if (reg & BMCR_ISO) {
144			PHY_WRITE(sc, MII_BMCR, reg & ~BMCR_ISO);
145		}
146
147		reg = PHY_READ(sc, MII_BMSR) | PHY_READ(sc, MII_BMSR);
148		if (reg & BMSR_LINK) {
149			sc->mii_ticks = 0;
150			break;
151		}
152
153		if (++sc->mii_ticks <= MII_ANEGTICKS) {
154			break;
155		}
156
157		sc->mii_ticks = 0;
158		PHY_RESET(sc);
159		smscphy_auto(sc, ife->ifm_media);
160                break;
161        }
162
163        /* Update the media status. */
164        PHY_STATUS(sc);
165
166        /* Callback if something changed. */
167        mii_phy_update(sc, cmd);
168        return (0);
169}
170
171static void
172smscphy_auto(struct mii_softc *sc, int media)
173{
174	uint16_t	anar;
175
176	anar = BMSR_MEDIA_TO_ANAR(sc->mii_capabilities) | ANAR_CSMA;
177	if ((media & IFM_FLOW) != 0 || (sc->mii_flags & MIIF_FORCEPAUSE) != 0)
178		anar |= ANAR_FC;
179	PHY_WRITE(sc, MII_ANAR, anar);
180	/* Apparently this helps. */
181	anar = PHY_READ(sc, MII_ANAR);
182	PHY_WRITE(sc, MII_BMCR, BMCR_AUTOEN | BMCR_STARTNEG);
183}
184
185static void
186smscphy_status(struct mii_softc *sc)
187{
188	struct mii_data *mii;
189	uint32_t bmcr, bmsr, status;
190
191	mii = sc->mii_pdata;
192	mii->mii_media_status = IFM_AVALID;
193	mii->mii_media_active = IFM_ETHER;
194
195	bmsr = PHY_READ(sc, MII_BMSR) | PHY_READ(sc, MII_BMSR);
196	if ((bmsr & BMSR_LINK) != 0)
197		mii->mii_media_status |= IFM_ACTIVE;
198
199	bmcr = PHY_READ(sc, MII_BMCR);
200	if ((bmcr & BMCR_ISO) != 0) {
201		mii->mii_media_active |= IFM_NONE;
202		mii->mii_media_status = 0;
203		return;
204	}
205
206	if ((bmcr & BMCR_LOOP) != 0)
207		mii->mii_media_active |= IFM_LOOP;
208
209	if ((bmcr & BMCR_AUTOEN) != 0) {
210		if ((bmsr & BMSR_ACOMP) == 0) {
211			/* Erg, still trying, I guess... */
212			mii->mii_media_active |= IFM_NONE;
213			return;
214		}
215	}
216
217	status = PHY_READ(sc, 0x1F);
218	if (status & 0x0008)
219		mii->mii_media_active |= IFM_100_TX;
220	else
221		mii->mii_media_active |= IFM_10_T;
222	if (status & 0x0010)
223		mii->mii_media_active |= IFM_FDX | mii_phy_flowstatus(sc);
224	else
225		mii->mii_media_active |= IFM_HDX;
226}
227