1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2006 Benno Rice. All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27 #include <sys/cdefs.h>
28 /*
29 * Driver for the SMSC LAN8710A
30 */
31
32 #include <sys/param.h>
33 #include <sys/systm.h>
34 #include <sys/kernel.h>
35 #include <sys/socket.h>
36 #include <sys/errno.h>
37 #include <sys/module.h>
38 #include <sys/bus.h>
39 #include <sys/malloc.h>
40
41 #include <machine/bus.h>
42
43 #include <net/if.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 "miibus_if.h"
51
52 static int smscphy_probe(device_t);
53 static int smscphy_attach(device_t);
54
55 static int smscphy_service(struct mii_softc *, struct mii_data *, int);
56 static void smscphy_auto(struct mii_softc *, int);
57 static void smscphy_status(struct mii_softc *);
58
59 static device_method_t smscphy_methods[] = {
60 /* device interface */
61 DEVMETHOD(device_probe, smscphy_probe),
62 DEVMETHOD(device_attach, smscphy_attach),
63 DEVMETHOD(device_detach, mii_phy_detach),
64 DEVMETHOD(device_shutdown, bus_generic_shutdown),
65 DEVMETHOD_END
66 };
67
68 static driver_t smscphy_driver = {
69 "smscphy",
70 smscphy_methods,
71 sizeof(struct mii_softc)
72 };
73
74 DRIVER_MODULE(smscphy, miibus, smscphy_driver, 0, 0);
75
76 static const struct mii_phydesc smscphys[] = {
77 MII_PHY_DESC(SMC, LAN8710A),
78 MII_PHY_DESC(SMC, LAN8700),
79 MII_PHY_END
80 };
81
82 static const struct mii_phy_funcs smscphy_funcs = {
83 smscphy_service,
84 smscphy_status,
85 mii_phy_reset
86 };
87
88 static int
smscphy_probe(device_t dev)89 smscphy_probe(device_t dev)
90 {
91
92 return (mii_phy_dev_probe(dev, smscphys, BUS_PROBE_DEFAULT));
93 }
94
95 static int
smscphy_attach(device_t dev)96 smscphy_attach(device_t dev)
97 {
98 struct mii_softc *sc;
99 const struct mii_phy_funcs *mpf;
100
101 sc = device_get_softc(dev);
102 mpf = &smscphy_funcs;
103 mii_phy_dev_attach(dev, MIIF_NOISOLATE | MIIF_NOMANPAUSE, mpf, 1);
104 mii_phy_setmedia(sc);
105
106 return (0);
107 }
108
109 static int
smscphy_service(struct mii_softc * sc,struct mii_data * mii,int cmd)110 smscphy_service(struct mii_softc *sc, struct mii_data *mii, int cmd)
111 {
112 struct ifmedia_entry *ife;
113 int reg;
114
115 ife = mii->mii_media.ifm_cur;
116
117 switch (cmd) {
118 case MII_POLLSTAT:
119 break;
120
121 case MII_MEDIACHG:
122 switch (IFM_SUBTYPE(ife->ifm_media)) {
123 case IFM_AUTO:
124 smscphy_auto(sc, ife->ifm_media);
125 break;
126
127 default:
128 mii_phy_setmedia(sc);
129 break;
130 }
131
132 break;
133
134 case MII_TICK:
135 if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO) {
136 break;
137 }
138
139 /* I have no idea why BMCR_ISO gets set. */
140 reg = PHY_READ(sc, MII_BMCR);
141 if (reg & BMCR_ISO) {
142 PHY_WRITE(sc, MII_BMCR, reg & ~BMCR_ISO);
143 }
144
145 reg = PHY_READ(sc, MII_BMSR) | PHY_READ(sc, MII_BMSR);
146 if (reg & BMSR_LINK) {
147 sc->mii_ticks = 0;
148 break;
149 }
150
151 if (++sc->mii_ticks <= MII_ANEGTICKS) {
152 break;
153 }
154
155 sc->mii_ticks = 0;
156 PHY_RESET(sc);
157 smscphy_auto(sc, ife->ifm_media);
158 break;
159 }
160
161 /* Update the media status. */
162 PHY_STATUS(sc);
163
164 /* Callback if something changed. */
165 mii_phy_update(sc, cmd);
166 return (0);
167 }
168
169 static void
smscphy_auto(struct mii_softc * sc,int media)170 smscphy_auto(struct mii_softc *sc, int media)
171 {
172 uint16_t anar;
173
174 anar = BMSR_MEDIA_TO_ANAR(sc->mii_capabilities) | ANAR_CSMA;
175 if ((media & IFM_FLOW) != 0 || (sc->mii_flags & MIIF_FORCEPAUSE) != 0)
176 anar |= ANAR_FC;
177 PHY_WRITE(sc, MII_ANAR, anar);
178 /* Apparently this helps. */
179 anar = PHY_READ(sc, MII_ANAR);
180 PHY_WRITE(sc, MII_BMCR, BMCR_AUTOEN | BMCR_STARTNEG);
181 }
182
183 static void
smscphy_status(struct mii_softc * sc)184 smscphy_status(struct mii_softc *sc)
185 {
186 struct mii_data *mii;
187 uint32_t bmcr, bmsr, status;
188
189 mii = sc->mii_pdata;
190 mii->mii_media_status = IFM_AVALID;
191 mii->mii_media_active = IFM_ETHER;
192
193 bmsr = PHY_READ(sc, MII_BMSR) | PHY_READ(sc, MII_BMSR);
194 if ((bmsr & BMSR_LINK) != 0)
195 mii->mii_media_status |= IFM_ACTIVE;
196
197 bmcr = PHY_READ(sc, MII_BMCR);
198 if ((bmcr & BMCR_ISO) != 0) {
199 mii->mii_media_active |= IFM_NONE;
200 mii->mii_media_status = 0;
201 return;
202 }
203
204 if ((bmcr & BMCR_LOOP) != 0)
205 mii->mii_media_active |= IFM_LOOP;
206
207 if ((bmcr & BMCR_AUTOEN) != 0) {
208 if ((bmsr & BMSR_ACOMP) == 0) {
209 /* Erg, still trying, I guess... */
210 mii->mii_media_active |= IFM_NONE;
211 return;
212 }
213 }
214
215 status = PHY_READ(sc, 0x1F);
216 if (status & 0x0008)
217 mii->mii_media_active |= IFM_100_TX;
218 else
219 mii->mii_media_active |= IFM_10_T;
220 if (status & 0x0010)
221 mii->mii_media_active |= IFM_FDX | mii_phy_flowstatus(sc);
222 else
223 mii->mii_media_active |= IFM_HDX;
224 }
225