xref: /netbsd-src/sys/dev/mii/ikphy.c (revision 8b0f9554ff8762542c4defc4f70e1eb76fb508fa)
1 /*	$NetBSD: ikphy.c,v 1.4 2007/12/09 20:28:03 jmcneill Exp $	*/
2 
3 /*******************************************************************************
4 Copyright (c) 2001-2005, Intel Corporation
5 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 are met:
9 
10  1. Redistributions of source code must retain the above copyright notice,
11     this list of conditions and the following disclaimer.
12 
13  2. Redistributions in binary form must reproduce the above copyright
14     notice, this list of conditions and the following disclaimer in the
15     documentation and/or other materials provided with the distribution.
16 
17  3. Neither the name of the Intel Corporation nor the names of its
18     contributors may be used to endorse or promote products derived from
19     this software without specific prior written permission.
20 
21 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
25 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31 POSSIBILITY OF SUCH DAMAGE.
32 *******************************************************************************/
33 /*
34  * Copyright (c) 2006 Manuel Bouyer.  All rights reserved.
35  *
36  * Redistribution and use in source and binary forms, with or without
37  * modification, are permitted provided that the following conditions
38  * are met:
39  * 1. Redistributions of source code must retain the above copyright
40  *    notice, this list of conditions and the following disclaimer.
41  * 2. Redistributions in binary form must reproduce the above copyright
42  *    notice, this list of conditions and the following disclaimer in the
43  *    documentation and/or other materials provided with the distribution.
44  * 3. All advertising materials mentioning features or use of this software
45  *    must display the following acknowledgement:
46  *	This product includes software developed by Manuel Bouyer.
47  * 4. The name of the author may not be used to endorse or promote products
48  *    derived from this software without specific prior written permission.
49  *
50  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
51  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
52  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
53  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
54  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
55  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
56  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
57  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
58  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
59  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
60  */
61 
62 /*
63  * driver for Intel's i82563 ethernet 10/100/1000 PHY
64  */
65 
66 #include <sys/cdefs.h>
67 __KERNEL_RCSID(0, "$NetBSD: ikphy.c,v 1.4 2007/12/09 20:28:03 jmcneill Exp $");
68 
69 #include <sys/param.h>
70 #include <sys/systm.h>
71 #include <sys/kernel.h>
72 #include <sys/device.h>
73 #include <sys/socket.h>
74 #include <sys/errno.h>
75 
76 #include <net/if.h>
77 #include <net/if_media.h>
78 
79 #include <dev/mii/mii.h>
80 #include <dev/mii/miivar.h>
81 #include <dev/mii/miidevs.h>
82 
83 #include <dev/mii/ikphyreg.h>
84 
85 static int	ikphymatch(struct device *, struct cfdata *, void *);
86 static void	ikphyattach(struct device *, struct device *, void *);
87 
88 CFATTACH_DECL(ikphy, sizeof(struct mii_softc),
89     ikphymatch, ikphyattach, mii_phy_detach, mii_phy_activate);
90 
91 static int	ikphy_service(struct mii_softc *, struct mii_data *, int);
92 static void	ikphy_status(struct mii_softc *);
93 static void	ikphy_setmedia(struct mii_softc *);
94 
95 static const struct mii_phy_funcs ikphy_funcs = {
96 	ikphy_service, ikphy_status, mii_phy_reset,
97 };
98 
99 static const struct mii_phydesc ikphys[] = {
100 	{ MII_OUI_xxMARVELL,		MII_MODEL_xxMARVELL_I82563,
101 	  MII_STR_xxMARVELL_I82563 },
102 
103 	{ 0,				0,
104 	  NULL },
105 };
106 
107 static int
108 ikphymatch(struct device *parent, struct cfdata *match,
109     void *aux)
110 {
111 	struct mii_attach_args *ma = aux;
112 
113 	if (mii_phy_match(ma, ikphys) != NULL)
114 		return (10);
115 
116 	return (0);
117 }
118 
119 static void
120 ikphyattach(struct device *parent, struct device *self, void *aux)
121 {
122 	struct mii_softc *sc = device_private(self);
123 	struct mii_attach_args *ma = aux;
124 	struct mii_data *mii = ma->mii_data;
125 	const struct mii_phydesc *mpd;
126 
127 	mpd = mii_phy_match(ma, ikphys);
128 	aprint_naive(": Media interface\n");
129 	aprint_normal(": %s, rev. %d\n", mpd->mpd_name, MII_REV(ma->mii_id2));
130 
131 	sc->mii_inst = mii->mii_instance;
132 	sc->mii_phy = ma->mii_phyno;
133 	sc->mii_funcs = &ikphy_funcs;
134 	sc->mii_pdata = mii;
135 	sc->mii_flags = ma->mii_flags;
136 	sc->mii_anegticks = MII_ANEGTICKS;
137 
138 	PHY_RESET(sc);
139 
140 	sc->mii_capabilities =
141 	    PHY_READ(sc, MII_BMSR) & ma->mii_capmask;
142 	if (sc->mii_capabilities & BMSR_EXTSTAT)
143 	    sc->mii_extcapabilities = PHY_READ(sc, MII_EXTSR);
144 	aprint_normal("%s: ", sc->mii_dev.dv_xname);
145 	if ((sc->mii_capabilities & BMSR_MEDIAMASK) == 0 &&
146 	    (sc->mii_extcapabilities & EXTSR_MEDIAMASK) == 0)
147 		aprint_error("no media present");
148 	else
149 		mii_phy_add_media(sc);
150 	aprint_normal("\n");
151 
152 	if (!pmf_device_register(self, NULL, mii_phy_resume))
153 		aprint_error_dev(self, "couldn't establish power handler\n");
154 }
155 
156 static int
157 ikphy_service(struct mii_softc *sc, struct mii_data *mii, int cmd)
158 {
159 	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
160 	int reg;
161 
162 	if (!device_is_active(&sc->mii_dev))
163 		return (ENXIO);
164 
165 	switch (cmd) {
166 	case MII_POLLSTAT:
167 		/*
168 		 * If we're not polling our PHY instance, just return.
169 		 */
170 		if (IFM_INST(ife->ifm_media) != sc->mii_inst)
171 			return (0);
172 		break;
173 
174 	case MII_MEDIACHG:
175 		/*
176 		 * If the media indicates a different PHY instance,
177 		 * isolate ourselves.
178 		 */
179 		if (IFM_INST(ife->ifm_media) != sc->mii_inst) {
180 			reg = PHY_READ(sc, MII_BMCR);
181 			PHY_WRITE(sc, MII_BMCR, reg | BMCR_ISO);
182 			return (0);
183 		}
184 
185 		/*
186 		 * If the interface is not up, don't do anything.
187 		 */
188 		if ((mii->mii_ifp->if_flags & IFF_UP) == 0)
189 			break;
190 
191 		ikphy_setmedia(sc);
192 		break;
193 
194 	case MII_TICK:
195 		/*
196 		 * If we're not currently selected, just return.
197 		 */
198 		if (IFM_INST(ife->ifm_media) != sc->mii_inst)
199 			return (0);
200 
201 		if (mii_phy_tick(sc) == EJUSTRETURN)
202 			return (0);
203 		break;
204 
205 	case MII_DOWN:
206 		mii_phy_down(sc);
207 		return (0);
208 	}
209 
210 	/* Update the media status. */
211 	mii_phy_status(sc);
212 
213 	/* Callback if something changed. */
214 	mii_phy_update(sc, cmd);
215 	return (0);
216 }
217 
218 static void
219 ikphy_setmedia(struct mii_softc *sc)
220 {
221 	uint16_t phy_data;
222 	struct mii_data *mii = sc->mii_pdata;
223 	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
224 
225 	/* Enable CRS on TX for half-duplex operation. */
226 	phy_data = PHY_READ(sc, GG82563_PHY_MAC_SPEC_CTRL);
227 	phy_data |= GG82563_MSCR_ASSERT_CRS_ON_TX;
228 	/* Use 25MHz for both link down and 1000BASE-T for Tx clock */
229 	phy_data |= GG82563_MSCR_TX_CLK_1000MBPS_25MHZ;
230 	PHY_WRITE(sc, GG82563_PHY_MAC_SPEC_CTRL, phy_data);
231 
232 	/* set mdi/mid-x options */
233 	phy_data = PHY_READ(sc, GG82563_PHY_SPEC_CTRL);
234 	phy_data &= ~GG82563_PSCR_CROSSOVER_MODE_MASK;
235 	if (IFM_SUBTYPE(ife->ifm_media) == IFM_AUTO)
236 		phy_data |= GG82563_PSCR_CROSSOVER_MODE_AUTO;
237 	else
238 		phy_data |= GG82563_PSCR_CROSSOVER_MODE_MDI;
239 	/* set polarity correction */
240 	phy_data &= ~GG82563_PSCR_POLARITY_REVERSAL_DISABLE;
241 	PHY_WRITE(sc, GG82563_PHY_SPEC_CTRL, phy_data);
242 
243 	/* SW Reset the PHY so all changes take effect */
244 	PHY_RESET(sc);
245 
246 	/* for the i80003 */
247 	phy_data = PHY_READ(sc, GG82563_PHY_SPEC_CTRL_2);
248 	phy_data &= ~GG82563_PSCR2_REVERSE_AUTO_NEG;
249 	PHY_WRITE(sc, GG82563_PHY_SPEC_CTRL_2, phy_data);
250 
251 	/* Enable Electrical Idle on the PHY */
252 	phy_data = PHY_READ(sc, GG82563_PHY_PWR_MGMT_CTRL);
253 	phy_data |= GG82563_PMCR_ENABLE_ELECTRICAL_IDLE;
254 	PHY_WRITE(sc, GG82563_PHY_PWR_MGMT_CTRL, phy_data);
255 
256 	phy_data = PHY_READ(sc, GG82563_PHY_KMRN_MODE_CTRL);
257 	phy_data &= ~GG82563_KMCR_PASS_FALSE_CARRIER;
258 	PHY_WRITE(sc, GG82563_PHY_KMRN_MODE_CTRL, phy_data);
259 
260 	/*
261 	 * Workaround: Disable padding in Kumeran interface in the MAC
262 	 * and in the PHY to avoid CRC errors.
263 	 */
264 	phy_data = PHY_READ(sc, GG82563_PHY_INBAND_CTRL);
265 	phy_data |= GG82563_ICR_DIS_PADDING;
266 	PHY_WRITE(sc, GG82563_PHY_INBAND_CTRL, phy_data);
267 
268 	mii_phy_setmedia(sc);
269 	if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO) {
270 		/*
271 		 * when not in auto mode, we need to restart nego
272 		 * anyway, or a switch from a fixed mode to another
273 		 * fixed mode may not be seen by the switch.
274 		 */
275 		PHY_WRITE(sc, MII_BMCR,
276 		    PHY_READ(sc, MII_BMCR) | BMCR_STARTNEG);
277 	}
278 	phy_data = PHY_READ(sc, GG82563_PHY_MAC_SPEC_CTRL);
279 	phy_data &= ~GG82563_MSCR_TX_CLK_MASK;
280 	switch(IFM_SUBTYPE(ife->ifm_media)) {
281 	case IFM_10_T:
282 		phy_data |= GG82563_MSCR_TX_CLK_10MBPS_2_5MHZ;
283 		break;
284 	case IFM_100_TX:
285 		phy_data |= GG82563_MSCR_TX_CLK_100MBPS_25MHZ;
286 		break;
287 	case IFM_1000_T:
288 		phy_data |= GG82563_MSCR_TX_CLK_1000MBPS_25MHZ;
289 		break;
290 	}
291 	phy_data |= GG82563_MSCR_ASSERT_CRS_ON_TX;
292 	PHY_WRITE(sc, GG82563_PHY_MAC_SPEC_CTRL, phy_data);
293 }
294 
295 static void
296 ikphy_status(struct mii_softc *sc)
297 {
298 	struct mii_data *mii = sc->mii_pdata;
299 	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
300 	int pssr, bmcr, gtsr, kmrn;
301 
302 	mii->mii_media_status = IFM_AVALID;
303 	mii->mii_media_active = IFM_ETHER;
304 
305 	pssr = PHY_READ(sc, GG82563_PHY_SPEC_STATUS);
306 
307 	if (pssr & GG82563_PSSR_LINK)
308 		mii->mii_media_status |= IFM_ACTIVE;
309 
310 	bmcr = PHY_READ(sc, MII_BMCR);
311 	if (bmcr & BMCR_ISO) {
312 		mii->mii_media_active |= IFM_NONE;
313 		mii->mii_media_status = 0;
314 		return;
315 	}
316 
317 	if (bmcr & BMCR_LOOP)
318 		mii->mii_media_active |= IFM_LOOP;
319 
320 	if (bmcr & BMCR_AUTOEN) {
321 		/*
322 		 * The media status bits are only valid of autonegotiation
323 		 * has completed (or it's disabled).
324 		 */
325 		if ((pssr & GG82563_PSSR_SPEED_DUPLEX_RESOLVED) == 0) {
326 			/* Erg, still trying, I guess... */
327 			mii->mii_media_active |= IFM_NONE;
328 			return;
329 		}
330 
331 		switch (pssr & GG82563_PSSR_SPEED_MASK) {
332 		case GG82563_PSSR_SPEED_1000MBPS:
333 			mii->mii_media_active |= IFM_1000_T;
334 			gtsr = PHY_READ(sc, MII_100T2SR);
335 			if (gtsr & GTSR_MS_RES)
336 				mii->mii_media_active |= IFM_ETH_MASTER;
337 			break;
338 
339 		case GG82563_PSSR_SPEED_100MBPS:
340 			mii->mii_media_active |= IFM_100_TX;
341 			break;
342 
343 		case GG82563_PSSR_SPEED_10MBPS:
344 			mii->mii_media_active |= IFM_10_T;
345 			break;
346 
347 		default:
348 			mii->mii_media_active |= IFM_NONE;
349 			mii->mii_media_status = 0;
350 			return;
351 		}
352 
353 		if (pssr & GG82563_PSSR_DUPLEX)
354 			mii->mii_media_active |=
355 			    IFM_FDX | mii_phy_flowstatus(sc);
356 	} else
357 		mii->mii_media_active = ife->ifm_media;
358 	kmrn = PHY_READ(sc, GG82563_PHY_KMRN_MODE_CTRL);
359 	if (mii->mii_media_active & IFM_FDX)
360 		kmrn &= ~GG82563_KMCR_PASS_FALSE_CARRIER;
361 	else
362 		kmrn |= GG82563_KMCR_PASS_FALSE_CARRIER;
363 	PHY_WRITE(sc, GG82563_PHY_KMRN_MODE_CTRL, kmrn);
364 }
365