Annotation of sys/dev/mii/ipgphy.c, Revision 1.1.1.1
1.1 nbrk 1: /* $OpenBSD: ipgphy.c,v 1.7 2006/12/31 15:04:33 krw Exp $ */
2:
3: /*-
4: * Copyright (c) 2006, Pyun YongHyeon <yongari@FreeBSD.org>
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
9: * are met:
10: * 1. Redistributions of source code must retain the above copyright
11: * notice unmodified, this list of conditions, and the following
12: * disclaimer.
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: * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20: * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27: * SUCH DAMAGE.
28: *
29: */
30:
31: /*
32: * Driver for the IC Plus IP1000A 10/100/1000 PHY.
33: */
34:
35: #include <sys/param.h>
36: #include <sys/systm.h>
37: #include <sys/kernel.h>
38: #include <sys/device.h>
39: #include <sys/socket.h>
40: #include <sys/errno.h>
41:
42: #include <machine/bus.h>
43:
44: #include <net/if.h>
45: #include <net/if_media.h>
46:
47: #ifdef INET
48: #include <netinet/in.h>
49: #include <netinet/if_ether.h>
50: #endif
51:
52: #include <dev/mii/mii.h>
53: #include <dev/mii/miivar.h>
54: #include <dev/mii/miidevs.h>
55:
56: #include <dev/mii/ipgphyreg.h>
57:
58: #include <dev/pci/if_stgereg.h>
59:
60: int ipgphy_probe(struct device *, void *, void *);
61: void ipgphy_attach(struct device *, struct device *, void *);
62:
63: struct cfattach ipgphy_ca = {
64: sizeof(struct mii_softc), ipgphy_probe, ipgphy_attach, mii_phy_detach,
65: mii_phy_activate
66: };
67:
68: struct cfdriver ipgphy_cd = {
69: NULL, "ipgphy", DV_DULL
70: };
71:
72: int ipgphy_service(struct mii_softc *, struct mii_data *, int);
73: void ipgphy_status(struct mii_softc *);
74: int ipgphy_mii_phy_auto(struct mii_softc *);
75: void ipgphy_load_dspcode(struct mii_softc *);
76: void ipgphy_reset(struct mii_softc *);
77:
78: const struct mii_phy_funcs ipgphy_funcs = {
79: ipgphy_service, ipgphy_status, ipgphy_reset,
80: };
81:
82: static const struct mii_phydesc ipgphys[] = {
83: { MII_OUI_ICPLUS, MII_MODEL_ICPLUS_IP1000A,
84: MII_STR_ICPLUS_IP1000A },
85:
86: { 0,
87: NULL },
88: };
89:
90: int
91: ipgphy_probe(struct device *parent, void *match, void *aux)
92: {
93: struct mii_attach_args *ma = aux;
94:
95: if (mii_phy_match(ma, ipgphys) != NULL)
96: return (10);
97:
98: return (0);
99: }
100:
101: void
102: ipgphy_attach(struct device *parent, struct device *self, void *aux)
103: {
104: struct mii_softc *sc = (struct mii_softc *)self;
105: struct mii_attach_args *ma = aux;
106: struct mii_data *mii = ma->mii_data;
107: const struct mii_phydesc *mpd;
108:
109: mpd = mii_phy_match(ma, ipgphys);
110: printf(": %s, rev. %d\n", mpd->mpd_name, MII_REV(ma->mii_id2));
111:
112: sc->mii_inst = mii->mii_instance;
113: sc->mii_phy = ma->mii_phyno;
114: sc->mii_funcs = &ipgphy_funcs;
115: sc->mii_pdata = mii;
116: sc->mii_anegticks = MII_ANEGTICKS_GIGE;
117:
118: sc->mii_flags |= MIIF_NOISOLATE;
119:
120: #define ADD(m, c) ifmedia_add(&mii->mii_media, (m), (c), NULL)
121:
122: ADD(IFM_MAKEWORD(IFM_ETHER, IFM_NONE, 0, sc->mii_inst),
123: BMCR_ISO);
124:
125: ADD(IFM_MAKEWORD(IFM_ETHER, IFM_10_T, 0, sc->mii_inst),
126: IPGPHY_BMCR_10);
127: ADD(IFM_MAKEWORD(IFM_ETHER, IFM_10_T, IFM_FDX, sc->mii_inst),
128: IPGPHY_BMCR_10 | IPGPHY_BMCR_FDX);
129: ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_TX, 0, sc->mii_inst),
130: IPGPHY_BMCR_100);
131: ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_TX, IFM_FDX, sc->mii_inst),
132: IPGPHY_BMCR_100 | IPGPHY_BMCR_FDX);
133: /* 1000baseT half-duplex, really supported? */
134: ADD(IFM_MAKEWORD(IFM_ETHER, IFM_1000_T, 0, sc->mii_inst),
135: IPGPHY_BMCR_1000);
136: ADD(IFM_MAKEWORD(IFM_ETHER, IFM_1000_T, IFM_FDX, sc->mii_inst),
137: IPGPHY_BMCR_1000 | IPGPHY_BMCR_FDX);
138: ADD(IFM_MAKEWORD(IFM_ETHER, IFM_AUTO, 0, sc->mii_inst), 0);
139: #undef ADD
140:
141: PHY_RESET(sc);
142: }
143:
144: int
145: ipgphy_service(struct mii_softc *sc, struct mii_data *mii, int cmd)
146: {
147: struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
148: uint32_t gig, reg, speed;
149:
150: switch (cmd) {
151: case MII_POLLSTAT:
152: /*
153: * If we're not polling our PHY instance, just return.
154: */
155: if (IFM_INST(ife->ifm_media) != sc->mii_inst)
156: return (0);
157: break;
158:
159: case MII_MEDIACHG:
160: /*
161: * If the media indicates a different PHY instance,
162: * isolate ourselves.
163: */
164: if (IFM_INST(ife->ifm_media) != sc->mii_inst) {
165: reg = PHY_READ(sc, IPGPHY_MII_BMCR);
166: PHY_WRITE(sc, IPGPHY_MII_BMCR,
167: reg | IPGPHY_BMCR_ISO);
168: return (0);
169: }
170:
171: /*
172: * If the interface is not up, don't do anything.
173: */
174: if ((mii->mii_ifp->if_flags & IFF_UP) == 0)
175: break;
176:
177: PHY_RESET(sc);
178:
179: switch (IFM_SUBTYPE(ife->ifm_media)) {
180: case IFM_AUTO:
181: (void)ipgphy_mii_phy_auto(sc);
182: goto done;
183: break;
184:
185: case IFM_1000_T:
186: /*
187: * XXX
188: * Manual 1000baseT setting doesn't seem to work.
189: */
190: speed = IPGPHY_BMCR_1000;
191: break;
192:
193: case IFM_100_TX:
194: speed = IPGPHY_BMCR_100;
195: break;
196:
197: case IFM_10_T:
198: speed = IPGPHY_BMCR_10;
199: break;
200:
201: default:
202: return (EINVAL);
203: }
204:
205: if (((ife->ifm_media & IFM_GMASK) & IFM_FDX) != 0) {
206: speed |= IPGPHY_BMCR_FDX;
207: gig = IPGPHY_1000CR_1000T_FDX;
208: } else
209: gig = IPGPHY_1000CR_1000T;
210:
211: PHY_WRITE(sc, IPGPHY_MII_1000CR, 0);
212: PHY_WRITE(sc, IPGPHY_MII_BMCR, speed);
213:
214: if (IFM_SUBTYPE(ife->ifm_media) != IFM_1000_T)
215: break;
216:
217: PHY_WRITE(sc, IPGPHY_MII_1000CR, gig);
218: PHY_WRITE(sc, IPGPHY_MII_BMCR, speed);
219:
220: if (mii->mii_media.ifm_media & IFM_ETH_MASTER)
221: gig |= IPGPHY_1000CR_MASTER | IPGPHY_1000CR_MANUAL;
222:
223: PHY_WRITE(sc, IPGPHY_MII_1000CR, gig);
224:
225: done:
226: break;
227:
228: case MII_TICK:
229: /*
230: * If we're not currently selected, just return.
231: */
232: if (IFM_INST(ife->ifm_media) != sc->mii_inst)
233: return (0);
234: /*
235: * Is the interface even up?
236: */
237: if ((mii->mii_ifp->if_flags & IFF_UP) == 0)
238: return (0);
239:
240: /*
241: * Only used for autonegotiation.
242: */
243: if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO) {
244: sc->mii_ticks = 0;
245: break;
246: }
247:
248: /*
249: * check for link.
250: */
251: reg = PHY_READ(sc, MII_BMSR) | PHY_READ(sc, MII_BMSR);
252: if (reg & BMSR_LINK) {
253: sc->mii_ticks = 0;
254: break;
255: }
256:
257: /* Announce link loss right after it happens */
258: if (sc->mii_ticks++ == 0)
259: break;
260:
261: /*
262: * Only retry autonegotiation every mii_anegticks seconds.
263: */
264: if (sc->mii_ticks <= sc->mii_anegticks)
265: return (0);
266:
267: sc->mii_ticks = 0;
268: ipgphy_mii_phy_auto(sc);
269: break;
270: }
271:
272: /* Update the media status. */
273: mii_phy_status(sc);
274:
275: /* Callback if something changed. */
276: mii_phy_update(sc, cmd);
277: return (0);
278: }
279:
280: void
281: ipgphy_status(struct mii_softc *sc)
282: {
283: struct mii_data *mii = sc->mii_pdata;
284: uint32_t bmsr, bmcr, stat;
285:
286: mii->mii_media_status = IFM_AVALID;
287: mii->mii_media_active = IFM_ETHER;
288:
289: bmsr = PHY_READ(sc, IPGPHY_MII_BMSR) |
290: PHY_READ(sc, IPGPHY_MII_BMSR);
291: if ((bmsr & IPGPHY_BMSR_LINK) != 0)
292: mii->mii_media_status |= IFM_ACTIVE;
293:
294: bmcr = PHY_READ(sc, IPGPHY_MII_BMCR);
295: if ((bmcr & IPGPHY_BMCR_LOOP) != 0)
296: mii->mii_media_active |= IFM_LOOP;
297:
298: if ((bmcr & IPGPHY_BMCR_AUTOEN) != 0) {
299: if ((bmsr & IPGPHY_BMSR_ANEGCOMP) == 0) {
300: /* Erg, still trying, I guess... */
301: mii->mii_media_active |= IFM_NONE;
302: return;
303: }
304: }
305:
306: stat = PHY_READ(sc, STGE_PhyCtrl);
307: switch (PC_LinkSpeed(stat)) {
308: case PC_LinkSpeed_Down:
309: mii->mii_media_active |= IFM_NONE;
310: return;
311: case PC_LinkSpeed_10:
312: mii->mii_media_active |= IFM_10_T;
313: break;
314: case PC_LinkSpeed_100:
315: mii->mii_media_active |= IFM_100_TX;
316: break;
317: case PC_LinkSpeed_1000:
318: mii->mii_media_active |= IFM_1000_T;
319: break;
320: }
321:
322: if ((stat & PC_PhyDuplexStatus) != 0)
323: mii->mii_media_active |= mii_phy_flowstatus(sc) | IFM_FDX;
324: else
325: mii->mii_media_active |= IFM_HDX;
326:
327: stat = PHY_READ(sc, IPGPHY_MII_1000SR);
328: if ((IFM_SUBTYPE(mii->mii_media_active) == IFM_1000_T) &&
329: stat & IPGPHY_1000SR_MASTER)
330: mii->mii_media_active |= IFM_ETH_MASTER;
331: }
332:
333: int
334: ipgphy_mii_phy_auto(struct mii_softc *mii)
335: {
336: uint32_t reg;
337:
338: reg = IPGPHY_ANAR_10T | IPGPHY_ANAR_10T_FDX |
339: IPGPHY_ANAR_100TX | IPGPHY_ANAR_100TX_FDX;
340:
341: if (sc->mii_flags & MIIF_DOPAUSE)
342: reg |= IPGPHY_ANAR_PAUSE | IPGPHY_ANAR_APAUSE;
343:
344: PHY_WRITE(mii, IPGPHY_MII_ANAR, reg);
345: reg = IPGPHY_1000CR_1000T | IPGPHY_1000CR_1000T_FDX;
346: reg |= IPGPHY_1000CR_MASTER;
347: PHY_WRITE(mii, IPGPHY_MII_1000CR, reg);
348: PHY_WRITE(mii, IPGPHY_MII_BMCR, (IPGPHY_BMCR_FDX |
349: IPGPHY_BMCR_AUTOEN | IPGPHY_BMCR_STARTNEG));
350:
351: return (EJUSTRETURN);
352: }
353:
354: void
355: ipgphy_load_dspcode(struct mii_softc *sc)
356: {
357: PHY_WRITE(sc, 31, 0x0001);
358: PHY_WRITE(sc, 27, 0x01e0);
359: PHY_WRITE(sc, 31, 0x0002);
360: PHY_WRITE(sc, 27, 0xeb8e);
361: PHY_WRITE(sc, 31, 0x0000);
362: PHY_WRITE(sc, 30, 0x005e);
363: PHY_WRITE(sc, 9, 0x0700);
364:
365: DELAY(50);
366: }
367:
368: void
369: ipgphy_reset(struct mii_softc *sc)
370: {
371: struct stge_softc *stge_sc;
372: struct ifnet *ifp;
373: uint32_t reg;
374:
375: mii_phy_reset(sc);
376:
377: /* clear autoneg/full-duplex as we don't want it after reset */
378: reg = PHY_READ(sc, IPGPHY_MII_BMCR);
379: reg &= ~(IPGPHY_BMCR_AUTOEN | IPGPHY_BMCR_FDX);
380: PHY_WRITE(sc, MII_BMCR, reg);
381:
382: ifp = sc->mii_pdata->mii_ifp;
383:
384: if (strcmp(ifp->if_xname, "stge") == 0) {
385: stge_sc = ifp->if_softc;
386: if (stge_sc->sc_rev >= 0x40 && stge_sc->sc_rev <= 0x4e)
387: ipgphy_load_dspcode(sc);
388: }
389: }
CVSweb