xref: /openbsd/sys/dev/mii/rdcphy.c (revision 898184e3)
1 /*	$OpenBSD: rdcphy.c,v 1.1 2011/01/15 04:35:34 kevlo Exp $	*/
2 /*-
3  * Copyright (c) 2010, Pyun YongHyeon <yongari@FreeBSD.org>
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice unmodified, this list of conditions, and the following
11  *    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  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28 
29 /*
30  * Driver for the RDC Semiconductor R6040 10/100 PHY.
31  */
32 
33 #include <sys/param.h>
34 #include <sys/systm.h>
35 #include <sys/kernel.h>
36 #include <sys/device.h>
37 #include <sys/socket.h>
38 
39 #include <net/if.h>
40 #include <net/if_media.h>
41 
42 #include <dev/mii/mii.h>
43 #include <dev/mii/miivar.h>
44 #include <dev/mii/miidevs.h>
45 
46 #define	MII_RDCPHY_DEBUG	0x11
47 #define	DEBUG_JABBER_DIS	0x0040
48 #define	DEBUG_LOOP_BACK_10MBPS	0x0400
49 
50 #define	MII_RDCPHY_CTRL		0x14
51 #define	CTRL_SQE_ENB		0x0100
52 #define	CTRL_NEG_POLARITY	0x0400
53 #define	CTRL_AUTO_POLARITY	0x0800
54 #define	CTRL_MDIXSEL_RX		0x2000
55 #define	CTRL_MDIXSEL_TX		0x4000
56 #define	CTRL_AUTO_MDIX_DIS	0x8000
57 
58 #define	MII_RDCPHY_CTRL2	0x15
59 #define	CTRL2_LED_DUPLEX	0x0000
60 #define	CTRL2_LED_DUPLEX_COL	0x0008
61 #define	CTRL2_LED_ACT		0x0010
62 #define	CTRL2_LED_SPEED_ACT	0x0018
63 #define	CTRL2_LED_BLK_100MBPS_DIS	0x0020
64 #define	CTRL2_LED_BLK_10MBPS_DIS	0x0040
65 #define	CTRL2_LED_BLK_LINK_ACT_DIS	0x0080
66 #define	CTRL2_SDT_THRESH_MASK	0x3E00
67 #define	CTRL2_TIMING_ERR_SEL	0x4000
68 #define	CTRL2_LED_BLK_80MS	0x8000
69 #define	CTRL2_LED_BLK_160MS	0x0000
70 #define	CTRL2_LED_MASK		0x0018
71 
72 #define	MII_RDCPHY_STATUS	0x16
73 #define	STATUS_AUTO_MDIX_RX	0x0200
74 #define	STATUS_AUTO_MDIX_TX	0x0400
75 #define	STATUS_NEG_POLARITY	0x0800
76 #define	STATUS_FULL_DUPLEX	0x1000
77 #define	STATUS_SPEED_10		0x0000
78 #define	STATUS_SPEED_100	0x2000
79 #define	STATUS_SPEED_MASK	0x6000
80 #define	STATUS_LINK_UP		0x8000
81 
82 /* Analog test register 2 */
83 #define	MII_RDCPHY_TEST2	0x1A
84 #define	TEST2_PWR_DOWN		0x0200
85 
86 struct rdcphy_softc {
87 	struct mii_softc sc_mii;
88 	int mii_link_tick;
89 #define	RDCPHY_MANNEG_TICK	3
90 };
91 
92 int	rdcphy_service(struct mii_softc *, struct mii_data *, int);
93 void	rdcphy_attach(struct device *, struct device *, void *);
94 int	rdcphy_match(struct device *, void *, void *);
95 void	rdcphy_status(struct mii_softc *);
96 
97 const struct mii_phy_funcs rdcphy_funcs = {
98 	rdcphy_service, rdcphy_status, mii_phy_reset,
99 };
100 
101 static const struct mii_phydesc rdcphys[] = {
102 	{ MII_OUI_RDC,		MII_MODEL_RDC_R6040,
103 	  MII_STR_RDC_R6040 },
104 	{ 0,			0,
105 	  NULL },
106 };
107 
108 struct cfattach rdcphy_ca = {
109 	sizeof(struct rdcphy_softc), rdcphy_match, rdcphy_attach,
110 	mii_phy_detach, mii_phy_activate
111 };
112 
113 struct cfdriver rdcphy_cd = {
114 	NULL, "rdcphy", DV_DULL
115 };
116 
117 int
118 rdcphy_match(struct device *parent, void *match, void *aux)
119 {
120 	struct mii_attach_args *ma = aux;
121 
122 	if (mii_phy_match(ma, rdcphys) != NULL)
123 		return (10);
124 
125 	return (0);
126 }
127 
128 void
129 rdcphy_attach(struct device *parent, struct device *self, void *aux)
130 {
131 	struct rdcphy_softc *sc = (struct rdcphy_softc *)self;
132 	struct mii_attach_args *ma = aux;
133 	struct mii_data *mii = ma->mii_data;
134 	const struct mii_phydesc *mpd;
135 
136 	mpd = mii_phy_match(ma, rdcphys);
137 	printf(": %s, rev. %d\n", mpd->mpd_name, MII_REV(ma->mii_id2));
138 
139 	sc->sc_mii.mii_inst = mii->mii_instance;
140 	sc->sc_mii.mii_phy = ma->mii_phyno;
141 	sc->sc_mii.mii_funcs = &rdcphy_funcs;
142 	sc->sc_mii.mii_pdata = mii;
143 	sc->sc_mii.mii_flags = ma->mii_flags;
144 
145 	PHY_RESET(&sc->sc_mii);
146 
147 	sc->sc_mii.mii_capabilities =
148 	    PHY_READ(&sc->sc_mii, MII_BMSR) & ma->mii_capmask;
149 	if (sc->sc_mii.mii_capabilities & BMSR_EXTSTAT)
150 		sc->sc_mii.mii_extcapabilities =
151 		    PHY_READ(&sc->sc_mii, MII_EXTSR);
152 
153 	if (sc->sc_mii.mii_capabilities & BMSR_MEDIAMASK)
154 		mii_phy_add_media(&sc->sc_mii);
155 }
156 
157 int
158 rdcphy_service(struct mii_softc *self, struct mii_data *mii, int cmd)
159 {
160 	struct rdcphy_softc *sc = (struct rdcphy_softc *)self;
161 	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
162 	int reg;
163 
164 	if ((sc->sc_mii.mii_dev.dv_flags & DVF_ACTIVE) == 0)
165 		return (ENXIO);
166 
167 	switch (cmd) {
168 	case MII_POLLSTAT:
169 		/*
170 		 * If we're not polling our PHY instance, just return.
171 		 */
172 		if (IFM_INST(ife->ifm_media) != sc->sc_mii.mii_inst)
173 			return (0);
174 		break;
175 
176 	case MII_MEDIACHG:
177 		/*
178 		 * If the media indicates a different PHY instance,
179 		 * isolate ourselves.
180 		 */
181 		if (IFM_INST(ife->ifm_media) != sc->sc_mii.mii_inst) {
182 			reg = PHY_READ(&sc->sc_mii, MII_BMCR);
183 			PHY_WRITE(&sc->sc_mii, MII_BMCR, reg | BMCR_ISO);
184 			return (0);
185 		}
186 
187 		/*
188 		 * If the interface is not up, don't do anything.
189 		 */
190 		if ((mii->mii_ifp->if_flags & IFF_UP) == 0)
191 			break;
192 
193 		mii_phy_setmedia(&sc->sc_mii);
194 		switch (IFM_SUBTYPE(ife->ifm_media)) {
195 		case IFM_100_TX:
196 		case IFM_10_T:
197 			/*
198 			 * Report fake lost link event to parent
199 			 * driver.  This will stop MAC of parent
200 			 * driver and make it possible to reconfigure
201 			 * MAC after completion of link establishment.
202 			 * Note, the parent MAC seems to require
203 			 * restarting MAC when underlying any PHY
204 			 * configuration was changed even if the
205 			 * resolved speed/duplex was not changed at
206 			 * all.
207 			 */
208 			mii->mii_media_status = 0;
209 			mii->mii_media_active = IFM_ETHER | IFM_NONE;
210 			sc->mii_link_tick = RDCPHY_MANNEG_TICK;
211 			/* Immediately report link down. */
212 			mii_phy_update(&sc->sc_mii, MII_MEDIACHG);
213 			return (0);
214 		default:
215 			break;
216 		}
217 		break;
218 
219 	case MII_TICK:
220 		/*
221 		 * If we're not currently selected, just return.
222 		 */
223 		if (IFM_INST(ife->ifm_media) != sc->sc_mii.mii_inst)
224 			return (0);
225 
226 		if (mii_phy_tick(&sc->sc_mii) == EJUSTRETURN)
227 			return (0);
228 
229 		if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO) {
230 			/*
231 			 * It seems the PHY hardware does not correctly
232 			 * report link status changes when manual link
233 			 * configuration is in progress.  It is also
234 			 * possible for the PHY to complete establishing
235 			 * a link within one second such that mii(4)
236 			 * did not notice the link change.  To workaround
237 			 * the issue, emulate lost link event and wait
238 			 * for 3 seconds when manual link configuration
239 			 * is in progress.  3 seconds would be long
240 			 * enough to absorb transient link flips.
241 			 */
242 			if (sc->mii_link_tick > 0) {
243 				sc->mii_link_tick--;
244 				return (0);
245 			}
246 		}
247 		break;
248 	}
249 
250 	/* Update the media status. */
251 	mii_phy_status(&sc->sc_mii);
252 
253 	/* Callback if something changed. */
254 	mii_phy_update(&sc->sc_mii, cmd);
255 	return (0);
256 }
257 
258 void
259 rdcphy_status(struct mii_softc *sc)
260 {
261 	struct mii_data *mii = sc->mii_pdata;
262 	struct ifmedia_entry *ife;
263 	int bmsr, bmcr, physts;
264 
265 	ife = mii->mii_media.ifm_cur;
266 
267 	mii->mii_media_status = IFM_AVALID;
268 	mii->mii_media_active = IFM_ETHER;
269 
270 	bmsr = PHY_READ(sc, MII_BMSR) | PHY_READ(sc, MII_BMSR);
271 	physts = PHY_READ(sc, MII_RDCPHY_STATUS);
272 
273 	if (physts & STATUS_LINK_UP)
274 		mii->mii_media_status |= IFM_ACTIVE;
275 
276 	bmcr = PHY_READ(sc, MII_BMCR);
277 	if (bmcr & BMCR_ISO) {
278 		mii->mii_media_active |= IFM_NONE;
279 		mii->mii_media_status = 0;
280 		return;
281 	}
282 
283 	if (bmcr & BMCR_LOOP)
284 		mii->mii_media_active |= IFM_LOOP;
285 
286 	if (bmcr & BMCR_AUTOEN) {
287 		if (!(bmsr & BMSR_ACOMP)) {
288 			/* Erg, still trying, I guess... */
289 			mii->mii_media_active |= IFM_NONE;
290 			return;
291 		}
292 	}
293 
294 	switch (physts & STATUS_SPEED_MASK) {
295 	case STATUS_SPEED_100:
296 		mii->mii_media_active |= IFM_100_TX;
297 		break;
298 	case STATUS_SPEED_10:
299 		mii->mii_media_active |= IFM_10_T;
300 		break;
301 	default:
302 		mii->mii_media_active |= IFM_NONE;
303 		return;
304 	}
305 	if ((physts & STATUS_FULL_DUPLEX) != 0)
306 		mii->mii_media_active |= IFM_FDX |
307 		    mii_phy_flowstatus(sc);
308 	else
309 		mii->mii_media_active |= IFM_HDX;
310 }
311