1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause
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 #include <sys/cdefs.h>
32 /*
33 * Driver for the IC Plus IP1000A/IP1001 10/100/1000 PHY.
34 */
35
36 #include <sys/param.h>
37 #include <sys/systm.h>
38 #include <sys/kernel.h>
39 #include <sys/module.h>
40 #include <sys/socket.h>
41 #include <sys/taskqueue.h>
42 #include <sys/bus.h>
43
44 #include <net/if.h>
45 #include <net/if_var.h>
46 #include <net/if_media.h>
47
48 #include <dev/mii/mii.h>
49 #include <dev/mii/miivar.h>
50 #include "miidevs.h"
51
52 #include <dev/mii/ip1000phyreg.h>
53
54 #include "miibus_if.h"
55
56 #include <machine/bus.h>
57 #include <dev/stge/if_stgereg.h>
58
59 static int ip1000phy_probe(device_t);
60 static int ip1000phy_attach(device_t);
61
62 static device_method_t ip1000phy_methods[] = {
63 /* device interface */
64 DEVMETHOD(device_probe, ip1000phy_probe),
65 DEVMETHOD(device_attach, ip1000phy_attach),
66 DEVMETHOD(device_detach, mii_phy_detach),
67 DEVMETHOD(device_shutdown, bus_generic_shutdown),
68 DEVMETHOD_END
69 };
70
71 static driver_t ip1000phy_driver = {
72 "ip1000phy",
73 ip1000phy_methods,
74 sizeof(struct mii_softc)
75 };
76
77 DRIVER_MODULE(ip1000phy, miibus, ip1000phy_driver, 0, 0);
78
79 static int ip1000phy_service(struct mii_softc *, struct mii_data *, int);
80 static void ip1000phy_status(struct mii_softc *);
81 static void ip1000phy_reset(struct mii_softc *);
82 static int ip1000phy_mii_phy_auto(struct mii_softc *, int);
83
84 static const struct mii_phydesc ip1000phys[] = {
85 MII_PHY_DESC(xxICPLUS, IP1000A),
86 MII_PHY_DESC(xxICPLUS, IP1001),
87 MII_PHY_END
88 };
89
90 static const struct mii_phy_funcs ip1000phy_funcs = {
91 ip1000phy_service,
92 ip1000phy_status,
93 ip1000phy_reset
94 };
95
96 static int
ip1000phy_probe(device_t dev)97 ip1000phy_probe(device_t dev)
98 {
99
100 return (mii_phy_dev_probe(dev, ip1000phys, BUS_PROBE_DEFAULT));
101 }
102
103 static int
ip1000phy_attach(device_t dev)104 ip1000phy_attach(device_t dev)
105 {
106 struct mii_attach_args *ma;
107 u_int flags;
108
109 ma = device_get_ivars(dev);
110 flags = MIIF_NOISOLATE | MIIF_NOMANPAUSE;
111 if (MII_MODEL(ma->mii_id2) == MII_MODEL_xxICPLUS_IP1000A &&
112 mii_dev_mac_match(dev, "stge") &&
113 (miibus_get_flags(dev) & MIIF_MACPRIV0) != 0)
114 flags |= MIIF_PHYPRIV0;
115 mii_phy_dev_attach(dev, flags, &ip1000phy_funcs, 1);
116 return (0);
117 }
118
119 static int
ip1000phy_service(struct mii_softc * sc,struct mii_data * mii,int cmd)120 ip1000phy_service(struct mii_softc *sc, struct mii_data *mii, int cmd)
121 {
122 struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
123 uint32_t gig, reg, speed;
124
125 switch (cmd) {
126 case MII_POLLSTAT:
127 break;
128
129 case MII_MEDIACHG:
130 PHY_RESET(sc);
131 switch (IFM_SUBTYPE(ife->ifm_media)) {
132 case IFM_AUTO:
133 (void)ip1000phy_mii_phy_auto(sc, ife->ifm_media);
134 goto done;
135
136 case IFM_1000_T:
137 /*
138 * XXX
139 * Manual 1000baseT setting doesn't seem to work.
140 */
141 speed = IP1000PHY_BMCR_1000;
142 break;
143
144 case IFM_100_TX:
145 speed = IP1000PHY_BMCR_100;
146 break;
147
148 case IFM_10_T:
149 speed = IP1000PHY_BMCR_10;
150 break;
151
152 default:
153 return (EINVAL);
154 }
155
156 if ((ife->ifm_media & IFM_FDX) != 0) {
157 speed |= IP1000PHY_BMCR_FDX;
158 gig = IP1000PHY_1000CR_1000T_FDX;
159 } else
160 gig = IP1000PHY_1000CR_1000T;
161
162 if (IFM_SUBTYPE(ife->ifm_media) == IFM_1000_T) {
163 gig |=
164 IP1000PHY_1000CR_MASTER | IP1000PHY_1000CR_MANUAL;
165 if ((ife->ifm_media & IFM_ETH_MASTER) != 0)
166 gig |= IP1000PHY_1000CR_MMASTER;
167 } else
168 gig = 0;
169 PHY_WRITE(sc, IP1000PHY_MII_1000CR, gig);
170 PHY_WRITE(sc, IP1000PHY_MII_BMCR, speed);
171
172 done:
173 break;
174
175 case MII_TICK:
176 /*
177 * Only used for autonegotiation.
178 */
179 if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO) {
180 sc->mii_ticks = 0;
181 break;
182 }
183
184 /*
185 * check for link.
186 */
187 reg = PHY_READ(sc, MII_BMSR) | PHY_READ(sc, MII_BMSR);
188 if (reg & BMSR_LINK) {
189 sc->mii_ticks = 0;
190 break;
191 }
192
193 /* Announce link loss right after it happens */
194 if (sc->mii_ticks++ == 0)
195 break;
196
197 /*
198 * Only retry autonegotiation every mii_anegticks seconds.
199 */
200 if (sc->mii_ticks <= sc->mii_anegticks)
201 break;
202
203 sc->mii_ticks = 0;
204 ip1000phy_mii_phy_auto(sc, ife->ifm_media);
205 break;
206 }
207
208 /* Update the media status. */
209 PHY_STATUS(sc);
210
211 /* Callback if something changed. */
212 mii_phy_update(sc, cmd);
213 return (0);
214 }
215
216 static void
ip1000phy_status(struct mii_softc * sc)217 ip1000phy_status(struct mii_softc *sc)
218 {
219 struct mii_data *mii = sc->mii_pdata;
220 uint32_t bmsr, bmcr, stat;
221
222 mii->mii_media_status = IFM_AVALID;
223 mii->mii_media_active = IFM_ETHER;
224
225 bmsr = PHY_READ(sc, IP1000PHY_MII_BMSR) |
226 PHY_READ(sc, IP1000PHY_MII_BMSR);
227 if ((bmsr & IP1000PHY_BMSR_LINK) != 0)
228 mii->mii_media_status |= IFM_ACTIVE;
229
230 bmcr = PHY_READ(sc, IP1000PHY_MII_BMCR);
231 if ((bmcr & IP1000PHY_BMCR_LOOP) != 0)
232 mii->mii_media_active |= IFM_LOOP;
233
234 if ((bmcr & IP1000PHY_BMCR_AUTOEN) != 0) {
235 if ((bmsr & IP1000PHY_BMSR_ANEGCOMP) == 0) {
236 /* Erg, still trying, I guess... */
237 mii->mii_media_active |= IFM_NONE;
238 return;
239 }
240 }
241
242 if (sc->mii_mpd_model == MII_MODEL_xxICPLUS_IP1001) {
243 stat = PHY_READ(sc, IP1000PHY_LSR);
244 switch (stat & IP1000PHY_LSR_SPEED_MASK) {
245 case IP1000PHY_LSR_SPEED_10:
246 mii->mii_media_active |= IFM_10_T;
247 break;
248 case IP1000PHY_LSR_SPEED_100:
249 mii->mii_media_active |= IFM_100_TX;
250 break;
251 case IP1000PHY_LSR_SPEED_1000:
252 mii->mii_media_active |= IFM_1000_T;
253 break;
254 default:
255 mii->mii_media_active |= IFM_NONE;
256 return;
257 }
258 if ((stat & IP1000PHY_LSR_FULL_DUPLEX) != 0)
259 mii->mii_media_active |= IFM_FDX;
260 else
261 mii->mii_media_active |= IFM_HDX;
262 } else {
263 stat = PHY_READ(sc, STGE_PhyCtrl);
264 switch (PC_LinkSpeed(stat)) {
265 case PC_LinkSpeed_Down:
266 mii->mii_media_active |= IFM_NONE;
267 return;
268 case PC_LinkSpeed_10:
269 mii->mii_media_active |= IFM_10_T;
270 break;
271 case PC_LinkSpeed_100:
272 mii->mii_media_active |= IFM_100_TX;
273 break;
274 case PC_LinkSpeed_1000:
275 mii->mii_media_active |= IFM_1000_T;
276 break;
277 default:
278 mii->mii_media_active |= IFM_NONE;
279 return;
280 }
281 if ((stat & PC_PhyDuplexStatus) != 0)
282 mii->mii_media_active |= IFM_FDX;
283 else
284 mii->mii_media_active |= IFM_HDX;
285 }
286
287 if ((mii->mii_media_active & IFM_FDX) != 0)
288 mii->mii_media_active |= mii_phy_flowstatus(sc);
289
290 if ((mii->mii_media_active & IFM_1000_T) != 0) {
291 stat = PHY_READ(sc, IP1000PHY_MII_1000SR);
292 if ((stat & IP1000PHY_1000SR_MASTER) != 0)
293 mii->mii_media_active |= IFM_ETH_MASTER;
294 }
295 }
296
297 static int
ip1000phy_mii_phy_auto(struct mii_softc * sc,int media)298 ip1000phy_mii_phy_auto(struct mii_softc *sc, int media)
299 {
300 uint32_t reg;
301
302 reg = 0;
303 if (sc->mii_mpd_model == MII_MODEL_xxICPLUS_IP1001) {
304 reg = PHY_READ(sc, IP1000PHY_MII_ANAR);
305 reg &= ~(IP1000PHY_ANAR_PAUSE | IP1000PHY_ANAR_APAUSE);
306 reg |= IP1000PHY_ANAR_NP;
307 }
308 reg |= IP1000PHY_ANAR_10T | IP1000PHY_ANAR_10T_FDX |
309 IP1000PHY_ANAR_100TX | IP1000PHY_ANAR_100TX_FDX;
310 if ((media & IFM_FLOW) != 0 || (sc->mii_flags & MIIF_FORCEPAUSE) != 0)
311 reg |= IP1000PHY_ANAR_PAUSE | IP1000PHY_ANAR_APAUSE;
312 PHY_WRITE(sc, IP1000PHY_MII_ANAR, reg | IP1000PHY_ANAR_CSMA);
313
314 reg = IP1000PHY_1000CR_1000T | IP1000PHY_1000CR_1000T_FDX;
315 if (sc->mii_mpd_model != MII_MODEL_xxICPLUS_IP1001)
316 reg |= IP1000PHY_1000CR_MASTER;
317 PHY_WRITE(sc, IP1000PHY_MII_1000CR, reg);
318 PHY_WRITE(sc, IP1000PHY_MII_BMCR, (IP1000PHY_BMCR_FDX |
319 IP1000PHY_BMCR_AUTOEN | IP1000PHY_BMCR_STARTNEG));
320
321 return (EJUSTRETURN);
322 }
323
324 static void
ip1000phy_load_dspcode(struct mii_softc * sc)325 ip1000phy_load_dspcode(struct mii_softc *sc)
326 {
327
328 PHY_WRITE(sc, 31, 0x0001);
329 PHY_WRITE(sc, 27, 0x01e0);
330 PHY_WRITE(sc, 31, 0x0002);
331 PHY_WRITE(sc, 27, 0xeb8e);
332 PHY_WRITE(sc, 31, 0x0000);
333 PHY_WRITE(sc, 30, 0x005e);
334 PHY_WRITE(sc, 9, 0x0700);
335
336 DELAY(50);
337 }
338
339 static void
ip1000phy_reset(struct mii_softc * sc)340 ip1000phy_reset(struct mii_softc *sc)
341 {
342 uint32_t reg;
343
344 mii_phy_reset(sc);
345
346 /* clear autoneg/full-duplex as we don't want it after reset */
347 reg = PHY_READ(sc, IP1000PHY_MII_BMCR);
348 reg &= ~(IP1000PHY_BMCR_AUTOEN | IP1000PHY_BMCR_FDX);
349 PHY_WRITE(sc, MII_BMCR, reg);
350
351 if ((sc->mii_flags & MIIF_PHYPRIV0) != 0)
352 ip1000phy_load_dspcode(sc);
353 }
354