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