xref: /netbsd-src/sys/dev/mii/ikphy.c (revision cac8e449158efc7261bebc8657cbb0125a2cfdde)
1 /*	$NetBSD: ikphy.c,v 1.7 2008/05/04 17:06:09 xtraeme 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.7 2008/05/04 17:06:09 xtraeme 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(device_t, cfdata_t, void *);
86 static void	ikphyattach(device_t, device_t, void *);
87 
88 CFATTACH_DECL_NEW(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(device_t parent, cfdata_t match, void *aux)
109 {
110 	struct mii_attach_args *ma = aux;
111 
112 	if (mii_phy_match(ma, ikphys) != NULL)
113 		return (10);
114 
115 	return (0);
116 }
117 
118 static void
119 ikphyattach(device_t parent, device_t self, void *aux)
120 {
121 	struct mii_softc *sc = device_private(self);
122 	struct mii_attach_args *ma = aux;
123 	struct mii_data *mii = ma->mii_data;
124 	const struct mii_phydesc *mpd;
125 
126 	mpd = mii_phy_match(ma, ikphys);
127 	aprint_naive(": Media interface\n");
128 	aprint_normal(": %s, rev. %d\n", mpd->mpd_name, MII_REV(ma->mii_id2));
129 
130 	sc->mii_dev = self;
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_dev(self, "");
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 	switch (cmd) {
163 	case MII_POLLSTAT:
164 		/*
165 		 * If we're not polling our PHY instance, just return.
166 		 */
167 		if (IFM_INST(ife->ifm_media) != sc->mii_inst)
168 			return (0);
169 		break;
170 
171 	case MII_MEDIACHG:
172 		/*
173 		 * If the media indicates a different PHY instance,
174 		 * isolate ourselves.
175 		 */
176 		if (IFM_INST(ife->ifm_media) != sc->mii_inst) {
177 			reg = PHY_READ(sc, MII_BMCR);
178 			PHY_WRITE(sc, MII_BMCR, reg | BMCR_ISO);
179 			return (0);
180 		}
181 
182 		/*
183 		 * If the interface is not up, don't do anything.
184 		 */
185 		if ((mii->mii_ifp->if_flags & IFF_UP) == 0)
186 			break;
187 
188 		ikphy_setmedia(sc);
189 		break;
190 
191 	case MII_TICK:
192 		/*
193 		 * If we're not currently selected, just return.
194 		 */
195 		if (IFM_INST(ife->ifm_media) != sc->mii_inst)
196 			return (0);
197 
198 		if (mii_phy_tick(sc) == EJUSTRETURN)
199 			return (0);
200 		break;
201 
202 	case MII_DOWN:
203 		mii_phy_down(sc);
204 		return (0);
205 	}
206 
207 	/* Update the media status. */
208 	mii_phy_status(sc);
209 
210 	/* Callback if something changed. */
211 	mii_phy_update(sc, cmd);
212 	return (0);
213 }
214 
215 static void
216 ikphy_setmedia(struct mii_softc *sc)
217 {
218 	uint16_t phy_data;
219 	struct mii_data *mii = sc->mii_pdata;
220 	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
221 
222 	/* Enable CRS on TX for half-duplex operation. */
223 	phy_data = PHY_READ(sc, GG82563_PHY_MAC_SPEC_CTRL);
224 	phy_data |= GG82563_MSCR_ASSERT_CRS_ON_TX;
225 	/* Use 25MHz for both link down and 1000BASE-T for Tx clock */
226 	phy_data |= GG82563_MSCR_TX_CLK_1000MBPS_25MHZ;
227 	PHY_WRITE(sc, GG82563_PHY_MAC_SPEC_CTRL, phy_data);
228 
229 	/* set mdi/mid-x options */
230 	phy_data = PHY_READ(sc, GG82563_PHY_SPEC_CTRL);
231 	phy_data &= ~GG82563_PSCR_CROSSOVER_MODE_MASK;
232 	if (IFM_SUBTYPE(ife->ifm_media) == IFM_AUTO)
233 		phy_data |= GG82563_PSCR_CROSSOVER_MODE_AUTO;
234 	else
235 		phy_data |= GG82563_PSCR_CROSSOVER_MODE_MDI;
236 	/* set polarity correction */
237 	phy_data &= ~GG82563_PSCR_POLARITY_REVERSAL_DISABLE;
238 	PHY_WRITE(sc, GG82563_PHY_SPEC_CTRL, phy_data);
239 
240 	/* SW Reset the PHY so all changes take effect */
241 	PHY_RESET(sc);
242 
243 	/* for the i80003 */
244 	phy_data = PHY_READ(sc, GG82563_PHY_SPEC_CTRL_2);
245 	phy_data &= ~GG82563_PSCR2_REVERSE_AUTO_NEG;
246 	PHY_WRITE(sc, GG82563_PHY_SPEC_CTRL_2, phy_data);
247 
248 	/* Enable Electrical Idle on the PHY */
249 	phy_data = PHY_READ(sc, GG82563_PHY_PWR_MGMT_CTRL);
250 	phy_data |= GG82563_PMCR_ENABLE_ELECTRICAL_IDLE;
251 	PHY_WRITE(sc, GG82563_PHY_PWR_MGMT_CTRL, phy_data);
252 
253 	phy_data = PHY_READ(sc, GG82563_PHY_KMRN_MODE_CTRL);
254 	phy_data &= ~GG82563_KMCR_PASS_FALSE_CARRIER;
255 	PHY_WRITE(sc, GG82563_PHY_KMRN_MODE_CTRL, phy_data);
256 
257 	/*
258 	 * Workaround: Disable padding in Kumeran interface in the MAC
259 	 * and in the PHY to avoid CRC errors.
260 	 */
261 	phy_data = PHY_READ(sc, GG82563_PHY_INBAND_CTRL);
262 	phy_data |= GG82563_ICR_DIS_PADDING;
263 	PHY_WRITE(sc, GG82563_PHY_INBAND_CTRL, phy_data);
264 
265 	mii_phy_setmedia(sc);
266 	if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO) {
267 		/*
268 		 * when not in auto mode, we need to restart nego
269 		 * anyway, or a switch from a fixed mode to another
270 		 * fixed mode may not be seen by the switch.
271 		 */
272 		PHY_WRITE(sc, MII_BMCR,
273 		    PHY_READ(sc, MII_BMCR) | BMCR_STARTNEG);
274 	}
275 	phy_data = PHY_READ(sc, GG82563_PHY_MAC_SPEC_CTRL);
276 	phy_data &= ~GG82563_MSCR_TX_CLK_MASK;
277 	switch(IFM_SUBTYPE(ife->ifm_media)) {
278 	case IFM_10_T:
279 		phy_data |= GG82563_MSCR_TX_CLK_10MBPS_2_5MHZ;
280 		break;
281 	case IFM_100_TX:
282 		phy_data |= GG82563_MSCR_TX_CLK_100MBPS_25MHZ;
283 		break;
284 	case IFM_1000_T:
285 		phy_data |= GG82563_MSCR_TX_CLK_1000MBPS_25MHZ;
286 		break;
287 	}
288 	phy_data |= GG82563_MSCR_ASSERT_CRS_ON_TX;
289 	PHY_WRITE(sc, GG82563_PHY_MAC_SPEC_CTRL, phy_data);
290 }
291 
292 static void
293 ikphy_status(struct mii_softc *sc)
294 {
295 	struct mii_data *mii = sc->mii_pdata;
296 	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
297 	int pssr, bmcr, gtsr, kmrn;
298 
299 	mii->mii_media_status = IFM_AVALID;
300 	mii->mii_media_active = IFM_ETHER;
301 
302 	pssr = PHY_READ(sc, GG82563_PHY_SPEC_STATUS);
303 
304 	if (pssr & GG82563_PSSR_LINK)
305 		mii->mii_media_status |= IFM_ACTIVE;
306 
307 	bmcr = PHY_READ(sc, MII_BMCR);
308 	if (bmcr & BMCR_ISO) {
309 		mii->mii_media_active |= IFM_NONE;
310 		mii->mii_media_status = 0;
311 		return;
312 	}
313 
314 	if (bmcr & BMCR_LOOP)
315 		mii->mii_media_active |= IFM_LOOP;
316 
317 	if (bmcr & BMCR_AUTOEN) {
318 		/*
319 		 * The media status bits are only valid of autonegotiation
320 		 * has completed (or it's disabled).
321 		 */
322 		if ((pssr & GG82563_PSSR_SPEED_DUPLEX_RESOLVED) == 0) {
323 			/* Erg, still trying, I guess... */
324 			mii->mii_media_active |= IFM_NONE;
325 			return;
326 		}
327 
328 		switch (pssr & GG82563_PSSR_SPEED_MASK) {
329 		case GG82563_PSSR_SPEED_1000MBPS:
330 			mii->mii_media_active |= IFM_1000_T;
331 			gtsr = PHY_READ(sc, MII_100T2SR);
332 			if (gtsr & GTSR_MS_RES)
333 				mii->mii_media_active |= IFM_ETH_MASTER;
334 			break;
335 
336 		case GG82563_PSSR_SPEED_100MBPS:
337 			mii->mii_media_active |= IFM_100_TX;
338 			break;
339 
340 		case GG82563_PSSR_SPEED_10MBPS:
341 			mii->mii_media_active |= IFM_10_T;
342 			break;
343 
344 		default:
345 			mii->mii_media_active |= IFM_NONE;
346 			mii->mii_media_status = 0;
347 			return;
348 		}
349 
350 		if (pssr & GG82563_PSSR_DUPLEX)
351 			mii->mii_media_active |=
352 			    IFM_FDX | mii_phy_flowstatus(sc);
353 	} else
354 		mii->mii_media_active = ife->ifm_media;
355 	kmrn = PHY_READ(sc, GG82563_PHY_KMRN_MODE_CTRL);
356 	if (mii->mii_media_active & IFM_FDX)
357 		kmrn &= ~GG82563_KMCR_PASS_FALSE_CARRIER;
358 	else
359 		kmrn |= GG82563_KMCR_PASS_FALSE_CARRIER;
360 	PHY_WRITE(sc, GG82563_PHY_KMRN_MODE_CTRL, kmrn);
361 }
362