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