Home | History | Annotate | Line # | Download | only in mii
mii.c revision 1.2
      1 /*	$NetBSD: mii.c,v 1.2 1997/11/17 08:52:38 thorpej Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 1997 Manuel Bouyer.  All rights reserved.
      5  *
      6  * Redistribution and use in source and binary forms, with or without
      7  * modification, are permitted provided that the following conditions
      8  * are met:
      9  * 1. Redistributions of source code must retain the above copyright
     10  *    notice, this list of conditions and the following disclaimer.
     11  * 2. Redistributions in binary form must reproduce the above copyright
     12  *    notice, this list of conditions and the following disclaimer in the
     13  *    documentation and/or other materials provided with the distribution.
     14  * 3. All advertising materials mentioning features or use of this software
     15  *    must display the following acknowledgement:
     16  *	This product includes software developed by Manuel Bouyer.
     17  * 4. The name of the author may not be used to endorse or promote products
     18  *    derived from this software without specific prior written permission.
     19  *
     20  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     21  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     22  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     23  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     24  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
     25  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     26  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     27  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     28  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     29  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     30  */
     31 
     32 #include <sys/param.h>
     33 #include <sys/systm.h>
     34 #include <sys/kernel.h>
     35 #include <sys/device.h>
     36 #include <sys/malloc.h>
     37 #include <sys/socket.h>
     38 #include <sys/sockio.h>
     39 
     40 #include <net/if.h>
     41 #if defined(SIOCSIFMEDIA)
     42 #include <net/if_media.h>
     43 #endif
     44 
     45 #include <dev/mii/mii_adapter.h>
     46 #include <dev/mii/mii_phy.h>
     47 #include <dev/mii/generic_phy.h>
     48 
     49 #include "locators.h"
     50 
     51 /* The mii bus private data definitions */
     52 
     53 struct mii_softc {
     54 	struct device sc_dev;
     55 	mii_data_t *adapter;
     56 	mii_phy_t *phy[32];
     57 	mii_phy_t *current_phy;
     58 };
     59 
     60 static void mii_sync __P((mii_data_t *));
     61 static void mii_sendbit __P((mii_data_t *, u_int32_t, int));
     62 
     63 #ifdef __BROKEN_INDIRECT_CONFIG
     64 int miimatch __P((struct device *, void *, void *));
     65 int mii_configmatch __P((struct device *, void *, void *));
     66 #else
     67 int miimatch __P((struct device *, struct cfdata *, void *));
     68 int mii_configmatch __P((struct device *, struct cfdata *, void *));
     69 #endif
     70 void miiattach __P((struct device *, struct device *, void *));
     71 
     72 int mii_print __P((void *, const char *));
     73 
     74 struct cfattach mii_ca = {
     75 	sizeof(struct mii_softc), miimatch, miiattach
     76 };
     77 
     78 struct cfdriver mii_cd = {
     79 	NULL, "mii", DV_DULL
     80 };
     81 
     82 int
     83 mii_print(aux, pnp)
     84 	void *aux;
     85 	const char *pnp;
     86 {
     87 	mii_phy_t *phy = aux;
     88 
     89 	if (pnp)
     90 		printf("PHY ID 0x%x at %s", phy->phy_id, pnp);
     91 	printf(" dev %d", phy->dev);
     92 	return (UNCONF);
     93 }
     94 
     95 #ifdef __BROKEN_INDIRECT_CONFIG
     96 int
     97 miimatch(parent, match, aux)
     98 	struct device *parent;
     99 	void *match, *aux;
    100 {
    101 #else
    102 int
    103 miimatch(parent, cf, aux)
    104 	struct device *parent;
    105 	struct cfdata *cf;
    106 	void *aux;
    107 {
    108 #endif
    109 	return 1;
    110 }
    111 
    112 void
    113 miiattach(parent, self, aux)
    114 	struct device *parent, *self;
    115 	void *aux;
    116 {
    117 	int phy_id_l, phy_id_h;
    118 	int i;
    119 	mii_phy_t *phy;
    120 	struct mii_softc *sc = (struct mii_softc *)self;
    121 	mii_data_t *adapter = aux;
    122 	/* struct cfdata *cf; */
    123 
    124 	printf("\n");
    125 	sc->adapter = adapter;
    126 	sc->adapter->mii_sc = sc;
    127 	sc->current_phy = NULL;
    128 
    129 	for (i = 0; i < 32; i++) {
    130 		phy_id_h = mii_readreg(sc, i, PHY_IDH);
    131 		phy_id_l = mii_readreg(sc, i, PHY_IDL);
    132 #ifdef MII_DEBUG
    133 		printf("Id of PHY 0x%x: 0x%x%x\n", i, phy_id_h, phy_id_l);
    134 #endif
    135 		if (phy_id_h != -1 && phy_id_l != -1) {
    136 			phy = malloc(sizeof(mii_phy_t), M_DEVBUF, M_WAITOK);
    137 			phy->phy_id = ((phy_id_h & 0xffff) << 16) |
    138 			    (phy_id_l & 0xffff);
    139 			phy->adapter_id = adapter->adapter_id;
    140 			phy->dev = i;
    141 			phy->mii_softc = sc;
    142 #if 0
    143 			if ((cf = config_search(mii_configmatch, self,
    144 			    phy)) != NULL) {
    145 				sc->phy[i] = phy;
    146 				config_attach(self, cf, phy, mii_print);
    147 			} else {
    148 				sc->phy[i] = NULL;
    149 				mii_print(phy, sc->sc_dev.dv_xname);
    150 				printf(" not configured\n");
    151 				free(phy, M_DEVBUF);
    152 			}
    153 #else
    154 			if (config_found_sm(self, phy, mii_print,
    155 			    mii_configmatch) != NULL) {
    156 				sc->phy[i] = phy;
    157 			} else {
    158 				sc->phy[i] = NULL;
    159 				free(phy, M_DEVBUF);
    160 			}
    161 #endif
    162 		}
    163 	}
    164 }
    165 
    166 #ifdef __BROKEN_INDIRECT_CONFIG
    167 int
    168 mii_configmatch(parent, match, aux)
    169 	struct device *parent;
    170 	void *match, *aux;
    171 {
    172 	struct cfdata *cf =match;
    173 #else
    174 int
    175 mii_configmatch(parent, cf, aux)
    176 	struct device *parent;
    177 	struct cfdata *cf;
    178 	void *aux;
    179 {
    180 #endif
    181 	mii_phy_t *phy = aux;
    182 
    183 	if (cf->cf_loc[MIICF_DEV] != MIICF_DEV_DEFAULT &&
    184 	    cf->cf_loc[MIICF_DEV] != phy->dev)
    185 		return (0);
    186 	return ((*cf->cf_attach->ca_match)(parent, cf, aux));
    187 }
    188 
    189 static void
    190 mii_sync(adapter)
    191 	mii_data_t* adapter;
    192 {
    193 	int i;
    194 
    195 	(*adapter->mii_clrbit)(adapter->adapter_softc, MII_TXEN);
    196 	for (i = 0; i < 32; i++) {
    197 		(*adapter->mii_clrbit)(adapter->adapter_softc, MII_CLOCK);
    198 		(*adapter->mii_setbit)(adapter->adapter_softc, MII_CLOCK);
    199 	}
    200 }
    201 
    202 static void
    203 mii_sendbit(adapter, data, nbits)
    204 	mii_data_t *adapter;
    205 	u_int32_t data;
    206 	int nbits;
    207 {
    208 	int i;
    209 
    210 	(*adapter->mii_setbit)(adapter->adapter_softc, MII_TXEN);
    211 	for (i = 1 << (nbits -1); i; i = i >>  1) {
    212 		(*adapter->mii_clrbit)(adapter->adapter_softc, MII_CLOCK);
    213 		(*adapter->mii_readbit)(adapter->adapter_softc, MII_CLOCK);
    214 		if (data & i)
    215 			(*adapter->mii_setbit)(adapter->adapter_softc,
    216 			    MII_DATA);
    217 		else
    218 			(*adapter->mii_clrbit)(adapter->adapter_softc,
    219 			    MII_DATA);
    220 		(*adapter->mii_setbit)(adapter->adapter_softc, MII_CLOCK);
    221 		(*adapter->mii_readbit)(adapter->adapter_softc, MII_CLOCK);
    222 	}
    223 }
    224 
    225 int
    226 mii_readreg(v, phy, reg)
    227 	void *v;
    228 	u_int16_t phy;
    229 	u_int16_t reg;
    230 {
    231 	mii_data_t *adapter = ((struct mii_softc *)v)->adapter;
    232 	u_int16_t val = 0;
    233 	int err =0;
    234 	int i;
    235 
    236 	if (adapter->mii_readreg) /* adapter has a special way to read PHYs */
    237 		return ((*adapter->mii_readreg)(adapter->adapter_softc,
    238 		    phy, reg));
    239 
    240 	/* else read using the control lines */
    241 	mii_sync(adapter);
    242 	mii_sendbit(adapter, MII_START, 2);
    243 	mii_sendbit(adapter, MII_READ, 2);
    244 	mii_sendbit(adapter, phy, 5);
    245 	mii_sendbit(adapter, reg, 5);
    246 
    247 	(*adapter->mii_clrbit)(adapter->adapter_softc, MII_TXEN);
    248 	(*adapter->mii_clrbit)(adapter->adapter_softc, MII_CLOCK);
    249 	(*adapter->mii_setbit)(adapter->adapter_softc, MII_CLOCK);
    250 	(*adapter->mii_clrbit)(adapter->adapter_softc, MII_CLOCK);
    251 
    252 	err = (*adapter->mii_readbit)(adapter->adapter_softc, MII_DATA);
    253 	(*adapter->mii_setbit)(adapter->adapter_softc, MII_CLOCK);
    254 
    255 	for (i = 0; i < 16; i++) {
    256 		val = val << 1;
    257 		(*adapter->mii_clrbit)(adapter->adapter_softc, MII_CLOCK);
    258 		if (err == 0)
    259 			if ((*adapter->mii_readbit)(adapter->adapter_softc,
    260 			    MII_DATA))
    261 				val |= 1;
    262 		(*adapter->mii_setbit)(adapter->adapter_softc, MII_CLOCK);
    263 	}
    264 	(*adapter->mii_clrbit)(adapter->adapter_softc, MII_CLOCK);
    265 	(*adapter->mii_setbit)(adapter->adapter_softc, MII_CLOCK);
    266 
    267 	if (err == 0)
    268 		return val;
    269 	else
    270 		return -1;
    271 }
    272 
    273 void
    274 mii_writereg(v, phy, reg, data)
    275 	void *v;
    276 	u_int16_t phy;
    277 	u_int16_t reg;
    278 	u_int16_t data;
    279 {
    280 	mii_data_t *adapter = ((struct mii_softc *)v)->adapter;
    281 
    282 	if (adapter->mii_writereg) {
    283 		/* Interface has a special way of writing to the PHY. */
    284 		(*adapter->mii_writereg)(adapter, phy, reg, data);
    285 		return;
    286 	}
    287 
    288 	/* else write using the control lines */
    289 	mii_sync(adapter);
    290 	mii_sendbit(adapter, MII_START, 2);
    291 	mii_sendbit(adapter, MII_WRITE, 2);
    292 	mii_sendbit(adapter, phy, 5);
    293 	mii_sendbit(adapter, reg, 5);
    294 	mii_sendbit(adapter, MII_ACK, 2);
    295 	mii_sendbit(adapter, data, 16);
    296 
    297 	(*adapter->mii_clrbit)(adapter->adapter_softc, MII_CLOCK);
    298 	(*adapter->mii_setbit)(adapter->adapter_softc, MII_CLOCK);
    299 }
    300 
    301 void
    302 mii_media_add(ifmedia, adapter)
    303 	struct ifmedia *ifmedia;
    304 	mii_data_t *adapter;
    305 {
    306 	struct mii_softc *sc = adapter->mii_sc;
    307 	int i;
    308 	u_int32_t media = 0;
    309 
    310 	for (i = 0; i < 32; i++) {
    311 		if (sc->phy[i])
    312 			media |= sc->phy[i]->phy_media;
    313 	}
    314 	if (media & PHY_BNC)
    315 		ifmedia_add(ifmedia, IFM_ETHER | IFM_10_2, 0, NULL);
    316 	if (media & PHY_AUI)
    317 		ifmedia_add(ifmedia, IFM_ETHER | IFM_10_5, 0, NULL);
    318 	if (media & PHY_10baseT)
    319 		ifmedia_add(ifmedia, IFM_ETHER | IFM_10_T, 0, NULL);
    320 	if (media & PHY_10baseTfd)
    321 		ifmedia_add(ifmedia, IFM_ETHER | IFM_10_T | IFM_FDX, 0, NULL);
    322 	if (media & PHY_100baseTx)
    323 		ifmedia_add(ifmedia, IFM_ETHER | IFM_100_TX, 0, NULL);
    324 	if (media & PHY_100baseTxfd)
    325 		ifmedia_add(ifmedia, IFM_ETHER | IFM_100_TX | IFM_FDX, 0, NULL);
    326 	if (media & PHY_100baseT4)
    327 		ifmedia_add(ifmedia, IFM_ETHER | IFM_100_T4, 0, NULL);
    328 	ifmedia_add(ifmedia, IFM_ETHER | IFM_NONE, 0, NULL);
    329 }
    330 
    331 int
    332 mii_mediachg(adapter)
    333 	mii_data_t *adapter;
    334 {
    335 	struct mii_softc *sc = adapter->mii_sc;
    336 	int i, best = -1, error = 0;
    337 	int media = adapter->mii_media_active;
    338 
    339 	sc->current_phy = NULL;
    340 
    341 	for (i = 0; i < 32; i++) {
    342 		if (sc->phy[i] == NULL)
    343 			continue;
    344 		switch (sc->phy[i]->phy_media_set(media,
    345 		    sc->phy[i]->phy_softc)) {
    346 		case -1:	/* PHY not available */
    347 			break;
    348 		case 0:		/* link sucessfully selected */
    349 			sc->current_phy = sc->phy[i];
    350 			break;
    351 		case ENETDOWN:	/* link selected but not up */
    352 			best = i;
    353 			break;
    354 		default:
    355 			break;
    356 		}
    357 	}
    358 	if (sc->current_phy == NULL) {
    359 		/*
    360 		 * We didn't find a valid media. Select the best one (i.e.
    361 		 * last supported but not up). If media != autoselect,
    362 		 * don't report any error code.
    363 		 */
    364 		if (best < 0)
    365 			return (EINVAL);
    366 		sc->current_phy = sc->phy[best];
    367 		error = sc->phy[best]->phy_media_set(media,
    368 		    sc->phy[best]->phy_softc);
    369 		if (media != IFM_AUTO)
    370 			error = 0;
    371 	}
    372 	/* power down all but current phy */
    373 	for (i = 0; i < 32; i++) {
    374 		if (sc->phy[i] != sc->current_phy) {
    375 			if (sc->phy[i] == NULL)
    376 				mii_writereg(sc, i, PHY_CONTROL, CTRL_ISO);
    377 			else
    378 				sc->phy[i]->phy_pdown(sc->phy[i]->phy_softc);
    379 		}
    380 	}
    381 	return (error);
    382 }
    383 
    384 void
    385 mii_pollstat(adapter)
    386 	mii_data_t *adapter;
    387 {
    388 	struct mii_softc *sc = adapter->mii_sc;
    389 
    390 	adapter->mii_media_status = IFM_AVALID;
    391 	if (sc->current_phy == NULL)
    392 		return;
    393 	if ((*sc->current_phy->phy_status)(adapter->mii_media_active,
    394 	    sc->current_phy->phy_softc) == 0)
    395 		adapter->mii_media_status |= IFM_ACTIVE;
    396 }
    397