1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2008, 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 #include <sys/cdefs.h>
31 /*
32 * Driver for the JMicron JMP211 10/100/1000, JMP202 10/100 PHY.
33 */
34
35 #include <sys/param.h>
36 #include <sys/systm.h>
37 #include <sys/kernel.h>
38 #include <sys/module.h>
39 #include <sys/socket.h>
40 #include <sys/bus.h>
41
42 #include <net/if.h>
43 #include <net/if_var.h>
44 #include <net/if_media.h>
45
46 #include <dev/mii/mii.h>
47 #include <dev/mii/miivar.h>
48 #include "miidevs.h"
49
50 #include <dev/mii/jmphyreg.h>
51
52 #include "miibus_if.h"
53
54 static int jmphy_probe(device_t);
55 static int jmphy_attach(device_t);
56 static void jmphy_reset(struct mii_softc *);
57 static uint16_t jmphy_anar(struct ifmedia_entry *);
58 static int jmphy_setmedia(struct mii_softc *, struct ifmedia_entry *);
59
60 static device_method_t jmphy_methods[] = {
61 /* Device interface. */
62 DEVMETHOD(device_probe, jmphy_probe),
63 DEVMETHOD(device_attach, jmphy_attach),
64 DEVMETHOD(device_detach, mii_phy_detach),
65 DEVMETHOD(device_shutdown, bus_generic_shutdown),
66 DEVMETHOD_END
67 };
68
69 static driver_t jmphy_driver = {
70 "jmphy",
71 jmphy_methods,
72 sizeof(struct mii_softc)
73 };
74
75 DRIVER_MODULE(jmphy, miibus, jmphy_driver, 0, 0);
76
77 static int jmphy_service(struct mii_softc *, struct mii_data *, int);
78 static void jmphy_status(struct mii_softc *);
79
80 static const struct mii_phydesc jmphys[] = {
81 MII_PHY_DESC(JMICRON, JMP202),
82 MII_PHY_DESC(JMICRON, JMP211),
83 MII_PHY_END
84 };
85
86 static const struct mii_phy_funcs jmphy_funcs = {
87 jmphy_service,
88 jmphy_status,
89 jmphy_reset
90 };
91
92 static int
jmphy_probe(device_t dev)93 jmphy_probe(device_t dev)
94 {
95
96 return (mii_phy_dev_probe(dev, jmphys, BUS_PROBE_DEFAULT));
97 }
98
99 static int
jmphy_attach(device_t dev)100 jmphy_attach(device_t dev)
101 {
102 u_int flags;
103
104 flags = 0;
105 if (mii_dev_mac_match(dev, "jme") &&
106 (miibus_get_flags(dev) & MIIF_MACPRIV0) != 0)
107 flags |= MIIF_PHYPRIV0;
108 mii_phy_dev_attach(dev, flags, &jmphy_funcs, 1);
109 return (0);
110 }
111
112 static int
jmphy_service(struct mii_softc * sc,struct mii_data * mii,int cmd)113 jmphy_service(struct mii_softc *sc, struct mii_data *mii, int cmd)
114 {
115 struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
116
117 switch (cmd) {
118 case MII_POLLSTAT:
119 break;
120
121 case MII_MEDIACHG:
122 if (jmphy_setmedia(sc, ife) != EJUSTRETURN)
123 return (EINVAL);
124 break;
125
126 case MII_TICK:
127 /*
128 * Only used for autonegotiation.
129 */
130 if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO) {
131 sc->mii_ticks = 0;
132 break;
133 }
134
135 /* Check for link. */
136 if ((PHY_READ(sc, JMPHY_SSR) & JMPHY_SSR_LINK_UP) != 0) {
137 sc->mii_ticks = 0;
138 break;
139 }
140
141 /* Announce link loss right after it happens. */
142 if (sc->mii_ticks++ == 0)
143 break;
144 if (sc->mii_ticks <= sc->mii_anegticks)
145 return (0);
146
147 sc->mii_ticks = 0;
148 (void)jmphy_setmedia(sc, ife);
149 break;
150 }
151
152 /* Update the media status. */
153 PHY_STATUS(sc);
154
155 /* Callback if something changed. */
156 mii_phy_update(sc, cmd);
157 return (0);
158 }
159
160 static void
jmphy_status(struct mii_softc * sc)161 jmphy_status(struct mii_softc *sc)
162 {
163 struct mii_data *mii = sc->mii_pdata;
164 int bmcr, ssr;
165
166 mii->mii_media_status = IFM_AVALID;
167 mii->mii_media_active = IFM_ETHER;
168
169 ssr = PHY_READ(sc, JMPHY_SSR);
170 if ((ssr & JMPHY_SSR_LINK_UP) != 0)
171 mii->mii_media_status |= IFM_ACTIVE;
172
173 bmcr = PHY_READ(sc, MII_BMCR);
174 if ((bmcr & BMCR_ISO) != 0) {
175 mii->mii_media_active |= IFM_NONE;
176 mii->mii_media_status = 0;
177 return;
178 }
179
180 if ((bmcr & BMCR_LOOP) != 0)
181 mii->mii_media_active |= IFM_LOOP;
182
183 if ((ssr & JMPHY_SSR_SPD_DPLX_RESOLVED) == 0) {
184 /* Erg, still trying, I guess... */
185 mii->mii_media_active |= IFM_NONE;
186 return;
187 }
188
189 switch ((ssr & JMPHY_SSR_SPEED_MASK)) {
190 case JMPHY_SSR_SPEED_1000:
191 mii->mii_media_active |= IFM_1000_T;
192 /*
193 * jmphy(4) got a valid link so reset mii_ticks.
194 * Resetting mii_ticks is needed in order to
195 * detect link loss after auto-negotiation.
196 */
197 sc->mii_ticks = 0;
198 break;
199 case JMPHY_SSR_SPEED_100:
200 mii->mii_media_active |= IFM_100_TX;
201 sc->mii_ticks = 0;
202 break;
203 case JMPHY_SSR_SPEED_10:
204 mii->mii_media_active |= IFM_10_T;
205 sc->mii_ticks = 0;
206 break;
207 default:
208 mii->mii_media_active |= IFM_NONE;
209 return;
210 }
211
212 if ((ssr & JMPHY_SSR_DUPLEX) != 0)
213 mii->mii_media_active |= IFM_FDX | mii_phy_flowstatus(sc);
214 else
215 mii->mii_media_active |= IFM_HDX;
216
217 if (IFM_SUBTYPE(mii->mii_media_active) == IFM_1000_T) {
218 if ((PHY_READ(sc, MII_100T2SR) & GTSR_MS_RES) != 0)
219 mii->mii_media_active |= IFM_ETH_MASTER;
220 }
221 }
222
223 static void
jmphy_reset(struct mii_softc * sc)224 jmphy_reset(struct mii_softc *sc)
225 {
226 uint16_t t2cr, val;
227 int i;
228
229 /* Disable sleep mode. */
230 PHY_WRITE(sc, JMPHY_TMCTL,
231 PHY_READ(sc, JMPHY_TMCTL) & ~JMPHY_TMCTL_SLEEP_ENB);
232 PHY_WRITE(sc, MII_BMCR, BMCR_RESET | BMCR_AUTOEN);
233
234 for (i = 0; i < 1000; i++) {
235 DELAY(1);
236 if ((PHY_READ(sc, MII_BMCR) & BMCR_RESET) == 0)
237 break;
238 }
239 /* Perform vendor recommended PHY calibration. */
240 if ((sc->mii_flags & MIIF_PHYPRIV0) != 0) {
241 /* Select PHY test mode 1. */
242 t2cr = PHY_READ(sc, MII_100T2CR);
243 t2cr &= ~GTCR_TEST_MASK;
244 t2cr |= 0x2000;
245 PHY_WRITE(sc, MII_100T2CR, t2cr);
246 /* Apply calibration patch. */
247 PHY_WRITE(sc, JMPHY_SPEC_ADDR, JMPHY_SPEC_ADDR_READ |
248 JMPHY_EXT_COMM_2);
249 val = PHY_READ(sc, JMPHY_SPEC_DATA);
250 val &= ~0x0002;
251 val |= 0x0010 | 0x0001;
252 PHY_WRITE(sc, JMPHY_SPEC_DATA, val);
253 PHY_WRITE(sc, JMPHY_SPEC_ADDR, JMPHY_SPEC_ADDR_WRITE |
254 JMPHY_EXT_COMM_2);
255
256 /* XXX 20ms to complete recalibration. */
257 DELAY(20 * 1000);
258
259 PHY_READ(sc, MII_100T2CR);
260 PHY_WRITE(sc, JMPHY_SPEC_ADDR, JMPHY_SPEC_ADDR_READ |
261 JMPHY_EXT_COMM_2);
262 val = PHY_READ(sc, JMPHY_SPEC_DATA);
263 val &= ~(0x0001 | 0x0002 | 0x0010);
264 PHY_WRITE(sc, JMPHY_SPEC_DATA, val);
265 PHY_WRITE(sc, JMPHY_SPEC_ADDR, JMPHY_SPEC_ADDR_WRITE |
266 JMPHY_EXT_COMM_2);
267 /* Disable PHY test mode. */
268 PHY_READ(sc, MII_100T2CR);
269 t2cr &= ~GTCR_TEST_MASK;
270 PHY_WRITE(sc, MII_100T2CR, t2cr);
271 }
272 }
273
274 static uint16_t
jmphy_anar(struct ifmedia_entry * ife)275 jmphy_anar(struct ifmedia_entry *ife)
276 {
277 uint16_t anar;
278
279 anar = 0;
280 switch (IFM_SUBTYPE(ife->ifm_media)) {
281 case IFM_AUTO:
282 anar |= ANAR_TX_FD | ANAR_TX | ANAR_10_FD | ANAR_10;
283 break;
284 case IFM_1000_T:
285 break;
286 case IFM_100_TX:
287 anar |= ANAR_TX | ANAR_TX_FD;
288 break;
289 case IFM_10_T:
290 anar |= ANAR_10 | ANAR_10_FD;
291 break;
292 default:
293 break;
294 }
295
296 return (anar);
297 }
298
299 static int
jmphy_setmedia(struct mii_softc * sc,struct ifmedia_entry * ife)300 jmphy_setmedia(struct mii_softc *sc, struct ifmedia_entry *ife)
301 {
302 uint16_t anar, bmcr, gig;
303
304 gig = 0;
305 bmcr = PHY_READ(sc, MII_BMCR);
306 switch (IFM_SUBTYPE(ife->ifm_media)) {
307 case IFM_AUTO:
308 gig |= GTCR_ADV_1000TFDX | GTCR_ADV_1000THDX;
309 break;
310 case IFM_1000_T:
311 gig |= GTCR_ADV_1000TFDX | GTCR_ADV_1000THDX;
312 break;
313 case IFM_100_TX:
314 case IFM_10_T:
315 break;
316 case IFM_NONE:
317 PHY_WRITE(sc, MII_BMCR, bmcr | BMCR_ISO | BMCR_PDOWN);
318 return (EJUSTRETURN);
319 default:
320 return (EINVAL);
321 }
322
323 anar = jmphy_anar(ife);
324 if ((IFM_SUBTYPE(ife->ifm_media) == IFM_AUTO ||
325 (ife->ifm_media & IFM_FDX) != 0) &&
326 ((ife->ifm_media & IFM_FLOW) != 0 ||
327 (sc->mii_flags & MIIF_FORCEPAUSE) != 0))
328 anar |= ANAR_PAUSE_TOWARDS;
329
330 if ((sc->mii_flags & MIIF_HAVE_GTCR) != 0) {
331 if (IFM_SUBTYPE(ife->ifm_media) == IFM_1000_T) {
332 gig |= GTCR_MAN_MS;
333 if ((ife->ifm_media & IFM_ETH_MASTER) != 0)
334 gig |= GTCR_ADV_MS;
335 }
336 PHY_WRITE(sc, MII_100T2CR, gig);
337 }
338 PHY_WRITE(sc, MII_ANAR, anar | ANAR_CSMA);
339 PHY_WRITE(sc, MII_BMCR, bmcr | BMCR_AUTOEN | BMCR_STARTNEG);
340
341 return (EJUSTRETURN);
342 }
343