1/*	$NetBSD: mii.c,v 1.12 1999/08/03 19:41:49 drochner Exp $	*/
2
3/*-
4 * Copyright (c) 1998 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
9 * NASA Ames Research Center.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 *    notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 *    notice, this list of conditions and the following disclaimer in the
18 *    documentation and/or other materials provided with the distribution.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
21 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
24 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30 * POSSIBILITY OF SUCH DAMAGE.
31 */
32
33#include <sys/cdefs.h>
34__FBSDID("$FreeBSD$");
35
36/*
37 * MII bus layer, glues MII-capable network interface drivers to sharable
38 * PHY drivers.  This exports an interface compatible with BSD/OS 3.0's,
39 * plus some NetBSD extensions.
40 */
41
42#include <sys/param.h>
43#include <sys/systm.h>
44#include <sys/socket.h>
45#include <sys/malloc.h>
46#include <sys/module.h>
47#include <sys/bus.h>
48
49#include <net/if.h>
50#include <net/if_media.h>
51
52#include <dev/mii/mii.h>
53#include <dev/mii/miivar.h>
54
55MODULE_VERSION(miibus, 1);
56
57#include "miibus_if.h"
58
59static device_attach_t miibus_attach;
60static bus_child_location_str_t miibus_child_location_str;
61static bus_child_pnpinfo_str_t miibus_child_pnpinfo_str;
62static device_detach_t miibus_detach;
63static bus_hinted_child_t miibus_hinted_child;
64static bus_print_child_t miibus_print_child;
65static device_probe_t miibus_probe;
66static bus_read_ivar_t miibus_read_ivar;
67static miibus_readreg_t miibus_readreg;
68static miibus_statchg_t miibus_statchg;
69static miibus_writereg_t miibus_writereg;
70static miibus_linkchg_t miibus_linkchg;
71static miibus_mediainit_t miibus_mediainit;
72
73static unsigned char mii_bitreverse(unsigned char x);
74
75static device_method_t miibus_methods[] = {
76	/* device interface */
77	DEVMETHOD(device_probe,		miibus_probe),
78	DEVMETHOD(device_attach,	miibus_attach),
79	DEVMETHOD(device_detach,	miibus_detach),
80	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
81
82	/* bus interface */
83	DEVMETHOD(bus_print_child,	miibus_print_child),
84	DEVMETHOD(bus_read_ivar,	miibus_read_ivar),
85	DEVMETHOD(bus_child_pnpinfo_str, miibus_child_pnpinfo_str),
86	DEVMETHOD(bus_child_location_str, miibus_child_location_str),
87	DEVMETHOD(bus_hinted_child,	miibus_hinted_child),
88
89	/* MII interface */
90	DEVMETHOD(miibus_readreg,	miibus_readreg),
91	DEVMETHOD(miibus_writereg,	miibus_writereg),
92	DEVMETHOD(miibus_statchg,	miibus_statchg),
93	DEVMETHOD(miibus_linkchg,	miibus_linkchg),
94	DEVMETHOD(miibus_mediainit,	miibus_mediainit),
95
96	DEVMETHOD_END
97};
98
99devclass_t miibus_devclass;
100
101driver_t miibus_driver = {
102	"miibus",
103	miibus_methods,
104	sizeof(struct mii_data)
105};
106
107struct miibus_ivars {
108	struct ifnet	*ifp;
109	ifm_change_cb_t	ifmedia_upd;
110	ifm_stat_cb_t	ifmedia_sts;
111	u_int		mii_flags;
112	u_int		mii_offset;
113};
114
115static int
116miibus_probe(device_t dev)
117{
118
119	device_set_desc(dev, "MII bus");
120
121	return (BUS_PROBE_SPECIFIC);
122}
123
124static int
125miibus_attach(device_t dev)
126{
127	struct miibus_ivars	*ivars;
128	struct mii_attach_args	*ma;
129	struct mii_data		*mii;
130	device_t		*children;
131	int			i, nchildren;
132
133	mii = device_get_softc(dev);
134	if (device_get_children(dev, &children, &nchildren) == 0) {
135		for (i = 0; i < nchildren; i++) {
136			ma = device_get_ivars(children[i]);
137			ma->mii_data = mii;
138		}
139		free(children, M_TEMP);
140	}
141	if (nchildren == 0) {
142		device_printf(dev, "cannot get children\n");
143		return (ENXIO);
144	}
145	ivars = device_get_ivars(dev);
146	ifmedia_init(&mii->mii_media, IFM_IMASK, ivars->ifmedia_upd,
147	    ivars->ifmedia_sts);
148	mii->mii_ifp = ivars->ifp;
149	mii->mii_ifp->if_capabilities |= IFCAP_LINKSTATE;
150	mii->mii_ifp->if_capenable |= IFCAP_LINKSTATE;
151	LIST_INIT(&mii->mii_phys);
152
153	return (bus_generic_attach(dev));
154}
155
156static int
157miibus_detach(device_t dev)
158{
159	struct mii_data		*mii;
160
161	bus_generic_detach(dev);
162	mii = device_get_softc(dev);
163	ifmedia_removeall(&mii->mii_media);
164	mii->mii_ifp = NULL;
165
166	return (0);
167}
168
169static int
170miibus_print_child(device_t dev, device_t child)
171{
172	struct mii_attach_args *ma;
173	int retval;
174
175	ma = device_get_ivars(child);
176	retval = bus_print_child_header(dev, child);
177	retval += printf(" PHY %d", ma->mii_phyno);
178	retval += bus_print_child_footer(dev, child);
179
180	return (retval);
181}
182
183static int
184miibus_read_ivar(device_t dev, device_t child __unused, int which,
185    uintptr_t *result)
186{
187	struct miibus_ivars *ivars;
188
189	/*
190	 * NB: this uses the instance variables of the miibus rather than
191	 * its PHY children.
192	 */
193	ivars = device_get_ivars(dev);
194	switch (which) {
195	case MIIBUS_IVAR_FLAGS:
196		*result = ivars->mii_flags;
197		break;
198	default:
199		return (ENOENT);
200	}
201	return (0);
202}
203
204static int
205miibus_child_pnpinfo_str(device_t dev __unused, device_t child, char *buf,
206    size_t buflen)
207{
208	struct mii_attach_args *ma;
209
210	ma = device_get_ivars(child);
211	snprintf(buf, buflen, "oui=0x%x model=0x%x rev=0x%x",
212	    MII_OUI(ma->mii_id1, ma->mii_id2),
213	    MII_MODEL(ma->mii_id2), MII_REV(ma->mii_id2));
214	return (0);
215}
216
217static int
218miibus_child_location_str(device_t dev __unused, device_t child, char *buf,
219    size_t buflen)
220{
221	struct mii_attach_args *ma;
222
223	ma = device_get_ivars(child);
224	snprintf(buf, buflen, "phyno=%d", ma->mii_phyno);
225	return (0);
226}
227
228static void
229miibus_hinted_child(device_t dev, const char *name, int unit)
230{
231	struct miibus_ivars *ivars;
232	struct mii_attach_args *args, *ma;
233	device_t *children, phy;
234	int i, nchildren;
235	u_int val;
236
237	if (resource_int_value(name, unit, "phyno", &val) != 0)
238		return;
239	if (device_get_children(dev, &children, &nchildren) != 0)
240		return;
241	ma = NULL;
242	for (i = 0; i < nchildren; i++) {
243		args = device_get_ivars(children[i]);
244		if (args->mii_phyno == val) {
245			ma = args;
246			break;
247		}
248	}
249	free(children, M_TEMP);
250
251	/*
252	 * Don't add a PHY that was automatically identified by having media
253	 * in its BMSR twice, only allow to alter its attach arguments.
254	 */
255	if (ma == NULL) {
256		ma = malloc(sizeof(struct mii_attach_args), M_DEVBUF,
257		    M_NOWAIT);
258		if (ma == NULL)
259			return;
260		phy = device_add_child(dev, name, unit);
261		if (phy == NULL) {
262			free(ma, M_DEVBUF);
263			return;
264		}
265		ivars = device_get_ivars(dev);
266		ma->mii_phyno = val;
267		ma->mii_offset = ivars->mii_offset++;
268		ma->mii_id1 = 0;
269		ma->mii_id2 = 0;
270		ma->mii_capmask = BMSR_DEFCAPMASK;
271		device_set_ivars(phy, ma);
272	}
273
274	if (resource_int_value(name, unit, "id1", &val) == 0)
275		ma->mii_id1 = val;
276	if (resource_int_value(name, unit, "id2", &val) == 0)
277		ma->mii_id2 = val;
278	if (resource_int_value(name, unit, "capmask", &val) == 0)
279		ma->mii_capmask = val;
280}
281
282static int
283miibus_readreg(device_t dev, int phy, int reg)
284{
285	device_t		parent;
286
287	parent = device_get_parent(dev);
288	return (MIIBUS_READREG(parent, phy, reg));
289}
290
291static int
292miibus_writereg(device_t dev, int phy, int reg, int data)
293{
294	device_t		parent;
295
296	parent = device_get_parent(dev);
297	return (MIIBUS_WRITEREG(parent, phy, reg, data));
298}
299
300static void
301miibus_statchg(device_t dev)
302{
303	device_t		parent;
304	struct mii_data		*mii;
305
306	parent = device_get_parent(dev);
307	MIIBUS_STATCHG(parent);
308
309	mii = device_get_softc(dev);
310	mii->mii_ifp->if_baudrate = ifmedia_baudrate(mii->mii_media_active);
311}
312
313static void
314miibus_linkchg(device_t dev)
315{
316	struct mii_data		*mii;
317	device_t		parent;
318	int			link_state;
319
320	parent = device_get_parent(dev);
321	MIIBUS_LINKCHG(parent);
322
323	mii = device_get_softc(dev);
324
325	if (mii->mii_media_status & IFM_AVALID) {
326		if (mii->mii_media_status & IFM_ACTIVE)
327			link_state = LINK_STATE_UP;
328		else
329			link_state = LINK_STATE_DOWN;
330	} else
331		link_state = LINK_STATE_UNKNOWN;
332	if_link_state_change(mii->mii_ifp, link_state);
333}
334
335static void
336miibus_mediainit(device_t dev)
337{
338	struct mii_data		*mii;
339	struct ifmedia_entry	*m;
340	int			media = 0;
341
342	/* Poke the parent in case it has any media of its own to add. */
343	MIIBUS_MEDIAINIT(device_get_parent(dev));
344
345	mii = device_get_softc(dev);
346	LIST_FOREACH(m, &mii->mii_media.ifm_list, ifm_list) {
347		media = m->ifm_media;
348		if (media == (IFM_ETHER | IFM_AUTO))
349			break;
350	}
351
352	ifmedia_set(&mii->mii_media, media);
353}
354
355/*
356 * Helper function used by network interface drivers, attaches the miibus and
357 * the PHYs to the network interface driver parent.
358 */
359int
360mii_attach(device_t dev, device_t *miibus, struct ifnet *ifp,
361    ifm_change_cb_t ifmedia_upd, ifm_stat_cb_t ifmedia_sts, int capmask,
362    int phyloc, int offloc, int flags)
363{
364	struct miibus_ivars *ivars;
365	struct mii_attach_args *args, ma;
366	device_t *children, phy;
367	int bmsr, first, i, nchildren, phymax, phymin, rv;
368	uint32_t phymask;
369
370	if (phyloc != MII_PHY_ANY && offloc != MII_OFFSET_ANY) {
371		printf("%s: phyloc and offloc specified\n", __func__);
372		return (EINVAL);
373	}
374
375	if (offloc != MII_OFFSET_ANY && (offloc < 0 || offloc >= MII_NPHY)) {
376		printf("%s: ivalid offloc %d\n", __func__, offloc);
377		return (EINVAL);
378	}
379
380	if (phyloc == MII_PHY_ANY) {
381		phymin = 0;
382		phymax = MII_NPHY - 1;
383	} else {
384		if (phyloc < 0 || phyloc >= MII_NPHY) {
385			printf("%s: ivalid phyloc %d\n", __func__, phyloc);
386			return (EINVAL);
387		}
388		phymin = phymax = phyloc;
389	}
390
391	first = 0;
392	if (*miibus == NULL) {
393		first = 1;
394		ivars = malloc(sizeof(*ivars), M_DEVBUF, M_NOWAIT);
395		if (ivars == NULL)
396			return (ENOMEM);
397		ivars->ifp = ifp;
398		ivars->ifmedia_upd = ifmedia_upd;
399		ivars->ifmedia_sts = ifmedia_sts;
400		ivars->mii_flags = flags;
401		*miibus = device_add_child(dev, "miibus", -1);
402		if (*miibus == NULL) {
403			rv = ENXIO;
404			goto fail;
405		}
406		device_set_ivars(*miibus, ivars);
407	} else {
408		ivars = device_get_ivars(*miibus);
409		if (ivars->ifp != ifp || ivars->ifmedia_upd != ifmedia_upd ||
410		    ivars->ifmedia_sts != ifmedia_sts ||
411		    ivars->mii_flags != flags) {
412			printf("%s: non-matching invariant\n", __func__);
413			return (EINVAL);
414		}
415		/*
416		 * Assignment of the attach arguments mii_data for the first
417		 * pass is done in miibus_attach(), i.e. once the miibus softc
418		 * has been allocated.
419		 */
420		ma.mii_data = device_get_softc(*miibus);
421	}
422
423	ma.mii_capmask = capmask;
424
425	if (resource_int_value(device_get_name(*miibus),
426	    device_get_unit(*miibus), "phymask", &phymask) != 0)
427		phymask = 0xffffffff;
428
429	if (device_get_children(*miibus, &children, &nchildren) != 0) {
430		children = NULL;
431		nchildren = 0;
432	}
433	ivars->mii_offset = 0;
434	for (ma.mii_phyno = phymin; ma.mii_phyno <= phymax; ma.mii_phyno++) {
435		/*
436		 * Make sure we haven't already configured a PHY at this
437		 * address.  This allows mii_attach() to be called
438		 * multiple times.
439		 */
440		for (i = 0; i < nchildren; i++) {
441			args = device_get_ivars(children[i]);
442			if (args->mii_phyno == ma.mii_phyno) {
443				/*
444				 * Yes, there is already something
445				 * configured at this address.
446				 */
447				goto skip;
448			}
449		}
450
451		/*
452		 * Check to see if there is a PHY at this address.  Note,
453		 * many braindead PHYs report 0/0 in their ID registers,
454		 * so we test for media in the BMSR.
455		 */
456		bmsr = MIIBUS_READREG(dev, ma.mii_phyno, MII_BMSR);
457		if (bmsr == 0 || bmsr == 0xffff ||
458		    (bmsr & (BMSR_EXTSTAT | BMSR_MEDIAMASK)) == 0) {
459			/* Assume no PHY at this address. */
460			continue;
461		}
462
463		/*
464		 * There is a PHY at this address.  If we were given an
465		 * `offset' locator, skip this PHY if it doesn't match.
466		 */
467		if (offloc != MII_OFFSET_ANY && offloc != ivars->mii_offset)
468			goto skip;
469
470		/*
471		 * Skip this PHY if it's not included in the phymask hint.
472		 */
473		if ((phymask & (1 << ma.mii_phyno)) == 0)
474			goto skip;
475
476		/*
477		 * Extract the IDs.  Braindead PHYs will be handled by
478		 * the `ukphy' driver, as we have no ID information to
479		 * match on.
480		 */
481		ma.mii_id1 = MIIBUS_READREG(dev, ma.mii_phyno, MII_PHYIDR1);
482		ma.mii_id2 = MIIBUS_READREG(dev, ma.mii_phyno, MII_PHYIDR2);
483
484		ma.mii_offset = ivars->mii_offset;
485		args = malloc(sizeof(struct mii_attach_args), M_DEVBUF,
486		    M_NOWAIT);
487		if (args == NULL)
488			goto skip;
489		bcopy((char *)&ma, (char *)args, sizeof(ma));
490		phy = device_add_child(*miibus, NULL, -1);
491		if (phy == NULL) {
492			free(args, M_DEVBUF);
493			goto skip;
494		}
495		device_set_ivars(phy, args);
496 skip:
497		ivars->mii_offset++;
498	}
499	free(children, M_TEMP);
500
501	if (first != 0) {
502		rv = device_set_driver(*miibus, &miibus_driver);
503		if (rv != 0)
504			goto fail;
505		bus_enumerate_hinted_children(*miibus);
506		rv = device_get_children(*miibus, &children, &nchildren);
507		if (rv != 0)
508			goto fail;
509		free(children, M_TEMP);
510		if (nchildren == 0) {
511			rv = ENXIO;
512			goto fail;
513		}
514		rv = bus_generic_attach(dev);
515		if (rv != 0)
516			goto fail;
517
518		/* Attaching of the PHY drivers is done in miibus_attach(). */
519		return (0);
520	}
521	rv = bus_generic_attach(*miibus);
522	if (rv != 0)
523		goto fail;
524
525	return (0);
526
527 fail:
528	if (*miibus != NULL)
529		device_delete_child(dev, *miibus);
530	free(ivars, M_DEVBUF);
531	if (first != 0)
532		*miibus = NULL;
533	return (rv);
534}
535
536/*
537 * Media changed; notify all PHYs.
538 */
539int
540mii_mediachg(struct mii_data *mii)
541{
542	struct mii_softc *child;
543	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
544	int rv;
545
546	mii->mii_media_status = 0;
547	mii->mii_media_active = IFM_NONE;
548
549	LIST_FOREACH(child, &mii->mii_phys, mii_list) {
550		/*
551		 * If the media indicates a different PHY instance,
552		 * isolate this one.
553		 */
554		if (IFM_INST(ife->ifm_media) != child->mii_inst) {
555			if ((child->mii_flags & MIIF_NOISOLATE) != 0) {
556				device_printf(child->mii_dev, "%s: "
557				    "can't handle non-zero PHY instance %d\n",
558				    __func__, child->mii_inst);
559				continue;
560			}
561			PHY_WRITE(child, MII_BMCR, PHY_READ(child, MII_BMCR) |
562			    BMCR_ISO);
563			continue;
564		}
565		rv = PHY_SERVICE(child, mii, MII_MEDIACHG);
566		if (rv)
567			return (rv);
568	}
569	return (0);
570}
571
572/*
573 * Call the PHY tick routines, used during autonegotiation.
574 */
575void
576mii_tick(struct mii_data *mii)
577{
578	struct mii_softc *child;
579	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
580
581	LIST_FOREACH(child, &mii->mii_phys, mii_list) {
582		/*
583		 * If this PHY instance isn't currently selected, just skip
584		 * it.
585		 */
586		if (IFM_INST(ife->ifm_media) != child->mii_inst)
587			continue;
588		(void)PHY_SERVICE(child, mii, MII_TICK);
589	}
590}
591
592/*
593 * Get media status from PHYs.
594 */
595void
596mii_pollstat(struct mii_data *mii)
597{
598	struct mii_softc *child;
599	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
600
601	mii->mii_media_status = 0;
602	mii->mii_media_active = IFM_NONE;
603
604	LIST_FOREACH(child, &mii->mii_phys, mii_list) {
605		/*
606		 * If we're not polling this PHY instance, just skip it.
607		 */
608		if (IFM_INST(ife->ifm_media) != child->mii_inst)
609			continue;
610		(void)PHY_SERVICE(child, mii, MII_POLLSTAT);
611	}
612}
613
614/*
615 * Inform the PHYs that the interface is down.
616 */
617void
618mii_down(struct mii_data *mii)
619{
620	struct mii_softc *child;
621
622	LIST_FOREACH(child, &mii->mii_phys, mii_list)
623		mii_phy_down(child);
624}
625
626static unsigned char
627mii_bitreverse(unsigned char x)
628{
629	static unsigned const char nibbletab[16] = {
630		0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15
631	};
632
633	return ((nibbletab[x & 15] << 4) | nibbletab[x >> 4]);
634}
635
636u_int
637mii_oui(u_int id1, u_int id2)
638{
639	u_int h;
640
641	h = (id1 << 6) | (id2 >> 10);
642
643	return ((mii_bitreverse(h >> 16) << 16) |
644	    (mii_bitreverse((h >> 8) & 0xff) << 8) |
645	    mii_bitreverse(h & 0xff));
646}
647