smcphy.c revision 214262
124139Sjoerg/*-
224139Sjoerg * Copyright (c) 2006 Benno Rice.  All rights reserved.
324139Sjoerg *
489750Sdwmalone * Redistribution and use in source and binary forms, with or without
589750Sdwmalone * modification, are permitted provided that the following conditions
624139Sjoerg * are met:
724139Sjoerg * 1. Redistributions of source code must retain the above copyright
824139Sjoerg *    notice, this list of conditions and the following disclaimer.
924139Sjoerg * 2. Redistributions in binary form must reproduce the above copyright
1024139Sjoerg *    notice, this list of conditions and the following disclaimer in the
1189750Sdwmalone *    documentation and/or other materials provided with the distribution.
1289750Sdwmalone *
1389750Sdwmalone * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1489750Sdwmalone * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1589750Sdwmalone * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1689750Sdwmalone * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
1789750Sdwmalone * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
1889750Sdwmalone * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
1924139Sjoerg * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2089750Sdwmalone * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2189750Sdwmalone * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2289750Sdwmalone * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2389750Sdwmalone */
2489750Sdwmalone
2589750Sdwmalone#include <sys/cdefs.h>
2689750Sdwmalone__FBSDID("$FreeBSD: head/sys/dev/mii/smcphy.c 214262 2010-10-24 11:37:01Z marius $");
2789750Sdwmalone
2889750Sdwmalone/*
2989750Sdwmalone * Driver for the internal PHY on the SMSC LAN91C111.
3024139Sjoerg */
3124139Sjoerg
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	smcphy_probe(device_t);
53static int	smcphy_attach(device_t);
54
55static int	smcphy_service(struct mii_softc *, struct mii_data *, int);
56static int	smcphy_reset(struct mii_softc *);
57static void	smcphy_auto(struct mii_softc *);
58
59static device_method_t smcphy_methods[] = {
60	/* device interface */
61	DEVMETHOD(device_probe,		smcphy_probe),
62	DEVMETHOD(device_attach,	smcphy_attach),
63	DEVMETHOD(device_detach,	mii_phy_detach),
64	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
65
66	{ 0, 0 }
67};
68
69static devclass_t smcphy_devclass;
70
71static driver_t smcphy_driver = {
72	"smcphy",
73	smcphy_methods,
74	sizeof(struct mii_softc)
75};
76
77DRIVER_MODULE(smcphy, miibus, smcphy_driver, smcphy_devclass, 0, 0);
78
79static const struct mii_phydesc smcphys[] = {
80	MII_PHY_DESC(SMSC, LAN83C183),
81	MII_PHY_END
82};
83
84static int
85smcphy_probe(device_t dev)
86{
87
88	return (mii_phy_dev_probe(dev, smcphys, BUS_PROBE_DEFAULT));
89}
90
91static int
92smcphy_attach(device_t dev)
93{
94	struct	mii_softc *sc;
95	struct	mii_attach_args *ma;
96	struct	mii_data *mii;
97
98	sc = device_get_softc(dev);
99	ma = device_get_ivars(dev);
100	sc->mii_dev = device_get_parent(dev);
101	mii = ma->mii_data;
102	LIST_INSERT_HEAD(&mii->mii_phys, sc, mii_list);
103
104	sc->mii_flags = miibus_get_flags(dev);
105	sc->mii_inst = mii->mii_instance++;
106	sc->mii_phy = ma->mii_phyno;
107	sc->mii_service = smcphy_service;
108	sc->mii_pdata = mii;
109
110	sc->mii_flags |= MIIF_NOISOLATE | MIIF_NOLOOP;
111
112	if (smcphy_reset(sc) != 0) {
113		device_printf(dev, "reset failed\n");
114	}
115
116	/* Mask interrupts, we poll instead. */
117	PHY_WRITE(sc, 0x1e, 0xffc0);
118
119	sc->mii_capabilities = PHY_READ(sc, MII_BMSR) & ma->mii_capmask;
120	device_printf(dev, " ");
121	mii_phy_add_media(sc);
122	printf("\n");
123
124	MIIBUS_MEDIAINIT(sc->mii_dev);
125	mii_phy_setmedia(sc);
126
127	return (0);
128}
129
130static int
131smcphy_service(struct mii_softc *sc, struct mii_data *mii, int cmd)
132{
133        struct	ifmedia_entry *ife;
134        int	reg;
135
136	ife = mii->mii_media.ifm_cur;
137
138        switch (cmd) {
139        case MII_POLLSTAT:
140                break;
141
142        case MII_MEDIACHG:
143                /*
144                 * If the interface is not up, don't do anything.
145                 */
146                if ((mii->mii_ifp->if_flags & IFF_UP) == 0)
147                        break;
148
149		switch (IFM_SUBTYPE(ife->ifm_media)) {
150		case IFM_AUTO:
151			smcphy_auto(sc);
152			break;
153
154		default:
155                	mii_phy_setmedia(sc);
156			break;
157		}
158
159                break;
160
161        case MII_TICK:
162		if ((mii->mii_ifp->if_flags & IFF_UP) == 0) {
163			return (0);
164		}
165
166		if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO) {
167			break;
168		}
169
170		/* I have no idea why BMCR_ISO gets set. */
171		reg = PHY_READ(sc, MII_BMCR);
172		if (reg & BMCR_ISO) {
173			PHY_WRITE(sc, MII_BMCR, reg & ~BMCR_ISO);
174		}
175
176		reg = PHY_READ(sc, MII_BMSR) | PHY_READ(sc, MII_BMSR);
177		if (reg & BMSR_LINK) {
178			sc->mii_ticks = 0;
179			break;
180		}
181
182		if (++sc->mii_ticks <= MII_ANEGTICKS) {
183			break;
184		}
185
186		sc->mii_ticks = 0;
187		if (smcphy_reset(sc) != 0) {
188			device_printf(sc->mii_dev, "reset failed\n");
189		}
190		smcphy_auto(sc);
191                break;
192        }
193
194        /* Update the media status. */
195        ukphy_status(sc);
196
197        /* Callback if something changed. */
198        mii_phy_update(sc, cmd);
199        return (0);
200}
201
202static int
203smcphy_reset(struct mii_softc *sc)
204{
205	u_int	bmcr;
206	int	timeout;
207
208	PHY_WRITE(sc, MII_BMCR, BMCR_RESET);
209
210	for (timeout = 2; timeout > 0; timeout--) {
211		DELAY(50000);
212		bmcr = PHY_READ(sc, MII_BMCR);
213		if ((bmcr & BMCR_RESET) == 0)
214			break;
215	}
216
217	if (bmcr & BMCR_RESET) {
218		return (EIO);
219	}
220
221	PHY_WRITE(sc, MII_BMCR, 0x3000);
222	return (0);
223}
224
225static void
226smcphy_auto(struct mii_softc *sc)
227{
228	uint16_t	anar;
229
230	anar = BMSR_MEDIA_TO_ANAR(sc->mii_capabilities) |
231	    ANAR_CSMA;
232	if (sc->mii_flags & MIIF_DOPAUSE)
233		anar |= ANAR_FC;
234	PHY_WRITE(sc, MII_ANAR, anar);
235	/* Apparently this helps. */
236	anar = PHY_READ(sc, MII_ANAR);
237	PHY_WRITE(sc, MII_BMCR, BMCR_AUTOEN | BMCR_STARTNEG);
238}
239