1 /*	$OpenBSD: nsgphy.c,v 1.16 2005/05/27 09:24:01 brad Exp $	*/
2 /*
3  * Copyright (c) 2001 Wind River Systems
4  * Copyright (c) 2001
5  *	Bill Paul <wpaul@bsdi.com>.  All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. All advertising materials mentioning features or use of this software
16  *    must display the following acknowledgement:
17  *	This product includes software developed by Bill Paul.
18  * 4. Neither the name of the author nor the names of any co-contributors
19  *    may be used to endorse or promote products derived from this software
20  *    without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
23  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25  * ARE DISCLAIMED.  IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD
26  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
32  * THE POSSIBILITY OF SUCH DAMAGE.
33  *
34  */
35 
36 /*
37  * Driver for the National Semiconductor DP83891 and DP83861
38  * 10/100/1000 PHYs.
39  * Datasheet available at: http://www.national.com/ds/DP/DP83861.pdf
40  *
41  * The DP83891 is the older NatSemi gigE PHY which isn't being sold
42  * anymore. The DP83861 is its replacement, which is an 'enhanced'
43  * firmware driven component. The major difference between the
44  * two is that the 83891 can't generate interrupts, while the
45  * 83861 can. (I think it wasn't originally designed to do this, but
46  * it can now thanks to firmware updates.) The 83861 also allows
47  * access to its internal RAM via indirect register access.
48  */
49 
50 #include <sys/param.h>
51 #include <sys/systm.h>
52 #include <sys/kernel.h>
53 #include <sys/device.h>
54 #include <sys/socket.h>
55 
56 #include <net/if.h>
57 #include <net/if_media.h>
58 
59 #include <dev/mii/mii.h>
60 #include <dev/mii/miivar.h>
61 #include <dev/mii/miidevs.h>
62 
63 #include <dev/mii/nsgphyreg.h>
64 
65 int	nsgphymatch(struct device*, void *, void *);
66 void	nsgphyattach(struct device *, struct device *, void *);
67 
68 struct cfattach nsgphy_ca = {
69 	sizeof(struct mii_softc), nsgphymatch, nsgphyattach, mii_phy_detach,
70 	mii_phy_activate
71 };
72 
73 struct cfdriver nsgphy_cd = {
74 	NULL, "nsgphy", DV_DULL
75 };
76 
77 int	nsgphy_service(struct mii_softc *, struct mii_data *, int);
78 void	nsgphy_status(struct mii_softc *);
79 
80 const struct mii_phy_funcs nsgphy_funcs = {
81 	nsgphy_service, nsgphy_status, mii_phy_reset,
82 };
83 
84 static const struct mii_phydesc nsgphys[] = {
85 	{ MII_OUI_NATSEMI,		MII_MODEL_NATSEMI_DP83861,
86 	  MII_STR_NATSEMI_DP83861 },
87 	{ MII_OUI_NATSEMI,		MII_MODEL_NATSEMI_DP83891,
88 	  MII_STR_NATSEMI_DP83891 },
89 
90 	{ 0,			0,
91 	  NULL },
92 };
93 
94 int
nsgphymatch(struct device * parent,void * match,void * aux)95 nsgphymatch(struct device *parent, void *match, void *aux)
96 {
97 	struct mii_attach_args *ma = aux;
98 
99 	if (mii_phy_match(ma, nsgphys) != NULL)
100 		return (10);
101 
102 	return (0);
103 }
104 
105 void
nsgphyattach(struct device * parent,struct device * self,void * aux)106 nsgphyattach(struct device *parent, struct device *self, void *aux)
107 {
108 	struct mii_softc *sc = (struct mii_softc *)self;
109 	struct mii_attach_args *ma = aux;
110 	struct mii_data *mii = ma->mii_data;
111 	const struct mii_phydesc *mpd;
112 
113 	mpd = mii_phy_match(ma, nsgphys);
114 	printf(": %s, rev. %d\n", mpd->mpd_name, MII_REV(ma->mii_id2));
115 
116 	sc->mii_inst = mii->mii_instance;
117 	sc->mii_phy = ma->mii_phyno;
118 	sc->mii_funcs = &nsgphy_funcs;
119 	sc->mii_pdata = mii;
120 	sc->mii_flags = ma->mii_flags;
121 	sc->mii_anegticks = MII_ANEGTICKS;
122 
123 	sc->mii_capabilities =
124 		PHY_READ(sc, MII_BMSR) & ma->mii_capmask;
125         if (sc->mii_capabilities & BMSR_EXTSTAT)
126 		sc->mii_extcapabilities = PHY_READ(sc, MII_EXTSR);
127         if ((sc->mii_capabilities & BMSR_MEDIAMASK) ||
128             (sc->mii_extcapabilities & EXTSR_MEDIAMASK))
129                 mii_phy_add_media(sc);
130 }
131 
132 int
nsgphy_service(struct mii_softc * sc,struct mii_data * mii,int cmd)133 nsgphy_service(struct mii_softc *sc, struct mii_data *mii, int cmd)
134 {
135 	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
136 	int reg;
137 
138 	if ((sc->mii_dev.dv_flags & DVF_ACTIVE) == 0)
139 		return (ENXIO);
140 
141 	switch (cmd) {
142 	case MII_POLLSTAT:
143 		/*
144 		 * If we're not polling our PHY instance, just return.
145 		 */
146 		if (IFM_INST(ife->ifm_media) != sc->mii_inst)
147 			return (0);
148 		break;
149 
150 	case MII_MEDIACHG:
151 		/*
152 		 * If the media indicates a different PHY instance,
153 		 * isolate ourselves.
154 		 */
155 		if (IFM_INST(ife->ifm_media) != sc->mii_inst) {
156 			reg = PHY_READ(sc, MII_BMCR);
157 			PHY_WRITE(sc, MII_BMCR, reg | BMCR_ISO);
158 			return (0);
159 		}
160 
161 		/*
162 		 * If the interface is not up, don't do anything.
163 		 */
164 		if ((mii->mii_ifp->if_flags & IFF_UP) == 0)
165 			break;
166 
167 		mii_phy_setmedia(sc);
168 		break;
169 
170 	case MII_TICK:
171 		/*
172 		 * If we're not currently selected, just return.
173 		 */
174 		if (IFM_INST(ife->ifm_media) != sc->mii_inst)
175 			return (0);
176 
177 		if (mii_phy_tick(sc) == EJUSTRETURN)
178 			return (0);
179 		break;
180 	case MII_DOWN:
181 		mii_phy_down(sc);
182 		return (0);
183 	}
184 
185 	/* Update the media status. */
186 	mii_phy_status(sc);
187 
188 	/* Callback if something changed. */
189 	mii_phy_update(sc, cmd);
190 	return (0);
191 }
192 
193 void
nsgphy_status(struct mii_softc * sc)194 nsgphy_status(struct mii_softc *sc)
195 {
196 	struct mii_data *mii = sc->mii_pdata;
197 	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
198 	int bmsr, bmcr, physup, gtsr;
199 
200 	mii->mii_media_status = IFM_AVALID;
201 	mii->mii_media_active = IFM_ETHER;
202 
203 	bmsr = PHY_READ(sc, MII_BMSR) | PHY_READ(sc, MII_BMSR);
204 
205 	physup = PHY_READ(sc, NSGPHY_MII_PHYSUP);
206 
207 	if (physup & PHY_SUP_LINK)
208 		mii->mii_media_status |= IFM_ACTIVE;
209 
210 	bmcr = PHY_READ(sc, MII_BMCR);
211 	if (bmcr & BMCR_ISO) {
212 		mii->mii_media_active |= IFM_NONE;
213 		mii->mii_media_status = 0;
214 		return;
215 	}
216 
217 	if (bmcr & BMCR_LOOP)
218 		mii->mii_media_active |= IFM_LOOP;
219 
220 	if (bmcr & BMCR_AUTOEN) {
221 		if ((bmsr & BMSR_ACOMP) == 0) {
222 			/* Erg, still trying, I guess... */
223 			mii->mii_media_active |= IFM_NONE;
224 			return;
225 		}
226 
227 		switch (physup & (PHY_SUP_SPEED1|PHY_SUP_SPEED0)) {
228 		case PHY_SUP_SPEED1:
229 			mii->mii_media_active |= IFM_1000_T;
230 			gtsr = PHY_READ(sc, MII_100T2SR);
231 			if (gtsr & GTSR_MS_RES)
232 				mii->mii_media_active |= IFM_ETH_MASTER;
233 			break;
234 
235 		case PHY_SUP_SPEED0:
236 			mii->mii_media_active |= IFM_100_TX;
237 			break;
238 
239 		case 0:
240 			mii->mii_media_active |= IFM_10_T;
241 			break;
242 
243 		default:
244 			mii->mii_media_active |= IFM_NONE;
245 			mii->mii_media_status = 0;
246 		}
247 		if (physup & PHY_SUP_DUPLEX)
248 			mii->mii_media_active |= IFM_FDX;
249 	} else
250 		mii->mii_media_active = ife->ifm_media;
251 }
252