xref: /openbsd-src/sys/dev/mii/brswphy.c (revision 0f9e9ec23bb2b65cc62a3d17df12827a45dae80c)
1 /*	$OpenBSD: brswphy.c,v 1.5 2024/05/13 01:15:51 jsg Exp $	*/
2 
3 /*
4  * Copyright (c) 2014 Paul Irofti <paul@irofti.net>
5  *
6  * Permission to use, copy, modify, and/or distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 /* Copyright (C) 2011-2013 Jonas Gorski <jogo@openwrt.org>
20  *
21  * Permission to use, copy, modify, and/or distribute this software for any
22  * purpose with or without fee is hereby granted, provided that the above
23  * copyright notice and this permission notice appear in all copies.
24  *
25  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
26  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
27  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
28  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
29  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
30  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
31  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
32  */
33 
34 /*-
35  * Copyright (c) 1998, 1999 The NetBSD Foundation, Inc.
36  * All rights reserved.
37  *
38  * This code is derived from software contributed to The NetBSD Foundation
39  * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
40  * NASA Ames Research Center, and by Frank van der Linden.
41  *
42  * Redistribution and use in source and binary forms, with or without
43  * modification, are permitted provided that the following conditions
44  * are met:
45  * 1. Redistributions of source code must retain the above copyright
46  *    notice, this list of conditions and the following disclaimer.
47  * 2. Redistributions in binary form must reproduce the above copyright
48  *    notice, this list of conditions and the following disclaimer in the
49  *    documentation and/or other materials provided with the distribution.
50  *
51  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
52  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
53  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
54  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
55  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
56  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
57  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
58  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
59  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
60  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
61  * POSSIBILITY OF SUCH DAMAGE.
62  */
63 
64 /*
65  * Copyright (c) 1997 Manuel Bouyer.  All rights reserved.
66  *
67  * Redistribution and use in source and binary forms, with or without
68  * modification, are permitted provided that the following conditions
69  * are met:
70  * 1. Redistributions of source code must retain the above copyright
71  *    notice, this list of conditions and the following disclaimer.
72  * 2. Redistributions in binary form must reproduce the above copyright
73  *    notice, this list of conditions and the following disclaimer in the
74  *    documentation and/or other materials provided with the distribution.
75  *
76  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
77  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
78  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
79  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
80  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
81  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
82  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
83  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
84  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
85  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
86  */
87 
88 #include <sys/param.h>
89 #include <sys/systm.h>
90 #include <sys/kernel.h>
91 #include <sys/device.h>
92 #include <sys/socket.h>
93 #include <sys/errno.h>
94 
95 #include <net/if.h>
96 #include <net/if_var.h>
97 #include <net/if_media.h>
98 
99 #include <dev/mii/mii.h>
100 #include <dev/mii/miivar.h>
101 #include <dev/mii/miidevs.h>
102 
103 #define BRSW_PSEUDO_PHY	0x1e /* Register Access Pseudo PHY */
104 
105 /* MII registers */
106 #define REG_MII_PAGE    0x10    /* MII Page register */
107 #define REG_MII_ADDR    0x11    /* MII Address register */
108 #define REG_MII_DATA0   0x18    /* MII Data register 0 */
109 #define REG_MII_DATA1   0x19    /* MII Data register 1 */
110 #define REG_MII_DATA2   0x1a    /* MII Data register 2 */
111 #define REG_MII_DATA3   0x1b    /* MII Data register 3 */
112 
113 #define REG_MII_PAGE_ENABLE     1
114 #define REG_MII_ADDR_WRITE      1
115 #define REG_MII_ADDR_READ       2
116 
117 /* Management Port (SMP) Page offsets */
118 #define BRSW_STAT_PAGE                   0x01 /* Status */
119 /* Link Status Summary Register (16bit) */
120 #define BRSW_LINK_STAT                   0x00
121 
122 /* Duplex Status Summary (16 bit) */
123 #define BRSW_DUPLEX_STAT_FE              0x06
124 #define BRSW_DUPLEX_STAT_GE              0x08
125 #define BRSW_DUPLEX_STAT_63XX            0x0c
126 
127 /* Port Speed Summary Register (16 bit for FE, 32 bit for GE) */
128 #define BRSW_SPEED_STAT			0x04
129 #define SPEED_PORT_FE(reg, port)	(((reg) >> (port)) & 1)
130 #define SPEED_PORT_GE(reg, port)	(((reg) >> 2 * (port)) & 3)
131 #define SPEED_STAT_10M			0
132 #define SPEED_STAT_100M			1
133 #define SPEED_STAT_1000M		2
134 
135 #define BRSW_CPU_PORT    8
136 
137 #define	BRSW_PHY_READ(p, r) \
138 	(*(p)->mii_pdata->mii_readreg)((p)->mii_dev.dv_parent, \
139 	    BRSW_PSEUDO_PHY, (r))
140 #define	BRSW_PHY_WRITE(p, r, v) \
141 	(*(p)->mii_pdata->mii_writereg)((p)->mii_dev.dv_parent, \
142 	    BRSW_PSEUDO_PHY, (r), (v))
143 
144 struct brswphy_softc {
145 	struct mii_softc	sc_mii; /* common mii device part */
146 
147 	uint8_t sc_current_page;
148 };
149 
150 int	brswphymatch(struct device *, void *, void *);
151 void	brswphyattach(struct device *, struct device *, void *);
152 
153 const struct cfattach brswphy_ca = { sizeof(struct brswphy_softc),
154 	brswphymatch, brswphyattach, mii_phy_detach,
155 };
156 
157 struct cfdriver brswphy_cd = {
158 	NULL, "brswphy", DV_DULL
159 };
160 
161 int	brswphy_service(struct mii_softc *, struct mii_data *, int);
162 void	brswphy_status(struct mii_softc *);
163 
164 const struct mii_phy_funcs brswphy_funcs = {
165 	brswphy_service, brswphy_status, mii_phy_reset,
166 };
167 
168 static int brswphy_read16(struct mii_softc *sc, uint8_t page, uint8_t reg,
169     uint16_t *val);
170 static int brswphy_read32(struct mii_softc *sc, uint8_t page, uint8_t reg,
171     uint32_t *val);
172 static int brswphy_op(struct mii_softc *sc, uint8_t page, uint8_t reg,
173     uint16_t op);
174 
175 static const struct mii_phydesc brswphys[] = {
176 	{ MII_OUI_xxBROADCOM2,		MII_MODEL_xxBROADCOM2_BCM53115,
177 	  MII_STR_xxBROADCOM2_BCM53115 },
178 
179 	{ 0,			0,
180 	  NULL },
181 };
182 
183 int
brswphymatch(struct device * parent,void * match,void * aux)184 brswphymatch(struct device *parent, void *match, void *aux)
185 {
186 	struct mii_attach_args *ma = aux;
187 
188 	if (mii_phy_match(ma, brswphys) != NULL)
189 		return (10);
190 
191 	return (0);
192 }
193 
194 void
brswphyattach(struct device * parent,struct device * self,void * aux)195 brswphyattach(struct device *parent, struct device *self, void *aux)
196 {
197 	struct brswphy_softc *bsc = (struct brswphy_softc *)self;
198 	struct mii_softc *sc = &bsc->sc_mii;
199 	struct mii_attach_args *ma = aux;
200 	struct mii_data *mii = ma->mii_data;
201 	const struct mii_phydesc *mpd;
202 
203 	mpd = mii_phy_match(ma, brswphys);
204 	printf(": %s, rev. %d\n", mpd->mpd_name, MII_REV(ma->mii_id2));
205 
206 	sc->mii_inst = mii->mii_instance;
207 	sc->mii_phy = ma->mii_phyno;
208 	sc->mii_funcs = &brswphy_funcs;
209 	sc->mii_model = MII_MODEL(ma->mii_id2);
210 	sc->mii_rev = MII_REV(ma->mii_id2);
211 	sc->mii_pdata = mii;
212 	sc->mii_flags = ma->mii_flags;
213 	sc->mii_anegticks = MII_ANEGTICKS_GIGE;
214 
215 	sc->mii_flags |= MIIF_NOISOLATE;
216 
217 	sc->mii_capabilities = PHY_READ(sc, MII_BMSR) & ma->mii_capmask;
218 
219 	if (sc->mii_capabilities & BMSR_EXTSTAT)
220 		sc->mii_extcapabilities = PHY_READ(sc, MII_EXTSR);
221 	if ((sc->mii_capabilities & BMSR_MEDIAMASK) ||
222 	    (sc->mii_extcapabilities & EXTSR_MEDIAMASK))
223 		mii_phy_add_media(sc);
224 
225 	PHY_RESET(sc);
226 
227 	bsc->sc_current_page = 0xff;
228 }
229 
230 int
brswphy_service(struct mii_softc * sc,struct mii_data * mii,int cmd)231 brswphy_service(struct mii_softc *sc, struct mii_data *mii, int cmd)
232 {
233 	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
234 	int reg;
235 
236 	if ((sc->mii_dev.dv_flags & DVF_ACTIVE) == 0)
237 		return (ENXIO);
238 
239 	switch (cmd) {
240 	case MII_POLLSTAT:
241 		/*
242 		 * If we're not polling our PHY instance, just return.
243 		 */
244 		if (IFM_INST(ife->ifm_media) != sc->mii_inst)
245 			return (0);
246 		break;
247 
248 	case MII_MEDIACHG:
249 		/*
250 		 * If the media indicates a different PHY instance,
251 		 * isolate ourselves.
252 		 */
253 		if (IFM_INST(ife->ifm_media) != sc->mii_inst) {
254 			reg = PHY_READ(sc, MII_BMCR);
255 			PHY_WRITE(sc, MII_BMCR, reg | BMCR_ISO);
256 			return (0);
257 		}
258 
259 		/*
260 		 * If the interface is not up, don't do anything.
261 		 */
262 		if ((mii->mii_ifp->if_flags & IFF_UP) == 0)
263 			break;
264 
265 		mii_phy_setmedia(sc);
266 		break;
267 
268 	case MII_TICK:
269 		/*
270 		 * If we're not currently selected, just return.
271 		 */
272 		if (IFM_INST(ife->ifm_media) != sc->mii_inst)
273 			return (0);
274 
275 		if (mii_phy_tick(sc) == EJUSTRETURN)
276 			return (0);
277 		break;
278 
279 	case MII_DOWN:
280 		mii_phy_down(sc);
281 		return (0);
282 	}
283 
284 	/* Update the media status. */
285 	mii_phy_status(sc);
286 
287 	/* Callback if something changed. */
288 	mii_phy_update(sc, cmd);
289 	return (0);
290 }
291 
292 void
brswphy_status(struct mii_softc * sc)293 brswphy_status(struct mii_softc *sc)
294 {
295 	struct mii_data *mii = sc->mii_pdata;
296 	uint32_t speed;
297 	uint16_t link, duplex;
298 
299 	mii->mii_media_status = IFM_AVALID;
300 	mii->mii_media_active = IFM_ETHER;
301 
302 	/* XXX: check that this is the CPU port when switch support arrives */
303 
304 	brswphy_read16(sc, BRSW_STAT_PAGE, BRSW_LINK_STAT, &link);
305 	if ((link >> BRSW_CPU_PORT) & 1)
306 		mii->mii_media_status |= IFM_ACTIVE;
307 
308 	brswphy_read16(sc, BRSW_STAT_PAGE, BRSW_DUPLEX_STAT_GE, &duplex);
309 	duplex = (duplex >> BRSW_CPU_PORT) & 1;
310 
311 	brswphy_read32(sc, BRSW_STAT_PAGE, BRSW_SPEED_STAT, &speed);
312 	speed = SPEED_PORT_GE(speed, BRSW_CPU_PORT);
313 	switch (speed) {
314 	case SPEED_STAT_10M:
315 		mii->mii_media_active |= IFM_10_T;
316 		break;
317 	case SPEED_STAT_100M:
318 		mii->mii_media_active |= IFM_100_TX;
319 		break;
320 	case SPEED_STAT_1000M:
321 		mii->mii_media_active |= IFM_1000_T;
322 		break;
323 	}
324 
325 	if (duplex)
326 		mii->mii_media_active |= IFM_FDX;
327 	else
328 		mii->mii_media_active |= IFM_HDX;
329 
330 	mii->mii_media_active |= IFM_ETH_MASTER;
331 }
332 
333 static int
brswphy_op(struct mii_softc * sc,uint8_t page,uint8_t reg,uint16_t op)334 brswphy_op(struct mii_softc *sc, uint8_t page, uint8_t reg, uint16_t op)
335 {
336 	struct brswphy_softc *bsc = (struct brswphy_softc *)sc;
337 	int i;
338 	uint16_t v;
339 
340 	if (bsc->sc_current_page != page) {
341 		/* set page number */
342 		v = (page << 8) | REG_MII_PAGE_ENABLE;
343 		BRSW_PHY_WRITE(sc, REG_MII_PAGE, v);
344 		bsc->sc_current_page = page;
345 	}
346 
347 	/* set register address */
348 	v = (reg << 8) | op;
349 	BRSW_PHY_WRITE(sc, REG_MII_ADDR, v);
350 
351 	/* check if operation completed */
352 	for (i = 0; i < 5; ++i) {
353 		v = BRSW_PHY_READ(sc, REG_MII_ADDR);
354 		if (!(v & (REG_MII_ADDR_WRITE | REG_MII_ADDR_READ)))
355 			break;
356 		delay(10);
357 	}
358 
359 	if (i == 5)
360 		return -EIO;
361 
362 	return 0;
363 }
364 
365 static int
brswphy_read16(struct mii_softc * sc,uint8_t page,uint8_t reg,uint16_t * val)366 brswphy_read16(struct mii_softc *sc, uint8_t page, uint8_t reg, uint16_t *val)
367 {
368 	int ret;
369 
370 	ret = brswphy_op(sc, page, reg, REG_MII_ADDR_READ);
371 	if (ret)
372 		return ret;
373 
374 	*val = BRSW_PHY_READ(sc, REG_MII_DATA0);
375 
376 	return 0;
377 }
378 
379 static int
brswphy_read32(struct mii_softc * sc,uint8_t page,uint8_t reg,uint32_t * val)380 brswphy_read32(struct mii_softc *sc, uint8_t page, uint8_t reg, uint32_t *val)
381 {
382 	int ret;
383 
384 	ret = brswphy_op(sc, page, reg, REG_MII_ADDR_READ);
385 	if (ret)
386 		return ret;
387 
388 	*val = BRSW_PHY_READ(sc, REG_MII_DATA0);
389 	*val |= BRSW_PHY_READ(sc, REG_MII_DATA1) << 16;
390 
391 	return 0;
392 }
393