xref: /openbsd-src/sys/dev/mii/rdcphy.c (revision ec9064e1d0c090dfda00cd22ab8d1db4f00fa07f)
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