xref: /freebsd/sys/dev/mii/mv88e151x.c (revision 685dc743)
1adff82eaSLuiz Souza /*-
2b61a5730SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
3adff82eaSLuiz Souza  *
4adff82eaSLuiz Souza  * Copyright (c) 2019 Rubicon Communications, LLC (Netgate)
5adff82eaSLuiz Souza  * All rights reserved.
6adff82eaSLuiz Souza  *
7adff82eaSLuiz Souza  * This code is derived from software contributed to The NetBSD Foundation
8adff82eaSLuiz Souza  * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
9adff82eaSLuiz Souza  * NASA Ames Research Center, and by Frank van der Linden.
10adff82eaSLuiz Souza  *
11adff82eaSLuiz Souza  * Redistribution and use in source and binary forms, with or without
12adff82eaSLuiz Souza  * modification, are permitted provided that the following conditions
13adff82eaSLuiz Souza  * are met:
14adff82eaSLuiz Souza  * 1. Redistributions of source code must retain the above copyright
15adff82eaSLuiz Souza  *    notice, this list of conditions and the following disclaimer.
16adff82eaSLuiz Souza  * 2. Redistributions in binary form must reproduce the above copyright
17adff82eaSLuiz Souza  *    notice, this list of conditions and the following disclaimer in the
18adff82eaSLuiz Souza  *    documentation and/or other materials provided with the distribution.
19adff82eaSLuiz Souza  *
20adff82eaSLuiz Souza  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
21adff82eaSLuiz Souza  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22adff82eaSLuiz Souza  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23adff82eaSLuiz Souza  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
24adff82eaSLuiz Souza  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25adff82eaSLuiz Souza  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26adff82eaSLuiz Souza  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27adff82eaSLuiz Souza  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28adff82eaSLuiz Souza  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29adff82eaSLuiz Souza  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30adff82eaSLuiz Souza  * POSSIBILITY OF SUCH DAMAGE.
31adff82eaSLuiz Souza  */
32adff82eaSLuiz Souza 
33adff82eaSLuiz Souza #include <sys/cdefs.h>
34adff82eaSLuiz Souza /* Driver for the Marvell 88e151x gigabit ethernet PHY. */
35adff82eaSLuiz Souza 
36adff82eaSLuiz Souza #include <sys/param.h>
37adff82eaSLuiz Souza #include <sys/systm.h>
38adff82eaSLuiz Souza #include <sys/kernel.h>
39adff82eaSLuiz Souza #include <sys/socket.h>
40adff82eaSLuiz Souza #include <sys/errno.h>
41adff82eaSLuiz Souza #include <sys/module.h>
42adff82eaSLuiz Souza #include <sys/bus.h>
43adff82eaSLuiz Souza 
44adff82eaSLuiz Souza #include <net/if.h>
45adff82eaSLuiz Souza #include <net/if_media.h>
46adff82eaSLuiz Souza 
47adff82eaSLuiz Souza #include <dev/mii/mii.h>
48adff82eaSLuiz Souza #include <dev/mii/miivar.h>
49adff82eaSLuiz Souza #include <dev/mii/mv88e151xreg.h>
50adff82eaSLuiz Souza #include "miidevs.h"
51adff82eaSLuiz Souza 
52adff82eaSLuiz Souza #include "miibus_if.h"
53adff82eaSLuiz Souza 
54adff82eaSLuiz Souza static int mv88e151x_probe(device_t);
55adff82eaSLuiz Souza static int mv88e151x_attach(device_t);
56adff82eaSLuiz Souza 
57adff82eaSLuiz Souza static device_method_t mv88e151x_methods[] = {
58adff82eaSLuiz Souza 	/* device interface */
59adff82eaSLuiz Souza 	DEVMETHOD(device_probe,		mv88e151x_probe),
60adff82eaSLuiz Souza 	DEVMETHOD(device_attach,	mv88e151x_attach),
61adff82eaSLuiz Souza 	DEVMETHOD(device_detach,	mii_phy_detach),
62adff82eaSLuiz Souza 	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
63adff82eaSLuiz Souza 	DEVMETHOD_END
64adff82eaSLuiz Souza };
65adff82eaSLuiz Souza 
66adff82eaSLuiz Souza static driver_t mv88e151x_driver = {
67adff82eaSLuiz Souza 	"mv88e151x",
68adff82eaSLuiz Souza 	mv88e151x_methods,
69adff82eaSLuiz Souza 	sizeof(struct mii_softc)
70adff82eaSLuiz Souza };
71adff82eaSLuiz Souza 
7251480b48SJohn Baldwin DRIVER_MODULE(mv88e151x, miibus, mv88e151x_driver, 0, 0);
73adff82eaSLuiz Souza 
74adff82eaSLuiz Souza static int mv88e151x_service(struct mii_softc *, struct mii_data *, int);
75adff82eaSLuiz Souza static void mv88e151x_status(struct mii_softc *);
76adff82eaSLuiz Souza 
77adff82eaSLuiz Souza static const struct mii_phydesc mv88e151xphys[] = {
78adff82eaSLuiz Souza 	MII_PHY_DESC(xxMARVELL, E1512),
79adff82eaSLuiz Souza 	MII_PHY_END
80adff82eaSLuiz Souza };
81adff82eaSLuiz Souza 
82adff82eaSLuiz Souza static const struct mii_phy_funcs mv88e151x_funcs = {
83adff82eaSLuiz Souza 	mv88e151x_service,
84adff82eaSLuiz Souza 	mv88e151x_status,
85adff82eaSLuiz Souza 	mii_phy_reset
86adff82eaSLuiz Souza };
87adff82eaSLuiz Souza 
88adff82eaSLuiz Souza static int
mv88e151x_probe(device_t dev)89adff82eaSLuiz Souza mv88e151x_probe(device_t dev)
90adff82eaSLuiz Souza {
91adff82eaSLuiz Souza 
92adff82eaSLuiz Souza 	return (mii_phy_dev_probe(dev, mv88e151xphys, BUS_PROBE_DEFAULT));
93adff82eaSLuiz Souza }
94adff82eaSLuiz Souza 
95adff82eaSLuiz Souza static int
mv88e151x_attach(device_t dev)96adff82eaSLuiz Souza mv88e151x_attach(device_t dev)
97adff82eaSLuiz Souza {
98adff82eaSLuiz Souza 	const struct mii_attach_args *ma;
99adff82eaSLuiz Souza 	struct mii_softc *sc;
100adff82eaSLuiz Souza 	uint32_t cop_cap, cop_extcap;
101adff82eaSLuiz Souza 
102adff82eaSLuiz Souza 	sc = device_get_softc(dev);
103adff82eaSLuiz Souza 	ma = device_get_ivars(dev);
104adff82eaSLuiz Souza 
105adff82eaSLuiz Souza 	mii_phy_dev_attach(dev, MIIF_NOMANPAUSE, &mv88e151x_funcs, 0);
106adff82eaSLuiz Souza 
107adff82eaSLuiz Souza 	PHY_RESET(sc);
108adff82eaSLuiz Souza 	sc->mii_capabilities = PHY_READ(sc, MII_BMSR) & sc->mii_capmask;
109adff82eaSLuiz Souza 	cop_cap = sc->mii_capabilities;
110adff82eaSLuiz Souza 	if (sc->mii_capabilities & BMSR_EXTSTAT) {
111adff82eaSLuiz Souza 		sc->mii_extcapabilities = PHY_READ(sc, MII_EXTSR);
112adff82eaSLuiz Souza 		cop_extcap = sc->mii_extcapabilities;
113adff82eaSLuiz Souza 	}
114adff82eaSLuiz Souza 	device_printf(dev, " ");
115adff82eaSLuiz Souza 	if (MII_MODEL(ma->mii_id2) == MII_MODEL_xxMARVELL_E1512)
116adff82eaSLuiz Souza 		sc->mii_capabilities &= ~BMSR_ANEG;
117adff82eaSLuiz Souza 	mii_phy_add_media(sc);
118adff82eaSLuiz Souza 	if (MII_MODEL(ma->mii_id2) == MII_MODEL_xxMARVELL_E1512) {
119adff82eaSLuiz Souza 		/* Switch the fiber PHY and add the supported media. */
120adff82eaSLuiz Souza 		PHY_WRITE(sc, MV88E151X_PAGE, MV88E151X_PAGE_FIBER);
121adff82eaSLuiz Souza 		PHY_RESET(sc);
122adff82eaSLuiz Souza 		sc->mii_capabilities = PHY_READ(sc, MII_BMSR) & sc->mii_capmask;
123adff82eaSLuiz Souza 		if (sc->mii_capabilities & BMSR_EXTSTAT)
124adff82eaSLuiz Souza 			sc->mii_extcapabilities = PHY_READ(sc, MII_EXTSR);
125adff82eaSLuiz Souza 		sc->mii_flags |= MIIF_NOISOLATE;
126adff82eaSLuiz Souza 		printf(", ");
127adff82eaSLuiz Souza 		mii_phy_add_media(sc);
128adff82eaSLuiz Souza 
129adff82eaSLuiz Souza 		/* Switch back to copper PHY. */
130adff82eaSLuiz Souza 		PHY_WRITE(sc, MV88E151X_PAGE, MV88E151X_PAGE_COPPER);
131adff82eaSLuiz Souza 		sc->mii_flags &= ~MIIF_NOISOLATE;
132adff82eaSLuiz Souza 		sc->mii_capabilities = cop_cap;
133adff82eaSLuiz Souza 		sc->mii_extcapabilities = cop_extcap;
134adff82eaSLuiz Souza 	}
135adff82eaSLuiz Souza 	printf("\n");
136adff82eaSLuiz Souza 
137adff82eaSLuiz Souza 	MIIBUS_MEDIAINIT(sc->mii_dev);
138adff82eaSLuiz Souza 
139adff82eaSLuiz Souza 	mii_phy_setmedia(sc);
140adff82eaSLuiz Souza 
141adff82eaSLuiz Souza 	/* Enable the fiber PHY auto negotiation. */
142adff82eaSLuiz Souza 	if (MII_MODEL(ma->mii_id2) == MII_MODEL_xxMARVELL_E1512) {
143adff82eaSLuiz Souza 		PHY_WRITE(sc, MV88E151X_PAGE, MV88E151X_PAGE_FIBER);
144adff82eaSLuiz Souza 		PHY_WRITE(sc, MII_BMCR, BMCR_RESET | BMCR_AUTOEN |
145adff82eaSLuiz Souza 		    BMCR_STARTNEG | BMCR_FDX | BMCR_S1000);
146adff82eaSLuiz Souza 		PHY_WRITE(sc, MV88E151X_PAGE, MV88E151X_PAGE_COPPER);
147adff82eaSLuiz Souza 	}
148adff82eaSLuiz Souza 
149adff82eaSLuiz Souza 	return (0);
150adff82eaSLuiz Souza }
151adff82eaSLuiz Souza 
152adff82eaSLuiz Souza static int
mv88e151x_service(struct mii_softc * sc,struct mii_data * mii,int cmd)153adff82eaSLuiz Souza mv88e151x_service(struct mii_softc *sc, struct mii_data *mii, int cmd)
154adff82eaSLuiz Souza {
155adff82eaSLuiz Souza 	switch (cmd) {
156adff82eaSLuiz Souza 	case MII_POLLSTAT:
157adff82eaSLuiz Souza 		break;
158adff82eaSLuiz Souza 
159adff82eaSLuiz Souza 	case MII_MEDIACHG:
160adff82eaSLuiz Souza 		mii_phy_setmedia(sc);
161adff82eaSLuiz Souza 		break;
162adff82eaSLuiz Souza 
163adff82eaSLuiz Souza 	case MII_TICK:
164adff82eaSLuiz Souza 		if (mii_phy_tick(sc) == EJUSTRETURN)
165adff82eaSLuiz Souza 			return (0);
166adff82eaSLuiz Souza 		break;
167adff82eaSLuiz Souza 	}
168adff82eaSLuiz Souza 
169adff82eaSLuiz Souza 	/* Update the media status. */
170adff82eaSLuiz Souza 	PHY_STATUS(sc);
171adff82eaSLuiz Souza 
172adff82eaSLuiz Souza 	/* Callback if something changed. */
173adff82eaSLuiz Souza 	mii_phy_update(sc, cmd);
174adff82eaSLuiz Souza 	return (0);
175adff82eaSLuiz Souza }
176adff82eaSLuiz Souza 
177adff82eaSLuiz Souza static void
mv88e151x_fiber_status(struct mii_softc * phy)178adff82eaSLuiz Souza mv88e151x_fiber_status(struct mii_softc *phy)
179adff82eaSLuiz Souza {
180adff82eaSLuiz Souza 	struct mii_data *mii = phy->mii_pdata;
181adff82eaSLuiz Souza 	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
182adff82eaSLuiz Souza 	int bmsr, bmcr, anlpar; //, gtcr, gtsr;
183adff82eaSLuiz Souza 	uint32_t reg;
184adff82eaSLuiz Souza 
185adff82eaSLuiz Souza 	mii->mii_media_status = IFM_AVALID;
186adff82eaSLuiz Souza 	mii->mii_media_active = IFM_ETHER;
187adff82eaSLuiz Souza 
188adff82eaSLuiz Souza 	/* Switch to the fiber PHY. */
189adff82eaSLuiz Souza 	PHY_WRITE(phy, MV88E151X_PAGE, MV88E151X_PAGE_FIBER);
190adff82eaSLuiz Souza 
191adff82eaSLuiz Souza 	reg = PHY_READ(phy, MV88E151X_FIBER_STATUS);
192adff82eaSLuiz Souza 	bmsr = PHY_READ(phy, MII_BMSR) | PHY_READ(phy, MII_BMSR);
193adff82eaSLuiz Souza 	if (reg & MV88E151X_STATUS_LINK)
194adff82eaSLuiz Souza 		mii->mii_media_status |= IFM_ACTIVE;
195adff82eaSLuiz Souza 	bmcr = PHY_READ(phy, MII_BMCR);
196adff82eaSLuiz Souza 	if (bmcr & BMCR_ISO) {
197adff82eaSLuiz Souza 		mii->mii_media_active |= IFM_NONE;
198adff82eaSLuiz Souza 		mii->mii_media_status = 0;
199adff82eaSLuiz Souza 		PHY_WRITE(phy, MV88E151X_PAGE, MV88E151X_PAGE_COPPER);
200adff82eaSLuiz Souza 		return;
201adff82eaSLuiz Souza 	}
202adff82eaSLuiz Souza 
203adff82eaSLuiz Souza 	if (bmcr & BMCR_LOOP)
204adff82eaSLuiz Souza 		mii->mii_media_active |= IFM_LOOP;
205adff82eaSLuiz Souza 
206adff82eaSLuiz Souza 	if (bmcr & BMCR_AUTOEN) {
207adff82eaSLuiz Souza 		/*
208adff82eaSLuiz Souza 		 * NWay autonegotiation takes the highest-order common
209adff82eaSLuiz Souza 		 * bit of the ANAR and ANLPAR (i.e. best media advertised
210adff82eaSLuiz Souza 		 * both by us and our link partner).
211adff82eaSLuiz Souza 		 */
212adff82eaSLuiz Souza 		if ((bmsr & BMSR_ACOMP) == 0) {
213adff82eaSLuiz Souza 			/* Erg, still trying, I guess... */
214adff82eaSLuiz Souza 			mii->mii_media_active |= IFM_NONE;
215adff82eaSLuiz Souza 			PHY_WRITE(phy, MV88E151X_PAGE, MV88E151X_PAGE_COPPER);
216adff82eaSLuiz Souza 			return;
217adff82eaSLuiz Souza 		}
218adff82eaSLuiz Souza 
219adff82eaSLuiz Souza 		anlpar = PHY_READ(phy, MII_ANAR) & PHY_READ(phy, MII_ANLPAR);
220adff82eaSLuiz Souza 		if (anlpar & ANLPAR_X_FD)
221adff82eaSLuiz Souza 			mii->mii_media_active |= IFM_1000_SX | IFM_FDX;
222adff82eaSLuiz Souza 		else if (anlpar & ANLPAR_X_HD)
223adff82eaSLuiz Souza 			mii->mii_media_active |= IFM_1000_SX | IFM_HDX;
224adff82eaSLuiz Souza 		else if (reg & MV88E151X_STATUS_LINK &&
225adff82eaSLuiz Souza 		    reg & MV88E151X_STATUS_SYNC &&
226adff82eaSLuiz Souza 		    (reg & MV88E151X_STATUS_ENERGY) == 0) {
227adff82eaSLuiz Souza 			if ((reg & MV88E151X_STATUS_SPEED_MASK) ==
228adff82eaSLuiz Souza 			    MV88E151X_STATUS_SPEED_1000)
229adff82eaSLuiz Souza 				mii->mii_media_active |= IFM_1000_SX;
230adff82eaSLuiz Souza 			else if ((reg & MV88E151X_STATUS_SPEED_MASK) ==
231adff82eaSLuiz Souza 			    MV88E151X_STATUS_SPEED_100)
232adff82eaSLuiz Souza 				mii->mii_media_active |= IFM_100_FX;
233adff82eaSLuiz Souza 			else
234adff82eaSLuiz Souza 				mii->mii_media_active |= IFM_NONE;
235adff82eaSLuiz Souza 			if ((reg & MV88E151X_STATUS_SPEED_MASK) != 0 &&
236adff82eaSLuiz Souza 			    (reg & MV88E151X_STATUS_FDX))
237adff82eaSLuiz Souza 				mii->mii_media_active |= IFM_FDX;
238adff82eaSLuiz Souza 		} else
239adff82eaSLuiz Souza 			mii->mii_media_active |= IFM_NONE;
240adff82eaSLuiz Souza 
241adff82eaSLuiz Souza 		if ((mii->mii_media_active & IFM_NONE) == 0)
242adff82eaSLuiz Souza 			phy->mii_flags |= MIIF_IS_1000X;
243adff82eaSLuiz Souza 		if ((mii->mii_media_active & IFM_FDX) != 0)
244adff82eaSLuiz Souza 			mii->mii_media_active |= mii_phy_flowstatus(phy);
245adff82eaSLuiz Souza 	} else
246adff82eaSLuiz Souza 		mii->mii_media_active = ife->ifm_media;
247adff82eaSLuiz Souza 
248adff82eaSLuiz Souza 	if ((mii->mii_media_status & IFM_ACTIVE) == 0)
249adff82eaSLuiz Souza 		phy->mii_flags &= ~MIIF_IS_1000X;
250adff82eaSLuiz Souza 
251adff82eaSLuiz Souza 	/* Switch back to copper PHY. */
252adff82eaSLuiz Souza 	PHY_WRITE(phy, MV88E151X_PAGE, MV88E151X_PAGE_COPPER);
253adff82eaSLuiz Souza }
254adff82eaSLuiz Souza 
255adff82eaSLuiz Souza static void
mv88e151x_status(struct mii_softc * phy)256adff82eaSLuiz Souza mv88e151x_status(struct mii_softc *phy)
257adff82eaSLuiz Souza {
258adff82eaSLuiz Souza 	struct mii_data *mii = phy->mii_pdata;
259adff82eaSLuiz Souza 
260adff82eaSLuiz Souza 	mv88e151x_fiber_status(phy);
261adff82eaSLuiz Souza 	if (mii->mii_media_status & IFM_ACTIVE)
262adff82eaSLuiz Souza 		return;
263adff82eaSLuiz Souza 	ukphy_status(phy);
264adff82eaSLuiz Souza }
265