xref: /openbsd-src/sys/dev/mii/rdcphy.c (revision 5a38ef86d0b61900239c7913d24a05e7b88a58f0)
1 /*	$OpenBSD: rdcphy.c,v 1.3 2014/12/05 15:50:04 mpi 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_var.h>
41 #include <net/if_media.h>
42 
43 #include <dev/mii/mii.h>
44 #include <dev/mii/miivar.h>
45 #include <dev/mii/miidevs.h>
46 
47 #define	MII_RDCPHY_DEBUG	0x11
48 #define	DEBUG_JABBER_DIS	0x0040
49 #define	DEBUG_LOOP_BACK_10MBPS	0x0400
50 
51 #define	MII_RDCPHY_CTRL		0x14
52 #define	CTRL_SQE_ENB		0x0100
53 #define	CTRL_NEG_POLARITY	0x0400
54 #define	CTRL_AUTO_POLARITY	0x0800
55 #define	CTRL_MDIXSEL_RX		0x2000
56 #define	CTRL_MDIXSEL_TX		0x4000
57 #define	CTRL_AUTO_MDIX_DIS	0x8000
58 
59 #define	MII_RDCPHY_CTRL2	0x15
60 #define	CTRL2_LED_DUPLEX	0x0000
61 #define	CTRL2_LED_DUPLEX_COL	0x0008
62 #define	CTRL2_LED_ACT		0x0010
63 #define	CTRL2_LED_SPEED_ACT	0x0018
64 #define	CTRL2_LED_BLK_100MBPS_DIS	0x0020
65 #define	CTRL2_LED_BLK_10MBPS_DIS	0x0040
66 #define	CTRL2_LED_BLK_LINK_ACT_DIS	0x0080
67 #define	CTRL2_SDT_THRESH_MASK	0x3E00
68 #define	CTRL2_TIMING_ERR_SEL	0x4000
69 #define	CTRL2_LED_BLK_80MS	0x8000
70 #define	CTRL2_LED_BLK_160MS	0x0000
71 #define	CTRL2_LED_MASK		0x0018
72 
73 #define	MII_RDCPHY_STATUS	0x16
74 #define	STATUS_AUTO_MDIX_RX	0x0200
75 #define	STATUS_AUTO_MDIX_TX	0x0400
76 #define	STATUS_NEG_POLARITY	0x0800
77 #define	STATUS_FULL_DUPLEX	0x1000
78 #define	STATUS_SPEED_10		0x0000
79 #define	STATUS_SPEED_100	0x2000
80 #define	STATUS_SPEED_MASK	0x6000
81 #define	STATUS_LINK_UP		0x8000
82 
83 /* Analog test register 2 */
84 #define	MII_RDCPHY_TEST2	0x1A
85 #define	TEST2_PWR_DOWN		0x0200
86 
87 struct rdcphy_softc {
88 	struct mii_softc sc_mii;
89 	int mii_link_tick;
90 #define	RDCPHY_MANNEG_TICK	3
91 };
92 
93 int	rdcphy_service(struct mii_softc *, struct mii_data *, int);
94 void	rdcphy_attach(struct device *, struct device *, void *);
95 int	rdcphy_match(struct device *, void *, void *);
96 void	rdcphy_status(struct mii_softc *);
97 
98 const struct mii_phy_funcs rdcphy_funcs = {
99 	rdcphy_service, rdcphy_status, mii_phy_reset,
100 };
101 
102 static const struct mii_phydesc rdcphys[] = {
103 	{ MII_OUI_RDC,		MII_MODEL_RDC_R6040,
104 	  MII_STR_RDC_R6040 },
105 	{ 0,			0,
106 	  NULL },
107 };
108 
109 struct cfattach rdcphy_ca = {
110 	sizeof(struct rdcphy_softc), rdcphy_match, rdcphy_attach,
111 	mii_phy_detach
112 };
113 
114 struct cfdriver rdcphy_cd = {
115 	NULL, "rdcphy", DV_DULL
116 };
117 
118 int
119 rdcphy_match(struct device *parent, void *match, void *aux)
120 {
121 	struct mii_attach_args *ma = aux;
122 
123 	if (mii_phy_match(ma, rdcphys) != NULL)
124 		return (10);
125 
126 	return (0);
127 }
128 
129 void
130 rdcphy_attach(struct device *parent, struct device *self, void *aux)
131 {
132 	struct rdcphy_softc *sc = (struct rdcphy_softc *)self;
133 	struct mii_attach_args *ma = aux;
134 	struct mii_data *mii = ma->mii_data;
135 	const struct mii_phydesc *mpd;
136 
137 	mpd = mii_phy_match(ma, rdcphys);
138 	printf(": %s, rev. %d\n", mpd->mpd_name, MII_REV(ma->mii_id2));
139 
140 	sc->sc_mii.mii_inst = mii->mii_instance;
141 	sc->sc_mii.mii_phy = ma->mii_phyno;
142 	sc->sc_mii.mii_funcs = &rdcphy_funcs;
143 	sc->sc_mii.mii_pdata = mii;
144 	sc->sc_mii.mii_flags = ma->mii_flags;
145 
146 	PHY_RESET(&sc->sc_mii);
147 
148 	sc->sc_mii.mii_capabilities =
149 	    PHY_READ(&sc->sc_mii, MII_BMSR) & ma->mii_capmask;
150 	if (sc->sc_mii.mii_capabilities & BMSR_EXTSTAT)
151 		sc->sc_mii.mii_extcapabilities =
152 		    PHY_READ(&sc->sc_mii, MII_EXTSR);
153 
154 	if (sc->sc_mii.mii_capabilities & BMSR_MEDIAMASK)
155 		mii_phy_add_media(&sc->sc_mii);
156 }
157 
158 int
159 rdcphy_service(struct mii_softc *self, struct mii_data *mii, int cmd)
160 {
161 	struct rdcphy_softc *sc = (struct rdcphy_softc *)self;
162 	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
163 	int reg;
164 
165 	if ((sc->sc_mii.mii_dev.dv_flags & DVF_ACTIVE) == 0)
166 		return (ENXIO);
167 
168 	switch (cmd) {
169 	case MII_POLLSTAT:
170 		/*
171 		 * If we're not polling our PHY instance, just return.
172 		 */
173 		if (IFM_INST(ife->ifm_media) != sc->sc_mii.mii_inst)
174 			return (0);
175 		break;
176 
177 	case MII_MEDIACHG:
178 		/*
179 		 * If the media indicates a different PHY instance,
180 		 * isolate ourselves.
181 		 */
182 		if (IFM_INST(ife->ifm_media) != sc->sc_mii.mii_inst) {
183 			reg = PHY_READ(&sc->sc_mii, MII_BMCR);
184 			PHY_WRITE(&sc->sc_mii, MII_BMCR, reg | BMCR_ISO);
185 			return (0);
186 		}
187 
188 		/*
189 		 * If the interface is not up, don't do anything.
190 		 */
191 		if ((mii->mii_ifp->if_flags & IFF_UP) == 0)
192 			break;
193 
194 		mii_phy_setmedia(&sc->sc_mii);
195 		switch (IFM_SUBTYPE(ife->ifm_media)) {
196 		case IFM_100_TX:
197 		case IFM_10_T:
198 			/*
199 			 * Report fake lost link event to parent
200 			 * driver.  This will stop MAC of parent
201 			 * driver and make it possible to reconfigure
202 			 * MAC after completion of link establishment.
203 			 * Note, the parent MAC seems to require
204 			 * restarting MAC when underlying any PHY
205 			 * configuration was changed even if the
206 			 * resolved speed/duplex was not changed at
207 			 * all.
208 			 */
209 			mii->mii_media_status = 0;
210 			mii->mii_media_active = IFM_ETHER | IFM_NONE;
211 			sc->mii_link_tick = RDCPHY_MANNEG_TICK;
212 			/* Immediately report link down. */
213 			mii_phy_update(&sc->sc_mii, MII_MEDIACHG);
214 			return (0);
215 		default:
216 			break;
217 		}
218 		break;
219 
220 	case MII_TICK:
221 		/*
222 		 * If we're not currently selected, just return.
223 		 */
224 		if (IFM_INST(ife->ifm_media) != sc->sc_mii.mii_inst)
225 			return (0);
226 
227 		if (mii_phy_tick(&sc->sc_mii) == EJUSTRETURN)
228 			return (0);
229 
230 		if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO) {
231 			/*
232 			 * It seems the PHY hardware does not correctly
233 			 * report link status changes when manual link
234 			 * configuration is in progress.  It is also
235 			 * possible for the PHY to complete establishing
236 			 * a link within one second such that mii(4)
237 			 * did not notice the link change.  To workaround
238 			 * the issue, emulate lost link event and wait
239 			 * for 3 seconds when manual link configuration
240 			 * is in progress.  3 seconds would be long
241 			 * enough to absorb transient link flips.
242 			 */
243 			if (sc->mii_link_tick > 0) {
244 				sc->mii_link_tick--;
245 				return (0);
246 			}
247 		}
248 		break;
249 	}
250 
251 	/* Update the media status. */
252 	mii_phy_status(&sc->sc_mii);
253 
254 	/* Callback if something changed. */
255 	mii_phy_update(&sc->sc_mii, cmd);
256 	return (0);
257 }
258 
259 void
260 rdcphy_status(struct mii_softc *sc)
261 {
262 	struct mii_data *mii = sc->mii_pdata;
263 	struct ifmedia_entry *ife;
264 	int bmsr, bmcr, physts;
265 
266 	ife = mii->mii_media.ifm_cur;
267 
268 	mii->mii_media_status = IFM_AVALID;
269 	mii->mii_media_active = IFM_ETHER;
270 
271 	bmsr = PHY_READ(sc, MII_BMSR) | PHY_READ(sc, MII_BMSR);
272 	physts = PHY_READ(sc, MII_RDCPHY_STATUS);
273 
274 	if (physts & STATUS_LINK_UP)
275 		mii->mii_media_status |= IFM_ACTIVE;
276 
277 	bmcr = PHY_READ(sc, MII_BMCR);
278 	if (bmcr & BMCR_ISO) {
279 		mii->mii_media_active |= IFM_NONE;
280 		mii->mii_media_status = 0;
281 		return;
282 	}
283 
284 	if (bmcr & BMCR_LOOP)
285 		mii->mii_media_active |= IFM_LOOP;
286 
287 	if (bmcr & BMCR_AUTOEN) {
288 		if (!(bmsr & BMSR_ACOMP)) {
289 			/* Erg, still trying, I guess... */
290 			mii->mii_media_active |= IFM_NONE;
291 			return;
292 		}
293 	}
294 
295 	switch (physts & STATUS_SPEED_MASK) {
296 	case STATUS_SPEED_100:
297 		mii->mii_media_active |= IFM_100_TX;
298 		break;
299 	case STATUS_SPEED_10:
300 		mii->mii_media_active |= IFM_10_T;
301 		break;
302 	default:
303 		mii->mii_media_active |= IFM_NONE;
304 		return;
305 	}
306 	if ((physts & STATUS_FULL_DUPLEX) != 0)
307 		mii->mii_media_active |= IFM_FDX |
308 		    mii_phy_flowstatus(sc);
309 	else
310 		mii->mii_media_active |= IFM_HDX;
311 }
312