1*ec9064e1Skevlo /* $OpenBSD: rdcphy.c,v 1.5 2022/04/19 03:26:33 kevlo Exp $ */
2b1dea5b2Skevlo /*-
3b1dea5b2Skevlo * Copyright (c) 2010, Pyun YongHyeon <yongari@FreeBSD.org>
4b1dea5b2Skevlo * All rights reserved.
5b1dea5b2Skevlo *
6b1dea5b2Skevlo * Redistribution and use in source and binary forms, with or without
7b1dea5b2Skevlo * modification, are permitted provided that the following conditions
8b1dea5b2Skevlo * are met:
9b1dea5b2Skevlo * 1. Redistributions of source code must retain the above copyright
10b1dea5b2Skevlo * notice unmodified, this list of conditions, and the following
11b1dea5b2Skevlo * disclaimer.
12b1dea5b2Skevlo * 2. Redistributions in binary form must reproduce the above copyright
13b1dea5b2Skevlo * notice, this list of conditions and the following disclaimer in the
14b1dea5b2Skevlo * documentation and/or other materials provided with the distribution.
15b1dea5b2Skevlo *
16b1dea5b2Skevlo * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17b1dea5b2Skevlo * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18b1dea5b2Skevlo * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19b1dea5b2Skevlo * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20b1dea5b2Skevlo * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21b1dea5b2Skevlo * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22b1dea5b2Skevlo * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23b1dea5b2Skevlo * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24b1dea5b2Skevlo * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25b1dea5b2Skevlo * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26b1dea5b2Skevlo * SUCH DAMAGE.
27b1dea5b2Skevlo */
28b1dea5b2Skevlo
29b1dea5b2Skevlo /*
30b1dea5b2Skevlo * Driver for the RDC Semiconductor R6040 10/100 PHY.
31b1dea5b2Skevlo */
32b1dea5b2Skevlo
33b1dea5b2Skevlo #include <sys/param.h>
34b1dea5b2Skevlo #include <sys/systm.h>
35b1dea5b2Skevlo #include <sys/kernel.h>
36b1dea5b2Skevlo #include <sys/device.h>
37b1dea5b2Skevlo #include <sys/socket.h>
38b1dea5b2Skevlo
39b1dea5b2Skevlo #include <net/if.h>
400deb6685Smpi #include <net/if_var.h>
41b1dea5b2Skevlo #include <net/if_media.h>
42b1dea5b2Skevlo
43b1dea5b2Skevlo #include <dev/mii/mii.h>
44b1dea5b2Skevlo #include <dev/mii/miivar.h>
45b1dea5b2Skevlo #include <dev/mii/miidevs.h>
46b1dea5b2Skevlo
47b1dea5b2Skevlo #define MII_RDCPHY_DEBUG 0x11
48b1dea5b2Skevlo #define DEBUG_JABBER_DIS 0x0040
49b1dea5b2Skevlo #define DEBUG_LOOP_BACK_10MBPS 0x0400
50b1dea5b2Skevlo
51b1dea5b2Skevlo #define MII_RDCPHY_CTRL 0x14
52b1dea5b2Skevlo #define CTRL_SQE_ENB 0x0100
53b1dea5b2Skevlo #define CTRL_NEG_POLARITY 0x0400
54b1dea5b2Skevlo #define CTRL_AUTO_POLARITY 0x0800
55b1dea5b2Skevlo #define CTRL_MDIXSEL_RX 0x2000
56b1dea5b2Skevlo #define CTRL_MDIXSEL_TX 0x4000
57b1dea5b2Skevlo #define CTRL_AUTO_MDIX_DIS 0x8000
58b1dea5b2Skevlo
59b1dea5b2Skevlo #define MII_RDCPHY_CTRL2 0x15
60b1dea5b2Skevlo #define CTRL2_LED_DUPLEX 0x0000
61b1dea5b2Skevlo #define CTRL2_LED_DUPLEX_COL 0x0008
62b1dea5b2Skevlo #define CTRL2_LED_ACT 0x0010
63b1dea5b2Skevlo #define CTRL2_LED_SPEED_ACT 0x0018
64b1dea5b2Skevlo #define CTRL2_LED_BLK_100MBPS_DIS 0x0020
65b1dea5b2Skevlo #define CTRL2_LED_BLK_10MBPS_DIS 0x0040
66b1dea5b2Skevlo #define CTRL2_LED_BLK_LINK_ACT_DIS 0x0080
67b1dea5b2Skevlo #define CTRL2_SDT_THRESH_MASK 0x3E00
68b1dea5b2Skevlo #define CTRL2_TIMING_ERR_SEL 0x4000
69b1dea5b2Skevlo #define CTRL2_LED_BLK_80MS 0x8000
70b1dea5b2Skevlo #define CTRL2_LED_BLK_160MS 0x0000
71b1dea5b2Skevlo #define CTRL2_LED_MASK 0x0018
72b1dea5b2Skevlo
73b1dea5b2Skevlo #define MII_RDCPHY_STATUS 0x16
74b1dea5b2Skevlo #define STATUS_AUTO_MDIX_RX 0x0200
75b1dea5b2Skevlo #define STATUS_AUTO_MDIX_TX 0x0400
76b1dea5b2Skevlo #define STATUS_NEG_POLARITY 0x0800
77b1dea5b2Skevlo #define STATUS_FULL_DUPLEX 0x1000
78b1dea5b2Skevlo #define STATUS_SPEED_10 0x0000
79b1dea5b2Skevlo #define STATUS_SPEED_100 0x2000
80b1dea5b2Skevlo #define STATUS_SPEED_MASK 0x6000
81b1dea5b2Skevlo #define STATUS_LINK_UP 0x8000
82b1dea5b2Skevlo
83b1dea5b2Skevlo /* Analog test register 2 */
84b1dea5b2Skevlo #define MII_RDCPHY_TEST2 0x1A
85b1dea5b2Skevlo #define TEST2_PWR_DOWN 0x0200
86b1dea5b2Skevlo
87b1dea5b2Skevlo struct rdcphy_softc {
88b1dea5b2Skevlo struct mii_softc sc_mii;
89b1dea5b2Skevlo int mii_link_tick;
90b1dea5b2Skevlo #define RDCPHY_MANNEG_TICK 3
91b1dea5b2Skevlo };
92b1dea5b2Skevlo
93b1dea5b2Skevlo int rdcphy_service(struct mii_softc *, struct mii_data *, int);
94b1dea5b2Skevlo void rdcphy_attach(struct device *, struct device *, void *);
95b1dea5b2Skevlo int rdcphy_match(struct device *, void *, void *);
96b1dea5b2Skevlo void rdcphy_status(struct mii_softc *);
97b1dea5b2Skevlo
98b1dea5b2Skevlo const struct mii_phy_funcs rdcphy_funcs = {
99b1dea5b2Skevlo rdcphy_service, rdcphy_status, mii_phy_reset,
100b1dea5b2Skevlo };
101b1dea5b2Skevlo
102b1dea5b2Skevlo static const struct mii_phydesc rdcphys[] = {
103b1dea5b2Skevlo { MII_OUI_RDC, MII_MODEL_RDC_R6040,
104b1dea5b2Skevlo MII_STR_RDC_R6040 },
105*ec9064e1Skevlo { MII_OUI_RDC, MII_MODEL_RDC_R6040_2,
106*ec9064e1Skevlo MII_STR_RDC_R6040_2 },
107b1dea5b2Skevlo { 0, 0,
108b1dea5b2Skevlo NULL },
109b1dea5b2Skevlo };
110b1dea5b2Skevlo
111471aeecfSnaddy const struct cfattach rdcphy_ca = {
112b1dea5b2Skevlo sizeof(struct rdcphy_softc), rdcphy_match, rdcphy_attach,
113fa9fb3edSderaadt mii_phy_detach
114b1dea5b2Skevlo };
115b1dea5b2Skevlo
116b1dea5b2Skevlo struct cfdriver rdcphy_cd = {
117b1dea5b2Skevlo NULL, "rdcphy", DV_DULL
118b1dea5b2Skevlo };
119b1dea5b2Skevlo
120b1dea5b2Skevlo int
rdcphy_match(struct device * parent,void * match,void * aux)121b1dea5b2Skevlo rdcphy_match(struct device *parent, void *match, void *aux)
122b1dea5b2Skevlo {
123b1dea5b2Skevlo struct mii_attach_args *ma = aux;
124b1dea5b2Skevlo
125b1dea5b2Skevlo if (mii_phy_match(ma, rdcphys) != NULL)
126b1dea5b2Skevlo return (10);
127b1dea5b2Skevlo
128b1dea5b2Skevlo return (0);
129b1dea5b2Skevlo }
130b1dea5b2Skevlo
131b1dea5b2Skevlo void
rdcphy_attach(struct device * parent,struct device * self,void * aux)132b1dea5b2Skevlo rdcphy_attach(struct device *parent, struct device *self, void *aux)
133b1dea5b2Skevlo {
134b1dea5b2Skevlo struct rdcphy_softc *sc = (struct rdcphy_softc *)self;
135b1dea5b2Skevlo struct mii_attach_args *ma = aux;
136b1dea5b2Skevlo struct mii_data *mii = ma->mii_data;
137b1dea5b2Skevlo const struct mii_phydesc *mpd;
138b1dea5b2Skevlo
139b1dea5b2Skevlo mpd = mii_phy_match(ma, rdcphys);
140b1dea5b2Skevlo printf(": %s, rev. %d\n", mpd->mpd_name, MII_REV(ma->mii_id2));
141b1dea5b2Skevlo
142b1dea5b2Skevlo sc->sc_mii.mii_inst = mii->mii_instance;
143b1dea5b2Skevlo sc->sc_mii.mii_phy = ma->mii_phyno;
144b1dea5b2Skevlo sc->sc_mii.mii_funcs = &rdcphy_funcs;
145b1dea5b2Skevlo sc->sc_mii.mii_pdata = mii;
146b1dea5b2Skevlo sc->sc_mii.mii_flags = ma->mii_flags;
147b1dea5b2Skevlo
148b1dea5b2Skevlo PHY_RESET(&sc->sc_mii);
149b1dea5b2Skevlo
150b1dea5b2Skevlo sc->sc_mii.mii_capabilities =
151b1dea5b2Skevlo PHY_READ(&sc->sc_mii, MII_BMSR) & ma->mii_capmask;
152b1dea5b2Skevlo if (sc->sc_mii.mii_capabilities & BMSR_EXTSTAT)
153b1dea5b2Skevlo sc->sc_mii.mii_extcapabilities =
154b1dea5b2Skevlo PHY_READ(&sc->sc_mii, MII_EXTSR);
155b1dea5b2Skevlo
156b1dea5b2Skevlo if (sc->sc_mii.mii_capabilities & BMSR_MEDIAMASK)
157b1dea5b2Skevlo mii_phy_add_media(&sc->sc_mii);
158b1dea5b2Skevlo }
159b1dea5b2Skevlo
160b1dea5b2Skevlo int
rdcphy_service(struct mii_softc * self,struct mii_data * mii,int cmd)161b1dea5b2Skevlo rdcphy_service(struct mii_softc *self, struct mii_data *mii, int cmd)
162b1dea5b2Skevlo {
163b1dea5b2Skevlo struct rdcphy_softc *sc = (struct rdcphy_softc *)self;
164b1dea5b2Skevlo struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
165b1dea5b2Skevlo int reg;
166b1dea5b2Skevlo
167b1dea5b2Skevlo if ((sc->sc_mii.mii_dev.dv_flags & DVF_ACTIVE) == 0)
168b1dea5b2Skevlo return (ENXIO);
169b1dea5b2Skevlo
170b1dea5b2Skevlo switch (cmd) {
171b1dea5b2Skevlo case MII_POLLSTAT:
172b1dea5b2Skevlo /*
173b1dea5b2Skevlo * If we're not polling our PHY instance, just return.
174b1dea5b2Skevlo */
175b1dea5b2Skevlo if (IFM_INST(ife->ifm_media) != sc->sc_mii.mii_inst)
176b1dea5b2Skevlo return (0);
177b1dea5b2Skevlo break;
178b1dea5b2Skevlo
179b1dea5b2Skevlo case MII_MEDIACHG:
180b1dea5b2Skevlo /*
181b1dea5b2Skevlo * If the media indicates a different PHY instance,
182b1dea5b2Skevlo * isolate ourselves.
183b1dea5b2Skevlo */
184b1dea5b2Skevlo if (IFM_INST(ife->ifm_media) != sc->sc_mii.mii_inst) {
185b1dea5b2Skevlo reg = PHY_READ(&sc->sc_mii, MII_BMCR);
186b1dea5b2Skevlo PHY_WRITE(&sc->sc_mii, MII_BMCR, reg | BMCR_ISO);
187b1dea5b2Skevlo return (0);
188b1dea5b2Skevlo }
189b1dea5b2Skevlo
190b1dea5b2Skevlo /*
191b1dea5b2Skevlo * If the interface is not up, don't do anything.
192b1dea5b2Skevlo */
193b1dea5b2Skevlo if ((mii->mii_ifp->if_flags & IFF_UP) == 0)
194b1dea5b2Skevlo break;
195b1dea5b2Skevlo
196b1dea5b2Skevlo mii_phy_setmedia(&sc->sc_mii);
197b1dea5b2Skevlo switch (IFM_SUBTYPE(ife->ifm_media)) {
198b1dea5b2Skevlo case IFM_100_TX:
199b1dea5b2Skevlo case IFM_10_T:
200b1dea5b2Skevlo /*
201b1dea5b2Skevlo * Report fake lost link event to parent
202b1dea5b2Skevlo * driver. This will stop MAC of parent
203b1dea5b2Skevlo * driver and make it possible to reconfigure
204b1dea5b2Skevlo * MAC after completion of link establishment.
205b1dea5b2Skevlo * Note, the parent MAC seems to require
206b1dea5b2Skevlo * restarting MAC when underlying any PHY
207b1dea5b2Skevlo * configuration was changed even if the
208b1dea5b2Skevlo * resolved speed/duplex was not changed at
209b1dea5b2Skevlo * all.
210b1dea5b2Skevlo */
211b1dea5b2Skevlo mii->mii_media_status = 0;
212b1dea5b2Skevlo mii->mii_media_active = IFM_ETHER | IFM_NONE;
213b1dea5b2Skevlo sc->mii_link_tick = RDCPHY_MANNEG_TICK;
214b1dea5b2Skevlo /* Immediately report link down. */
215b1dea5b2Skevlo mii_phy_update(&sc->sc_mii, MII_MEDIACHG);
216b1dea5b2Skevlo return (0);
217b1dea5b2Skevlo default:
218b1dea5b2Skevlo break;
219b1dea5b2Skevlo }
220b1dea5b2Skevlo break;
221b1dea5b2Skevlo
222b1dea5b2Skevlo case MII_TICK:
223b1dea5b2Skevlo /*
224b1dea5b2Skevlo * If we're not currently selected, just return.
225b1dea5b2Skevlo */
226b1dea5b2Skevlo if (IFM_INST(ife->ifm_media) != sc->sc_mii.mii_inst)
227b1dea5b2Skevlo return (0);
228b1dea5b2Skevlo
229b1dea5b2Skevlo if (mii_phy_tick(&sc->sc_mii) == EJUSTRETURN)
230b1dea5b2Skevlo return (0);
231b1dea5b2Skevlo
232b1dea5b2Skevlo if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO) {
233b1dea5b2Skevlo /*
234b1dea5b2Skevlo * It seems the PHY hardware does not correctly
235b1dea5b2Skevlo * report link status changes when manual link
236b1dea5b2Skevlo * configuration is in progress. It is also
237b1dea5b2Skevlo * possible for the PHY to complete establishing
238b1dea5b2Skevlo * a link within one second such that mii(4)
239b1dea5b2Skevlo * did not notice the link change. To workaround
240b1dea5b2Skevlo * the issue, emulate lost link event and wait
241b1dea5b2Skevlo * for 3 seconds when manual link configuration
242b1dea5b2Skevlo * is in progress. 3 seconds would be long
243b1dea5b2Skevlo * enough to absorb transient link flips.
244b1dea5b2Skevlo */
245b1dea5b2Skevlo if (sc->mii_link_tick > 0) {
246b1dea5b2Skevlo sc->mii_link_tick--;
247b1dea5b2Skevlo return (0);
248b1dea5b2Skevlo }
249b1dea5b2Skevlo }
250b1dea5b2Skevlo break;
251b1dea5b2Skevlo }
252b1dea5b2Skevlo
253b1dea5b2Skevlo /* Update the media status. */
254b1dea5b2Skevlo mii_phy_status(&sc->sc_mii);
255b1dea5b2Skevlo
256b1dea5b2Skevlo /* Callback if something changed. */
257b1dea5b2Skevlo mii_phy_update(&sc->sc_mii, cmd);
258b1dea5b2Skevlo return (0);
259b1dea5b2Skevlo }
260b1dea5b2Skevlo
261b1dea5b2Skevlo void
rdcphy_status(struct mii_softc * sc)262b1dea5b2Skevlo rdcphy_status(struct mii_softc *sc)
263b1dea5b2Skevlo {
264b1dea5b2Skevlo struct mii_data *mii = sc->mii_pdata;
265b1dea5b2Skevlo struct ifmedia_entry *ife;
266b1dea5b2Skevlo int bmsr, bmcr, physts;
267b1dea5b2Skevlo
268b1dea5b2Skevlo ife = mii->mii_media.ifm_cur;
269b1dea5b2Skevlo
270b1dea5b2Skevlo mii->mii_media_status = IFM_AVALID;
271b1dea5b2Skevlo mii->mii_media_active = IFM_ETHER;
272b1dea5b2Skevlo
273b1dea5b2Skevlo bmsr = PHY_READ(sc, MII_BMSR) | PHY_READ(sc, MII_BMSR);
274b1dea5b2Skevlo physts = PHY_READ(sc, MII_RDCPHY_STATUS);
275b1dea5b2Skevlo
276b1dea5b2Skevlo if (physts & STATUS_LINK_UP)
277b1dea5b2Skevlo mii->mii_media_status |= IFM_ACTIVE;
278b1dea5b2Skevlo
279b1dea5b2Skevlo bmcr = PHY_READ(sc, MII_BMCR);
280b1dea5b2Skevlo if (bmcr & BMCR_ISO) {
281b1dea5b2Skevlo mii->mii_media_active |= IFM_NONE;
282b1dea5b2Skevlo mii->mii_media_status = 0;
283b1dea5b2Skevlo return;
284b1dea5b2Skevlo }
285b1dea5b2Skevlo
286b1dea5b2Skevlo if (bmcr & BMCR_LOOP)
287b1dea5b2Skevlo mii->mii_media_active |= IFM_LOOP;
288b1dea5b2Skevlo
289b1dea5b2Skevlo if (bmcr & BMCR_AUTOEN) {
290b1dea5b2Skevlo if (!(bmsr & BMSR_ACOMP)) {
291b1dea5b2Skevlo /* Erg, still trying, I guess... */
292b1dea5b2Skevlo mii->mii_media_active |= IFM_NONE;
293b1dea5b2Skevlo return;
294b1dea5b2Skevlo }
295b1dea5b2Skevlo }
296b1dea5b2Skevlo
297b1dea5b2Skevlo switch (physts & STATUS_SPEED_MASK) {
298b1dea5b2Skevlo case STATUS_SPEED_100:
299b1dea5b2Skevlo mii->mii_media_active |= IFM_100_TX;
300b1dea5b2Skevlo break;
301b1dea5b2Skevlo case STATUS_SPEED_10:
302b1dea5b2Skevlo mii->mii_media_active |= IFM_10_T;
303b1dea5b2Skevlo break;
304b1dea5b2Skevlo default:
305b1dea5b2Skevlo mii->mii_media_active |= IFM_NONE;
306b1dea5b2Skevlo return;
307b1dea5b2Skevlo }
308b1dea5b2Skevlo if ((physts & STATUS_FULL_DUPLEX) != 0)
309b1dea5b2Skevlo mii->mii_media_active |= IFM_FDX |
310b1dea5b2Skevlo mii_phy_flowstatus(sc);
311b1dea5b2Skevlo else
312b1dea5b2Skevlo mii->mii_media_active |= IFM_HDX;
313b1dea5b2Skevlo }
314