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