1e6713fe5SPyun YongHyeon /*-
2*4d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause
3718cf2ccSPedro F. Giffuni *
4e6713fe5SPyun YongHyeon * Copyright (c) 2010, Pyun YongHyeon <yongari@FreeBSD.org>
5e6713fe5SPyun YongHyeon * All rights reserved.
6e6713fe5SPyun YongHyeon *
7e6713fe5SPyun YongHyeon * Redistribution and use in source and binary forms, with or without
8e6713fe5SPyun YongHyeon * modification, are permitted provided that the following conditions
9e6713fe5SPyun YongHyeon * are met:
10e6713fe5SPyun YongHyeon * 1. Redistributions of source code must retain the above copyright
11e6713fe5SPyun YongHyeon * notice unmodified, this list of conditions, and the following
12e6713fe5SPyun YongHyeon * disclaimer.
13e6713fe5SPyun YongHyeon * 2. Redistributions in binary form must reproduce the above copyright
14e6713fe5SPyun YongHyeon * notice, this list of conditions and the following disclaimer in the
15e6713fe5SPyun YongHyeon * documentation and/or other materials provided with the distribution.
16e6713fe5SPyun YongHyeon *
17e6713fe5SPyun YongHyeon * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18e6713fe5SPyun YongHyeon * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19e6713fe5SPyun YongHyeon * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20e6713fe5SPyun YongHyeon * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21e6713fe5SPyun YongHyeon * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22e6713fe5SPyun YongHyeon * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23e6713fe5SPyun YongHyeon * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24e6713fe5SPyun YongHyeon * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25e6713fe5SPyun YongHyeon * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26e6713fe5SPyun YongHyeon * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27e6713fe5SPyun YongHyeon * SUCH DAMAGE.
28e6713fe5SPyun YongHyeon */
29e6713fe5SPyun YongHyeon
30e6713fe5SPyun YongHyeon #include <sys/cdefs.h>
31e6713fe5SPyun YongHyeon /*
32e6713fe5SPyun YongHyeon * Driver for the RDC Semiconductor R6040 10/100 PHY.
33e6713fe5SPyun YongHyeon */
34e6713fe5SPyun YongHyeon
35e6713fe5SPyun YongHyeon #include <sys/param.h>
36e6713fe5SPyun YongHyeon #include <sys/systm.h>
37e6713fe5SPyun YongHyeon #include <sys/kernel.h>
38e6713fe5SPyun YongHyeon #include <sys/module.h>
39e6713fe5SPyun YongHyeon #include <sys/socket.h>
40e6713fe5SPyun YongHyeon #include <sys/bus.h>
41e6713fe5SPyun YongHyeon
42e6713fe5SPyun YongHyeon #include <net/if.h>
43e6713fe5SPyun YongHyeon #include <net/if_media.h>
44e6713fe5SPyun YongHyeon
45e6713fe5SPyun YongHyeon #include <dev/mii/mii.h>
46e6713fe5SPyun YongHyeon #include <dev/mii/miivar.h>
47e6713fe5SPyun YongHyeon #include "miidevs.h"
48e6713fe5SPyun YongHyeon
49e6713fe5SPyun YongHyeon #include <dev/mii/rdcphyreg.h>
50e6713fe5SPyun YongHyeon
51e6713fe5SPyun YongHyeon #include "miibus_if.h"
52e6713fe5SPyun YongHyeon
53e6713fe5SPyun YongHyeon static device_probe_t rdcphy_probe;
54e6713fe5SPyun YongHyeon static device_attach_t rdcphy_attach;
55e6713fe5SPyun YongHyeon
56e6713fe5SPyun YongHyeon struct rdcphy_softc {
57e6713fe5SPyun YongHyeon struct mii_softc mii_sc;
58e6713fe5SPyun YongHyeon int mii_link_tick;
59e6713fe5SPyun YongHyeon #define RDCPHY_MANNEG_TICK 3
60e6713fe5SPyun YongHyeon };
61e6713fe5SPyun YongHyeon
62e6713fe5SPyun YongHyeon static device_method_t rdcphy_methods[] = {
63e6713fe5SPyun YongHyeon /* device interface */
64e6713fe5SPyun YongHyeon DEVMETHOD(device_probe, rdcphy_probe),
65e6713fe5SPyun YongHyeon DEVMETHOD(device_attach, rdcphy_attach),
66e6713fe5SPyun YongHyeon DEVMETHOD(device_detach, mii_phy_detach),
67e6713fe5SPyun YongHyeon DEVMETHOD(device_shutdown, bus_generic_shutdown),
68848e30ffSMarius Strobl DEVMETHOD_END
69e6713fe5SPyun YongHyeon };
70e6713fe5SPyun YongHyeon
71e6713fe5SPyun YongHyeon static driver_t rdcphy_driver = {
72e6713fe5SPyun YongHyeon "rdcphy",
73e6713fe5SPyun YongHyeon rdcphy_methods,
74e6713fe5SPyun YongHyeon sizeof(struct rdcphy_softc)
75e6713fe5SPyun YongHyeon };
76e6713fe5SPyun YongHyeon
77f438c2ffSJohn Baldwin DRIVER_MODULE(rdcphy, miibus, rdcphy_driver, 0, 0);
78e6713fe5SPyun YongHyeon
79e6713fe5SPyun YongHyeon static int rdcphy_service(struct mii_softc *, struct mii_data *, int);
80e6713fe5SPyun YongHyeon static void rdcphy_status(struct mii_softc *);
81e6713fe5SPyun YongHyeon
82e6713fe5SPyun YongHyeon static const struct mii_phydesc rdcphys[] = {
83e6713fe5SPyun YongHyeon MII_PHY_DESC(RDC, R6040),
845586515aSKevin Lo MII_PHY_DESC(RDC, R6040_2),
85e6713fe5SPyun YongHyeon MII_PHY_END
86e6713fe5SPyun YongHyeon };
87e6713fe5SPyun YongHyeon
883fcb7a53SMarius Strobl static const struct mii_phy_funcs rdcphy_funcs = {
893fcb7a53SMarius Strobl rdcphy_service,
903fcb7a53SMarius Strobl rdcphy_status,
913fcb7a53SMarius Strobl mii_phy_reset
923fcb7a53SMarius Strobl };
933fcb7a53SMarius Strobl
94e6713fe5SPyun YongHyeon static int
rdcphy_probe(device_t dev)95e6713fe5SPyun YongHyeon rdcphy_probe(device_t dev)
96e6713fe5SPyun YongHyeon {
97e6713fe5SPyun YongHyeon
98e6713fe5SPyun YongHyeon return (mii_phy_dev_probe(dev, rdcphys, BUS_PROBE_DEFAULT));
99e6713fe5SPyun YongHyeon }
100e6713fe5SPyun YongHyeon
101e6713fe5SPyun YongHyeon static int
rdcphy_attach(device_t dev)102e6713fe5SPyun YongHyeon rdcphy_attach(device_t dev)
103e6713fe5SPyun YongHyeon {
104e6713fe5SPyun YongHyeon
1053fcb7a53SMarius Strobl mii_phy_dev_attach(dev, MIIF_NOMANPAUSE, &rdcphy_funcs, 1);
106e6713fe5SPyun YongHyeon return (0);
107e6713fe5SPyun YongHyeon }
108e6713fe5SPyun YongHyeon
109e6713fe5SPyun YongHyeon static int
rdcphy_service(struct mii_softc * sc,struct mii_data * mii,int cmd)110e6713fe5SPyun YongHyeon rdcphy_service(struct mii_softc *sc, struct mii_data *mii, int cmd)
111e6713fe5SPyun YongHyeon {
112e6713fe5SPyun YongHyeon struct rdcphy_softc *rsc;
113e6713fe5SPyun YongHyeon struct ifmedia_entry *ife;
114e6713fe5SPyun YongHyeon
115e6713fe5SPyun YongHyeon rsc = (struct rdcphy_softc *)sc;
116e6713fe5SPyun YongHyeon ife = mii->mii_media.ifm_cur;
117e6713fe5SPyun YongHyeon
118e6713fe5SPyun YongHyeon switch (cmd) {
119e6713fe5SPyun YongHyeon case MII_POLLSTAT:
120e6713fe5SPyun YongHyeon break;
121e6713fe5SPyun YongHyeon
122e6713fe5SPyun YongHyeon case MII_MEDIACHG:
123e6713fe5SPyun YongHyeon mii_phy_setmedia(sc);
124e6713fe5SPyun YongHyeon switch (IFM_SUBTYPE(ife->ifm_media)) {
125e6713fe5SPyun YongHyeon case IFM_100_TX:
126e6713fe5SPyun YongHyeon case IFM_10_T:
127e6713fe5SPyun YongHyeon /*
128e6713fe5SPyun YongHyeon * Report fake lost link event to parent
129e6713fe5SPyun YongHyeon * driver. This will stop MAC of parent
130e6713fe5SPyun YongHyeon * driver and make it possible to reconfigure
131e6713fe5SPyun YongHyeon * MAC after completion of link establishment.
132e6713fe5SPyun YongHyeon * Note, the parent MAC seems to require
133e6713fe5SPyun YongHyeon * restarting MAC when underlying any PHY
134e6713fe5SPyun YongHyeon * configuration was changed even if the
135e6713fe5SPyun YongHyeon * resolved speed/duplex was not changed at
136e6713fe5SPyun YongHyeon * all.
137e6713fe5SPyun YongHyeon */
138e6713fe5SPyun YongHyeon mii->mii_media_status = 0;
139e6713fe5SPyun YongHyeon mii->mii_media_active = IFM_ETHER | IFM_NONE;
140e6713fe5SPyun YongHyeon rsc->mii_link_tick = RDCPHY_MANNEG_TICK;
141e6713fe5SPyun YongHyeon /* Immediately report link down. */
142e6713fe5SPyun YongHyeon mii_phy_update(sc, MII_MEDIACHG);
143e6713fe5SPyun YongHyeon return (0);
144e6713fe5SPyun YongHyeon default:
145e6713fe5SPyun YongHyeon break;
146e6713fe5SPyun YongHyeon }
147e6713fe5SPyun YongHyeon break;
148e6713fe5SPyun YongHyeon
149e6713fe5SPyun YongHyeon case MII_TICK:
150e6713fe5SPyun YongHyeon if (mii_phy_tick(sc) == EJUSTRETURN)
151e6713fe5SPyun YongHyeon return (0);
152e6713fe5SPyun YongHyeon if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO) {
153e6713fe5SPyun YongHyeon /*
154e6713fe5SPyun YongHyeon * It seems the PHY hardware does not correctly
155e6713fe5SPyun YongHyeon * report link status changes when manual link
156e6713fe5SPyun YongHyeon * configuration is in progress. It is also
157e6713fe5SPyun YongHyeon * possible for the PHY to complete establishing
158e6713fe5SPyun YongHyeon * a link within one second such that mii(4)
159e6713fe5SPyun YongHyeon * did not notice the link change. To workaround
160e6713fe5SPyun YongHyeon * the issue, emulate lost link event and wait
161e6713fe5SPyun YongHyeon * for 3 seconds when manual link configuration
162e6713fe5SPyun YongHyeon * is in progress. 3 seconds would be long
163e6713fe5SPyun YongHyeon * enough to absorb transient link flips.
164e6713fe5SPyun YongHyeon */
165e6713fe5SPyun YongHyeon if (rsc->mii_link_tick > 0) {
166e6713fe5SPyun YongHyeon rsc->mii_link_tick--;
167e6713fe5SPyun YongHyeon return (0);
168e6713fe5SPyun YongHyeon }
169e6713fe5SPyun YongHyeon }
170e6713fe5SPyun YongHyeon break;
171e6713fe5SPyun YongHyeon }
172e6713fe5SPyun YongHyeon
173e6713fe5SPyun YongHyeon /* Update the media status. */
1743fcb7a53SMarius Strobl PHY_STATUS(sc);
175e6713fe5SPyun YongHyeon
176e6713fe5SPyun YongHyeon /* Callback if something changed. */
177e6713fe5SPyun YongHyeon mii_phy_update(sc, cmd);
178e6713fe5SPyun YongHyeon return (0);
179e6713fe5SPyun YongHyeon }
180e6713fe5SPyun YongHyeon
181e6713fe5SPyun YongHyeon static void
rdcphy_status(struct mii_softc * sc)182e6713fe5SPyun YongHyeon rdcphy_status(struct mii_softc *sc)
183e6713fe5SPyun YongHyeon {
184e6713fe5SPyun YongHyeon struct mii_data *mii;
185e6713fe5SPyun YongHyeon int bmsr, bmcr, physts;
186e6713fe5SPyun YongHyeon
187e6713fe5SPyun YongHyeon mii = sc->mii_pdata;
188e6713fe5SPyun YongHyeon
189e6713fe5SPyun YongHyeon mii->mii_media_status = IFM_AVALID;
190e6713fe5SPyun YongHyeon mii->mii_media_active = IFM_ETHER;
191e6713fe5SPyun YongHyeon
192e6713fe5SPyun YongHyeon bmsr = PHY_READ(sc, MII_BMSR) | PHY_READ(sc, MII_BMSR);
193e6713fe5SPyun YongHyeon physts = PHY_READ(sc, MII_RDCPHY_STATUS);
194e6713fe5SPyun YongHyeon
195e6713fe5SPyun YongHyeon if ((physts & STATUS_LINK_UP) != 0)
196e6713fe5SPyun YongHyeon mii->mii_media_status |= IFM_ACTIVE;
197e6713fe5SPyun YongHyeon
198e6713fe5SPyun YongHyeon bmcr = PHY_READ(sc, MII_BMCR);
199e6713fe5SPyun YongHyeon if ((bmcr & BMCR_ISO) != 0) {
200e6713fe5SPyun YongHyeon mii->mii_media_active |= IFM_NONE;
201e6713fe5SPyun YongHyeon mii->mii_media_status = 0;
202e6713fe5SPyun YongHyeon return;
203e6713fe5SPyun YongHyeon }
204e6713fe5SPyun YongHyeon
205e6713fe5SPyun YongHyeon if ((bmcr & BMCR_LOOP) != 0)
206e6713fe5SPyun YongHyeon mii->mii_media_active |= IFM_LOOP;
207e6713fe5SPyun YongHyeon
208e6713fe5SPyun YongHyeon if ((bmcr & BMCR_AUTOEN) != 0) {
209e6713fe5SPyun YongHyeon if ((bmsr & BMSR_ACOMP) == 0) {
210e6713fe5SPyun YongHyeon /* Erg, still trying, I guess... */
211e6713fe5SPyun YongHyeon mii->mii_media_active |= IFM_NONE;
212e6713fe5SPyun YongHyeon return;
213e6713fe5SPyun YongHyeon }
214e6713fe5SPyun YongHyeon }
215e6713fe5SPyun YongHyeon
216e6713fe5SPyun YongHyeon switch (physts & STATUS_SPEED_MASK) {
217e6713fe5SPyun YongHyeon case STATUS_SPEED_100:
218e6713fe5SPyun YongHyeon mii->mii_media_active |= IFM_100_TX;
219e6713fe5SPyun YongHyeon break;
220e6713fe5SPyun YongHyeon case STATUS_SPEED_10:
221e6713fe5SPyun YongHyeon mii->mii_media_active |= IFM_10_T;
222e6713fe5SPyun YongHyeon break;
223e6713fe5SPyun YongHyeon default:
224e6713fe5SPyun YongHyeon mii->mii_media_active |= IFM_NONE;
225e6713fe5SPyun YongHyeon return;
226e6713fe5SPyun YongHyeon }
227e6713fe5SPyun YongHyeon if ((physts & STATUS_FULL_DUPLEX) != 0)
228e6713fe5SPyun YongHyeon mii->mii_media_active |= IFM_FDX | mii_phy_flowstatus(sc);
229e6713fe5SPyun YongHyeon else
230e6713fe5SPyun YongHyeon mii->mii_media_active |= IFM_HDX;
231e6713fe5SPyun YongHyeon }
232