xref: /openbsd/sys/dev/mii/dcphy.c (revision fc61954a)
1 /*	$OpenBSD: dcphy.c,v 1.25 2013/12/28 03:30:41 deraadt Exp $	*/
2 
3 /*
4  * Copyright (c) 1997, 1998, 1999
5  *	Bill Paul <wpaul@ee.columbia.edu>.  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  * $FreeBSD: src/sys/dev/mii/dcphy.c,v 1.6 2000/10/05 17:36:14 wpaul Exp $
35  */
36 
37 /*
38  * Pseudo-driver for internal NWAY support on DEC 21143 and workalike
39  * controllers. Technically we're abusing the miibus code to handle
40  * media selection and NWAY support here since there is no MII
41  * interface. However the logical operations are roughly the same,
42  * and the alternative is to create a fake MII interface in the driver,
43  * which is harder to do.
44  */
45 
46 #include <sys/param.h>
47 #include <sys/systm.h>
48 #include <sys/kernel.h>
49 #include <sys/device.h>
50 #include <sys/socket.h>
51 #include <sys/errno.h>
52 
53 #include <machine/bus.h>
54 
55 #include <net/if.h>
56 #include <net/if_media.h>
57 
58 #include <netinet/in.h>
59 #include <netinet/if_ether.h>
60 
61 #include <dev/mii/mii.h>
62 #include <dev/mii/miivar.h>
63 #include <dev/mii/miidevs.h>
64 
65 #include <dev/ic/dcreg.h>
66 
67 #define DC_SETBIT(sc, reg, x)                           \
68         CSR_WRITE_4(sc, reg,                            \
69                 CSR_READ_4(sc, reg) | x)
70 
71 #define DC_CLRBIT(sc, reg, x)                           \
72         CSR_WRITE_4(sc, reg,                            \
73                 CSR_READ_4(sc, reg) & ~x)
74 
75 #define MIIF_AUTOTIMEOUT	0x0004
76 
77 /*
78  * This is the subsystem ID for the built-in 21143 ethernet
79  * in several Compaq Presario systems. Apparently these are
80  * 10Mbps only, so we need to treat them specially.
81  */
82 #define COMPAQ_PRESARIO_ID	0xb0bb0e11
83 
84 int	dcphy_match(struct device *, void *, void *);
85 void	dcphy_attach(struct device *, struct device *, void *);
86 
87 struct cfattach dcphy_ca = {
88 	sizeof(struct mii_softc), dcphy_match, dcphy_attach, mii_phy_detach
89 };
90 
91 struct cfdriver dcphy_cd = {
92 	NULL, "dcphy", DV_DULL
93 };
94 
95 int	dcphy_service(struct mii_softc *, struct mii_data *, int);
96 void	dcphy_status(struct mii_softc *);
97 int	dcphy_mii_phy_auto(struct mii_softc *, int);
98 void	dcphy_reset(struct mii_softc *);
99 
100 const struct mii_phy_funcs dcphy_funcs = {
101 	dcphy_service, dcphy_status, dcphy_reset,
102 };
103 
104 int
105 dcphy_match(struct device *parent, void *match, void *aux)
106 {
107 	struct mii_attach_args *ma = aux;
108 
109 	/*
110 	 * The dc driver will report the 21143 vendor and device
111 	 * ID to let us know that it wants us to attach.
112 	 */
113 	if (MII_OUI(ma->mii_id1, ma->mii_id2) == MII_OUI_xxDEC &&
114 	    MII_MODEL(ma->mii_id2) == MII_MODEL_xxDEC_xxDC)
115 		return (10);
116 
117 	return (0);
118 }
119 
120 void
121 dcphy_attach(struct device *parent, struct device *self, void *aux)
122 {
123 	struct mii_softc *sc = (struct mii_softc *)self;
124 	struct mii_attach_args *ma = aux;
125 	struct mii_data *mii = ma->mii_data;
126 	struct dc_softc *dc_sc;
127 
128 	printf(": internal PHY\n");
129 	sc->mii_inst = mii->mii_instance;
130 	sc->mii_phy = ma->mii_phyno;
131 	sc->mii_funcs = &dcphy_funcs;
132 	sc->mii_pdata = mii;
133 	sc->mii_flags = ma->mii_flags;
134 	sc->mii_anegticks = 50;
135 
136 	sc->mii_flags |= MIIF_NOISOLATE;
137 
138 	dc_sc = mii->mii_ifp->if_softc;
139 	CSR_WRITE_4(dc_sc, DC_10BTSTAT, 0);
140 	CSR_WRITE_4(dc_sc, DC_10BTCTRL, 0);
141 
142 #define	ADD(m, c)	ifmedia_add(&mii->mii_media, (m), (c), NULL)
143 
144 	switch(dc_sc->dc_csid) {
145 	case COMPAQ_PRESARIO_ID:
146 		/* Example of how to only allow 10Mbps modes. */
147 		sc->mii_capabilities = BMSR_ANEG|BMSR_10TFDX|BMSR_10THDX;
148 		break;
149 	default:
150 		if (dc_sc->dc_pmode == DC_PMODE_SIA) {
151 			sc->mii_capabilities =
152 			    BMSR_ANEG|BMSR_10TFDX|BMSR_10THDX;
153 		} else {
154 			ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_TX, IFM_LOOP,
155 			    sc->mii_inst), BMCR_LOOP|BMCR_S100);
156 
157 			sc->mii_capabilities =
158 			    BMSR_ANEG|BMSR_100TXFDX|BMSR_100TXHDX|
159 			    BMSR_10TFDX|BMSR_10THDX;
160 		}
161 		break;
162 	}
163 
164 	if (dc_sc->dc_type == DC_TYPE_21145)
165 		sc->mii_capabilities = BMSR_10THDX;
166 
167 	sc->mii_capabilities &= ma->mii_capmask;
168 	if (sc->mii_capabilities & BMSR_MEDIAMASK)
169 		mii_phy_add_media(sc);
170 #undef ADD
171 }
172 
173 int
174 dcphy_service(struct mii_softc *sc, struct mii_data *mii, int cmd)
175 {
176 	struct dc_softc *dc_sc;
177 	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
178 	int reg;
179 	u_int32_t mode;
180 
181 	if ((sc->mii_dev.dv_flags & DVF_ACTIVE) == 0)
182 		return (ENXIO);
183 
184 	dc_sc = mii->mii_ifp->if_softc;
185 
186 	switch (cmd) {
187 	case MII_POLLSTAT:
188 		/*
189 		 * If we're not polling our PHY instance, just return.
190 		 */
191 		if (IFM_INST(ife->ifm_media) != sc->mii_inst)
192 			return (0);
193 		break;
194 
195 	case MII_MEDIACHG:
196 		/*
197 		 * If the media indicates a different PHY instance,
198 		 * isolate ourselves.
199 		 */
200 		if (IFM_INST(ife->ifm_media) != sc->mii_inst)
201 			return (0);
202 
203 		/*
204 		 * If the interface is not up, don't do anything.
205 		 */
206 		if ((mii->mii_ifp->if_flags & IFF_UP) == 0)
207 			break;
208 
209 		sc->mii_flags = 0;
210 		mii->mii_media_active = IFM_NONE;
211 		mode = CSR_READ_4(dc_sc, DC_NETCFG);
212 		mode &= ~(DC_NETCFG_FULLDUPLEX|DC_NETCFG_PORTSEL|
213 		    DC_NETCFG_PCS|DC_NETCFG_SCRAMBLER|DC_NETCFG_SPEEDSEL);
214 
215 		switch (IFM_SUBTYPE(ife->ifm_media)) {
216 		case IFM_AUTO:
217 			/*PHY_RESET(sc);*/
218 			sc->mii_flags &= ~MIIF_DOINGAUTO;
219 			(void) dcphy_mii_phy_auto(sc, 0);
220 			break;
221 		case IFM_100_TX:
222 			PHY_RESET(sc);
223 			DC_CLRBIT(dc_sc, DC_10BTCTRL, DC_TCTL_AUTONEGENBL);
224 			mode |= DC_NETCFG_PORTSEL|DC_NETCFG_PCS|
225 			    DC_NETCFG_SCRAMBLER;
226 			if ((ife->ifm_media & IFM_GMASK) == IFM_FDX)
227 				mode |= DC_NETCFG_FULLDUPLEX;
228 			else
229 				mode &= ~DC_NETCFG_FULLDUPLEX;
230 			CSR_WRITE_4(dc_sc, DC_NETCFG, mode);
231 			break;
232 		case IFM_10_T:
233 			DC_CLRBIT(dc_sc, DC_SIARESET, DC_SIA_RESET);
234 			DC_CLRBIT(dc_sc, DC_10BTCTRL, 0xFFFF);
235 			if ((ife->ifm_media & IFM_GMASK) == IFM_FDX)
236 				DC_SETBIT(dc_sc, DC_10BTCTRL, 0x7F3D);
237 			else
238 				DC_SETBIT(dc_sc, DC_10BTCTRL, 0x7F3F);
239 			DC_SETBIT(dc_sc, DC_SIARESET, DC_SIA_RESET);
240 			DC_CLRBIT(dc_sc, DC_10BTCTRL, DC_TCTL_AUTONEGENBL);
241 			mode &= ~DC_NETCFG_PORTSEL;
242 			mode |= DC_NETCFG_SPEEDSEL;
243 			if ((ife->ifm_media & IFM_GMASK) == IFM_FDX)
244 				mode |= DC_NETCFG_FULLDUPLEX;
245 			else
246 				mode &= ~DC_NETCFG_FULLDUPLEX;
247 			CSR_WRITE_4(dc_sc, DC_NETCFG, mode);
248 			break;
249 		default:
250 			return (EINVAL);
251 		}
252 		break;
253 
254 	case MII_TICK:
255 		/*
256 		 * If we're not currently selected, just return.
257 		 */
258 		if (IFM_INST(ife->ifm_media) != sc->mii_inst)
259 			return (0);
260 
261 		/*
262 		 * Is the interface even up?
263 		 */
264 		if ((mii->mii_ifp->if_flags & IFF_UP) == 0)
265 			return (0);
266 
267 		/*
268 		 * Only used for autonegotiation.
269 		 */
270 		if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO)
271 			break;
272 
273 		reg = CSR_READ_4(dc_sc, DC_10BTSTAT);
274 		if (!(reg & DC_TSTAT_LS10) || !(reg & DC_TSTAT_LS100)) {
275 			sc->mii_ticks = 0;
276 			break;
277 		}
278 
279 		/*
280 		 * Only retry autonegotiation every mii_anegticks seconds.
281 		 *
282 		 * Otherwise, fall through to calling dcphy_status()
283 		 * since real Intel 21143 chips don't show valid link
284 		 * status until autonegotiation is switched off, and
285 		 * that only happens in dcphy_status().  Without this,
286 		 * successful autonegotiation is never recognised on
287 		 * these chips.
288 		 */
289 		if (++sc->mii_ticks <= sc->mii_anegticks)
290 			break;
291 
292 		sc->mii_ticks = 0;
293 		sc->mii_flags &= ~MIIF_DOINGAUTO;
294 		dcphy_mii_phy_auto(sc, 0);
295 
296 		break;
297 	}
298 
299 	/* Update the media status. */
300 	mii_phy_status(sc);
301 
302 	/* Callback if something changed. */
303 	mii_phy_update(sc, cmd);
304 	return (0);
305 }
306 
307 void
308 dcphy_status(struct mii_softc *sc)
309 {
310 	struct mii_data *mii = sc->mii_pdata;
311 	int reg, anlpar, tstat = 0;
312 	struct dc_softc *dc_sc;
313 
314 	dc_sc = mii->mii_ifp->if_softc;
315 
316 	mii->mii_media_status = IFM_AVALID;
317 	mii->mii_media_active = IFM_ETHER;
318 
319 	reg = CSR_READ_4(dc_sc, DC_10BTSTAT);
320 	if (!(reg & DC_TSTAT_LS10) || !(reg & DC_TSTAT_LS100))
321 		mii->mii_media_status |= IFM_ACTIVE;
322 
323 	if (CSR_READ_4(dc_sc, DC_10BTCTRL) & DC_TCTL_AUTONEGENBL) {
324 		/* Erg, still trying, I guess... */
325 		tstat = CSR_READ_4(dc_sc, DC_10BTSTAT);
326 		if ((tstat & DC_TSTAT_ANEGSTAT) != DC_ASTAT_AUTONEGCMP) {
327 			if ((DC_IS_MACRONIX(dc_sc) || DC_IS_PNICII(dc_sc)) &&
328 			    (tstat & DC_TSTAT_ANEGSTAT) == DC_ASTAT_DISABLE)
329 				goto skip;
330 			mii->mii_media_active |= IFM_NONE;
331 			return;
332 		}
333 
334 		if (tstat & DC_TSTAT_LP_CAN_NWAY) {
335 			anlpar = tstat >> 16;
336 			if (anlpar & ANLPAR_TX_FD &&
337 			    sc->mii_capabilities & BMSR_100TXFDX)
338 				mii->mii_media_active |= IFM_100_TX|IFM_FDX;
339 			else if (anlpar & ANLPAR_T4 &&
340 			    sc->mii_capabilities & BMSR_100T4)
341 				mii->mii_media_active |= IFM_100_T4|IFM_HDX;
342 			else if (anlpar & ANLPAR_TX &&
343 			    sc->mii_capabilities & BMSR_100TXHDX)
344 				mii->mii_media_active |= IFM_100_TX|IFM_HDX;
345 			else if (anlpar & ANLPAR_10_FD)
346 				mii->mii_media_active |= IFM_10_T|IFM_FDX;
347 			else if (anlpar & ANLPAR_10)
348 				mii->mii_media_active |= IFM_10_T|IFM_HDX;
349 			else
350 				mii->mii_media_active |= IFM_NONE;
351 			if (DC_IS_INTEL(dc_sc))
352 				DC_CLRBIT(dc_sc, DC_10BTCTRL,
353 				    DC_TCTL_AUTONEGENBL);
354 			return;
355 		}
356 
357 		/*
358 		 * If the other side doesn't support NWAY, then the
359 		 * best we can do is determine if we have a 10Mbps or
360 		 * 100Mbps link. There's no way to know if the link
361 		 * is full or half duplex, so we default to half duplex
362 		 * and hope that the user is clever enough to manually
363 		 * change the media settings if we're wrong.
364 		 */
365 		if (!(reg & DC_TSTAT_LS100))
366 			mii->mii_media_active |= IFM_100_TX|IFM_HDX;
367 		else if (!(reg & DC_TSTAT_LS10))
368 			mii->mii_media_active |= IFM_10_T|IFM_HDX;
369 		else
370 			mii->mii_media_active |= IFM_NONE;
371 		if (DC_IS_INTEL(dc_sc))
372 			DC_CLRBIT(dc_sc, DC_10BTCTRL, DC_TCTL_AUTONEGENBL);
373 		return;
374 	}
375 
376 skip:
377 	if (CSR_READ_4(dc_sc, DC_NETCFG) & DC_NETCFG_SPEEDSEL)
378 		mii->mii_media_active |= IFM_10_T;
379 	else
380 		mii->mii_media_active |= IFM_100_TX;
381 
382 	if (CSR_READ_4(dc_sc, DC_NETCFG) & DC_NETCFG_FULLDUPLEX)
383 		mii->mii_media_active |= IFM_FDX;
384 	else
385 		mii->mii_media_active |= IFM_HDX;
386 }
387 
388 int
389 dcphy_mii_phy_auto(struct mii_softc *mii, int waitfor)
390 {
391 	int			i;
392 	struct dc_softc		*sc;
393 
394 	sc = mii->mii_pdata->mii_ifp->if_softc;
395 
396 	if ((mii->mii_flags & MIIF_DOINGAUTO) == 0) {
397 		DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_PORTSEL);
398 		DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_FULLDUPLEX);
399 		DC_CLRBIT(sc, DC_SIARESET, DC_SIA_RESET);
400 		if (mii->mii_capabilities & BMSR_100TXHDX)
401 			CSR_WRITE_4(sc, DC_10BTCTRL, 0x3FFFF);
402 		else
403 			CSR_WRITE_4(sc, DC_10BTCTRL, 0xFFFF);
404 		DC_SETBIT(sc, DC_SIARESET, DC_SIA_RESET);
405 		DC_SETBIT(sc, DC_10BTCTRL, DC_TCTL_AUTONEGENBL);
406 		DC_SETBIT(sc, DC_10BTSTAT, DC_ASTAT_TXDISABLE);
407 	}
408 
409 	if (waitfor) {
410 		/* Wait 500ms for it to complete. */
411 		for (i = 0; i < 500; i++) {
412 			if ((CSR_READ_4(sc, DC_10BTSTAT) & DC_TSTAT_ANEGSTAT)
413 			    == DC_ASTAT_AUTONEGCMP)
414 				return (0);
415 			DELAY(1000);
416 		}
417 		/*
418 		 * Don't need to worry about clearing MIIF_DOINGAUTO.
419 		 * If that's set, a timeout is pending, and it will
420 		 * clear the flag.
421 		 */
422 		return (EIO);
423 	}
424 
425 	/*
426 	 * Just let it finish asynchronously.  This is for the benefit of
427 	 * the tick handler driving autonegotiation.  Don't want 500ms
428 	 * delays all the time while the system is running!
429 	 */
430 	if ((mii->mii_flags & MIIF_DOINGAUTO) == 0)
431 		mii->mii_flags |= MIIF_DOINGAUTO;
432 
433 	return (EJUSTRETURN);
434 }
435 
436 void
437 dcphy_reset(struct mii_softc *mii)
438 {
439 	struct dc_softc		*sc;
440 
441 	sc = mii->mii_pdata->mii_ifp->if_softc;
442 
443 	DC_CLRBIT(sc, DC_SIARESET, DC_SIA_RESET);
444 	DELAY(1000);
445 	DC_SETBIT(sc, DC_SIARESET, DC_SIA_RESET);
446 
447 	return;
448 }
449