xref: /openbsd-src/sys/dev/mii/brswphy.c (revision f2da64fbbbf1b03f09f390ab01267c93dfd77c4c)
1 /*	$OpenBSD: brswphy.c,v 1.2 2014/12/05 15:50:04 mpi Exp $	*/
2 
3 /*
4  * Copyright (c) 2014 Paul Irofti <pirofti@openbsd.org>
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 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 void	brswphy_reset(struct mii_softc *);
164 
165 const struct mii_phy_funcs brswphy_funcs = {
166 	brswphy_service, brswphy_status, mii_phy_reset,
167 };
168 
169 static int brswphy_read16(struct mii_softc *sc, uint8_t page, uint8_t reg,
170     uint16_t *val);
171 static int brswphy_read32(struct mii_softc *sc, uint8_t page, uint8_t reg,
172     uint32_t *val);
173 static int brswphy_op(struct mii_softc *sc, uint8_t page, uint8_t reg,
174     uint16_t op);
175 
176 static const struct mii_phydesc brswphys[] = {
177 	{ MII_OUI_xxBROADCOM2,		MII_MODEL_xxBROADCOM2_BCM53115,
178 	  MII_STR_xxBROADCOM2_BCM53115 },
179 
180 	{ 0,			0,
181 	  NULL },
182 };
183 
184 int
185 brswphymatch(struct device *parent, void *match, void *aux)
186 {
187 	struct mii_attach_args *ma = aux;
188 
189 	if (mii_phy_match(ma, brswphys) != NULL)
190 		return (10);
191 
192 	return (0);
193 }
194 
195 void
196 brswphyattach(struct device *parent, struct device *self, void *aux)
197 {
198 	struct brswphy_softc *bsc = (struct brswphy_softc *)self;
199 	struct mii_softc *sc = &bsc->sc_mii;
200 	struct mii_attach_args *ma = aux;
201 	struct mii_data *mii = ma->mii_data;
202 	const struct mii_phydesc *mpd;
203 
204 	mpd = mii_phy_match(ma, brswphys);
205 	printf(": %s, rev. %d\n", mpd->mpd_name, MII_REV(ma->mii_id2));
206 
207 	sc->mii_inst = mii->mii_instance;
208 	sc->mii_phy = ma->mii_phyno;
209 	sc->mii_funcs = &brswphy_funcs;
210 	sc->mii_model = MII_MODEL(ma->mii_id2);
211 	sc->mii_rev = MII_REV(ma->mii_id2);
212 	sc->mii_pdata = mii;
213 	sc->mii_flags = ma->mii_flags;
214 	sc->mii_anegticks = MII_ANEGTICKS_GIGE;
215 
216 	sc->mii_flags |= MIIF_NOISOLATE;
217 
218 	sc->mii_capabilities = PHY_READ(sc, MII_BMSR) & ma->mii_capmask;
219 
220 	if (sc->mii_capabilities & BMSR_EXTSTAT)
221 		sc->mii_extcapabilities = PHY_READ(sc, MII_EXTSR);
222 	if ((sc->mii_capabilities & BMSR_MEDIAMASK) ||
223 	    (sc->mii_extcapabilities & EXTSR_MEDIAMASK))
224 		mii_phy_add_media(sc);
225 
226 	PHY_RESET(sc);
227 
228 	bsc->sc_current_page = 0xff;
229 }
230 
231 int
232 brswphy_service(struct mii_softc *sc, struct mii_data *mii, int cmd)
233 {
234 	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
235 	int reg;
236 
237 	if ((sc->mii_dev.dv_flags & DVF_ACTIVE) == 0)
238 		return (ENXIO);
239 
240 	switch (cmd) {
241 	case MII_POLLSTAT:
242 		/*
243 		 * If we're not polling our PHY instance, just return.
244 		 */
245 		if (IFM_INST(ife->ifm_media) != sc->mii_inst)
246 			return (0);
247 		break;
248 
249 	case MII_MEDIACHG:
250 		/*
251 		 * If the media indicates a different PHY instance,
252 		 * isolate ourselves.
253 		 */
254 		if (IFM_INST(ife->ifm_media) != sc->mii_inst) {
255 			reg = PHY_READ(sc, MII_BMCR);
256 			PHY_WRITE(sc, MII_BMCR, reg | BMCR_ISO);
257 			return (0);
258 		}
259 
260 		/*
261 		 * If the interface is not up, don't do anything.
262 		 */
263 		if ((mii->mii_ifp->if_flags & IFF_UP) == 0)
264 			break;
265 
266 		mii_phy_setmedia(sc);
267 		break;
268 
269 	case MII_TICK:
270 		/*
271 		 * If we're not currently selected, just return.
272 		 */
273 		if (IFM_INST(ife->ifm_media) != sc->mii_inst)
274 			return (0);
275 
276 		if (mii_phy_tick(sc) == EJUSTRETURN)
277 			return (0);
278 		break;
279 
280 	case MII_DOWN:
281 		mii_phy_down(sc);
282 		return (0);
283 	}
284 
285 	/* Update the media status. */
286 	mii_phy_status(sc);
287 
288 	/* Callback if something changed. */
289 	mii_phy_update(sc, cmd);
290 	return (0);
291 }
292 
293 void
294 brswphy_status(struct mii_softc *sc)
295 {
296 	struct mii_data *mii = sc->mii_pdata;
297 	uint32_t speed;
298 	uint16_t link, duplex;
299 
300 	mii->mii_media_status = IFM_AVALID;
301 	mii->mii_media_active = IFM_ETHER;
302 
303 	/* XXX: check that this is the CPU port when switch support arrives */
304 
305 	brswphy_read16(sc, BRSW_STAT_PAGE, BRSW_LINK_STAT, &link);
306 	if ((link >> BRSW_CPU_PORT) & 1)
307 		mii->mii_media_status |= IFM_ACTIVE;
308 
309 	brswphy_read16(sc, BRSW_STAT_PAGE, BRSW_DUPLEX_STAT_GE, &duplex);
310 	duplex = (duplex >> BRSW_CPU_PORT) & 1;
311 
312 	brswphy_read32(sc, BRSW_STAT_PAGE, BRSW_SPEED_STAT, &speed);
313 	speed = SPEED_PORT_GE(speed, BRSW_CPU_PORT);
314 	switch (speed) {
315 	case SPEED_STAT_10M:
316 		mii->mii_media_active |= IFM_10_T;
317 		break;
318 	case SPEED_STAT_100M:
319 		mii->mii_media_active |= IFM_100_TX;
320 		break;
321 	case SPEED_STAT_1000M:
322 		mii->mii_media_active |= IFM_1000_T;
323 		break;
324 	}
325 
326 	if (duplex)
327 		mii->mii_media_active |= IFM_FDX;
328 	else
329 		mii->mii_media_active |= IFM_HDX;
330 
331 	mii->mii_media_active |= IFM_ETH_MASTER;
332 }
333 
334 static int
335 brswphy_op(struct mii_softc *sc, uint8_t page, uint8_t reg, uint16_t op)
336 {
337 	struct brswphy_softc *bsc = (struct brswphy_softc *)sc;
338 	int i;
339 	uint16_t v;
340 
341 	if (bsc->sc_current_page != page) {
342 		/* set page number */
343 		v = (page << 8) | REG_MII_PAGE_ENABLE;
344 		BRSW_PHY_WRITE(sc, REG_MII_PAGE, v);
345 		bsc->sc_current_page = page;
346 	}
347 
348 	/* set register address */
349 	v = (reg << 8) | op;
350 	BRSW_PHY_WRITE(sc, REG_MII_ADDR, v);
351 
352 	/* check if operation completed */
353 	for (i = 0; i < 5; ++i) {
354 		v = BRSW_PHY_READ(sc, REG_MII_ADDR);
355 		if (!(v & (REG_MII_ADDR_WRITE | REG_MII_ADDR_READ)))
356 			break;
357 		delay(10);
358 	}
359 
360 	if (i == 5)
361 		return -EIO;
362 
363 	return 0;
364 }
365 
366 static int
367 brswphy_read16(struct mii_softc *sc, uint8_t page, uint8_t reg, uint16_t *val)
368 {
369 	int ret;
370 
371 	ret = brswphy_op(sc, page, reg, REG_MII_ADDR_READ);
372 	if (ret)
373 		return ret;
374 
375 	*val = BRSW_PHY_READ(sc, REG_MII_DATA0);
376 
377 	return 0;
378 }
379 
380 static int
381 brswphy_read32(struct mii_softc *sc, uint8_t page, uint8_t reg, uint32_t *val)
382 {
383 	int ret;
384 
385 	ret = brswphy_op(sc, page, reg, REG_MII_ADDR_READ);
386 	if (ret)
387 		return ret;
388 
389 	*val = BRSW_PHY_READ(sc, REG_MII_DATA0);
390 	*val |= BRSW_PHY_READ(sc, REG_MII_DATA1) << 16;
391 
392 	return 0;
393 }
394