Home | History | Annotate | Line # | Download | only in mii
mii.c revision 1.4
      1 /*	$NetBSD: mii.c,v 1.4 1998/02/11 19:02:11 bouyer 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 int mii_adapter_print(aux, pnp)
     79     void *aux;
     80 	const char *pnp;
     81 {
     82 	if (pnp)
     83 		printf("mii at %s", pnp);
     84 	return UNCONF;
     85 }
     86 
     87 int
     88 mii_print(aux, pnp)
     89 	void *aux;
     90 	const char *pnp;
     91 {
     92 	mii_phy_t *phy = aux;
     93 
     94 	if (pnp)
     95 		printf("PHY ID 0x%x at %s", phy->phy_id, pnp);
     96 	printf(" dev %d", phy->dev);
     97 	return (UNCONF);
     98 }
     99 
    100 #ifdef __BROKEN_INDIRECT_CONFIG
    101 int
    102 miimatch(parent, match, aux)
    103 	struct device *parent;
    104 	void *match, *aux;
    105 {
    106 #else
    107 int
    108 miimatch(parent, cf, aux)
    109 	struct device *parent;
    110 	struct cfdata *cf;
    111 	void *aux;
    112 {
    113 #endif
    114 	return 1;
    115 }
    116 
    117 void
    118 miiattach(parent, self, aux)
    119 	struct device *parent, *self;
    120 	void *aux;
    121 {
    122 	int phy_id_l, phy_id_h;
    123 	int i;
    124 	mii_phy_t *phy;
    125 	struct mii_softc *sc = (struct mii_softc *)self;
    126 	mii_data_t *adapter = aux;
    127 	/* struct cfdata *cf; */
    128 
    129 	printf("\n");
    130 	sc->adapter = adapter;
    131 	sc->adapter->mii_sc = sc;
    132 	sc->current_phy = NULL;
    133 
    134 	for (i = 0; i < 32; i++) {
    135 		phy_id_h = mii_readreg(sc, i, PHY_IDH);
    136 		phy_id_l = mii_readreg(sc, i, PHY_IDL);
    137 #ifdef MII_DEBUG
    138 		printf("Id of PHY 0x%x: 0x%x%x\n", i, phy_id_h, phy_id_l);
    139 #endif
    140 		if (phy_id_h != -1 && phy_id_l != -1) {
    141 			phy = malloc(sizeof(mii_phy_t), M_DEVBUF, M_WAITOK);
    142 			phy->phy_id = ((phy_id_h & 0xffff) << 16) |
    143 			    (phy_id_l & 0xffff);
    144 			phy->adapter_id = adapter->adapter_id;
    145 			phy->dev = i;
    146 			phy->mii_softc = sc;
    147 #if 0
    148 			if ((cf = config_search(mii_configmatch, self,
    149 			    phy)) != NULL) {
    150 				sc->phy[i] = phy;
    151 				config_attach(self, cf, phy, mii_print);
    152 			} else {
    153 				sc->phy[i] = NULL;
    154 				mii_print(phy, sc->sc_dev.dv_xname);
    155 				printf(" not configured\n");
    156 				free(phy, M_DEVBUF);
    157 			}
    158 #else
    159 			if (config_found_sm(self, phy, mii_print,
    160 			    mii_configmatch) != NULL) {
    161 				sc->phy[i] = phy;
    162 			} else {
    163 				sc->phy[i] = NULL;
    164 				free(phy, M_DEVBUF);
    165 			}
    166 #endif
    167 		}
    168 	}
    169 }
    170 
    171 #ifdef __BROKEN_INDIRECT_CONFIG
    172 int
    173 mii_configmatch(parent, match, aux)
    174 	struct device *parent;
    175 	void *match, *aux;
    176 {
    177 	struct cfdata *cf =match;
    178 #else
    179 int
    180 mii_configmatch(parent, cf, aux)
    181 	struct device *parent;
    182 	struct cfdata *cf;
    183 	void *aux;
    184 {
    185 #endif
    186 	mii_phy_t *phy = aux;
    187 
    188 	if (cf->cf_loc[MIICF_DEV] != MIICF_DEV_DEFAULT &&
    189 	    cf->cf_loc[MIICF_DEV] != phy->dev)
    190 		return (0);
    191 	return ((*cf->cf_attach->ca_match)(parent, cf, aux));
    192 }
    193 
    194 static void
    195 mii_sync(adapter)
    196 	mii_data_t* adapter;
    197 {
    198 	int i;
    199 
    200 	(*adapter->mii_clrbit)(adapter->adapter_softc, MII_TXEN);
    201 	for (i = 0; i < 32; i++) {
    202 		(*adapter->mii_clrbit)(adapter->adapter_softc, MII_CLOCK);
    203 		(*adapter->mii_setbit)(adapter->adapter_softc, MII_CLOCK);
    204 	}
    205 }
    206 
    207 static void
    208 mii_sendbit(adapter, data, nbits)
    209 	mii_data_t *adapter;
    210 	u_int32_t data;
    211 	int nbits;
    212 {
    213 	int i;
    214 
    215 	(*adapter->mii_setbit)(adapter->adapter_softc, MII_TXEN);
    216 	for (i = 1 << (nbits -1); i; i = i >>  1) {
    217 		(*adapter->mii_clrbit)(adapter->adapter_softc, MII_CLOCK);
    218 		(*adapter->mii_readbit)(adapter->adapter_softc, MII_CLOCK);
    219 		if (data & i)
    220 			(*adapter->mii_setbit)(adapter->adapter_softc,
    221 			    MII_DATA);
    222 		else
    223 			(*adapter->mii_clrbit)(adapter->adapter_softc,
    224 			    MII_DATA);
    225 		(*adapter->mii_setbit)(adapter->adapter_softc, MII_CLOCK);
    226 		(*adapter->mii_readbit)(adapter->adapter_softc, MII_CLOCK);
    227 	}
    228 }
    229 
    230 int
    231 mii_readreg(v, phy, reg)
    232 	void *v;
    233 	u_int16_t phy;
    234 	u_int16_t reg;
    235 {
    236 	mii_data_t *adapter = ((struct mii_softc *)v)->adapter;
    237 	u_int16_t val = 0;
    238 	int err =0;
    239 	int i;
    240 
    241 	if (adapter->mii_readreg) /* adapter has a special way to read PHYs */
    242 		return ((*adapter->mii_readreg)(adapter->adapter_softc,
    243 		    phy, reg));
    244 
    245 	/* else read using the control lines */
    246 	mii_sync(adapter);
    247 	mii_sendbit(adapter, MII_START, 2);
    248 	mii_sendbit(adapter, MII_READ, 2);
    249 	mii_sendbit(adapter, phy, 5);
    250 	mii_sendbit(adapter, reg, 5);
    251 
    252 	(*adapter->mii_clrbit)(adapter->adapter_softc, MII_TXEN);
    253 	(*adapter->mii_clrbit)(adapter->adapter_softc, MII_CLOCK);
    254 	(*adapter->mii_setbit)(adapter->adapter_softc, MII_CLOCK);
    255 	(*adapter->mii_clrbit)(adapter->adapter_softc, MII_CLOCK);
    256 
    257 	err = (*adapter->mii_readbit)(adapter->adapter_softc, MII_DATA);
    258 	(*adapter->mii_setbit)(adapter->adapter_softc, MII_CLOCK);
    259 
    260 	for (i = 0; i < 16; i++) {
    261 		val = val << 1;
    262 		(*adapter->mii_clrbit)(adapter->adapter_softc, MII_CLOCK);
    263 		if (err == 0)
    264 			if ((*adapter->mii_readbit)(adapter->adapter_softc,
    265 			    MII_DATA))
    266 				val |= 1;
    267 		(*adapter->mii_setbit)(adapter->adapter_softc, MII_CLOCK);
    268 	}
    269 	(*adapter->mii_clrbit)(adapter->adapter_softc, MII_CLOCK);
    270 	(*adapter->mii_setbit)(adapter->adapter_softc, MII_CLOCK);
    271 
    272 	if (err == 0)
    273 		return val;
    274 	else
    275 		return -1;
    276 }
    277 
    278 void
    279 mii_writereg(v, phy, reg, data)
    280 	void *v;
    281 	u_int16_t phy;
    282 	u_int16_t reg;
    283 	u_int16_t data;
    284 {
    285 	mii_data_t *adapter = ((struct mii_softc *)v)->adapter;
    286 
    287 	if (adapter->mii_writereg) {
    288 		/* Interface has a special way of writing to the PHY. */
    289 		(*adapter->mii_writereg)(adapter, phy, reg, data);
    290 		return;
    291 	}
    292 
    293 	/* else write using the control lines */
    294 	mii_sync(adapter);
    295 	mii_sendbit(adapter, MII_START, 2);
    296 	mii_sendbit(adapter, MII_WRITE, 2);
    297 	mii_sendbit(adapter, phy, 5);
    298 	mii_sendbit(adapter, reg, 5);
    299 	mii_sendbit(adapter, MII_ACK, 2);
    300 	mii_sendbit(adapter, data, 16);
    301 
    302 	(*adapter->mii_clrbit)(adapter->adapter_softc, MII_CLOCK);
    303 	(*adapter->mii_setbit)(adapter->adapter_softc, MII_CLOCK);
    304 }
    305 
    306 void
    307 mii_media_add(ifmedia, adapter)
    308 	struct ifmedia *ifmedia;
    309 	mii_data_t *adapter;
    310 {
    311 	struct mii_softc *sc = adapter->mii_sc;
    312 	int i;
    313 	u_int32_t media = 0;
    314 
    315 	for (i = 0; i < 32; i++) {
    316 		if (sc->phy[i])
    317 			media |= sc->phy[i]->phy_media;
    318 	}
    319 	if (media & PHY_BNC)
    320 		ifmedia_add(ifmedia, IFM_ETHER | IFM_10_2, 0, NULL);
    321 	if (media & PHY_AUI)
    322 		ifmedia_add(ifmedia, IFM_ETHER | IFM_10_5, 0, NULL);
    323 	if (media & PHY_10baseT)
    324 		ifmedia_add(ifmedia, IFM_ETHER | IFM_10_T, 0, NULL);
    325 	if (media & PHY_10baseTfd)
    326 		ifmedia_add(ifmedia, IFM_ETHER | IFM_10_T | IFM_FDX, 0, NULL);
    327 	if (media & PHY_100baseTx)
    328 		ifmedia_add(ifmedia, IFM_ETHER | IFM_100_TX, 0, NULL);
    329 	if (media & PHY_100baseTxfd)
    330 		ifmedia_add(ifmedia, IFM_ETHER | IFM_100_TX | IFM_FDX, 0, NULL);
    331 	if (media & PHY_100baseT4)
    332 		ifmedia_add(ifmedia, IFM_ETHER | IFM_100_T4, 0, NULL);
    333 	ifmedia_add(ifmedia, IFM_ETHER | IFM_NONE, 0, NULL);
    334 }
    335 
    336 int
    337 mii_mediachg(adapter)
    338 	mii_data_t *adapter;
    339 {
    340 	struct mii_softc *sc = adapter->mii_sc;
    341 	int i, best = -1, error = 0;
    342 	int media = adapter->mii_media_active;
    343 
    344 	sc->current_phy = NULL;
    345 
    346 	for (i = 0; i < 32; i++) {
    347 		if (sc->phy[i] == NULL)
    348 			continue;
    349 		switch (sc->phy[i]->phy_media_set(media,
    350 		    sc->phy[i]->phy_softc)) {
    351 		case -1:	/* PHY not available */
    352 			break;
    353 		case 0:		/* link sucessfully selected */
    354 			sc->current_phy = sc->phy[i];
    355 			break;
    356 		case ENETDOWN:	/* link selected but not up */
    357 			best = i;
    358 			break;
    359 		default:
    360 			break;
    361 		}
    362 	}
    363 	if (sc->current_phy == NULL) {
    364 		/*
    365 		 * We didn't find a valid media. Select the best one (i.e.
    366 		 * last supported but not up). If media != autoselect,
    367 		 * don't report any error code.
    368 		 */
    369 		if (best < 0)
    370 			return (EINVAL);
    371 		sc->current_phy = sc->phy[best];
    372 		error = sc->phy[best]->phy_media_set(media,
    373 		    sc->phy[best]->phy_softc);
    374 		if (media != IFM_AUTO)
    375 			error = 0;
    376 	}
    377 	/* power down all but current phy */
    378 	for (i = 0; i < 32; i++) {
    379 		if (sc->phy[i] != sc->current_phy) {
    380 			if (sc->phy[i] == NULL)
    381 				mii_writereg(sc, i, PHY_CONTROL, CTRL_ISO);
    382 			else
    383 				sc->phy[i]->phy_pdown(sc->phy[i]->phy_softc);
    384 		}
    385 	}
    386 	return (error);
    387 }
    388 
    389 void
    390 mii_pollstat(adapter)
    391 	mii_data_t *adapter;
    392 {
    393 	struct mii_softc *sc = adapter->mii_sc;
    394 
    395 	adapter->mii_media_status = IFM_AVALID;
    396 	if (sc->current_phy == NULL)
    397 		return;
    398 	if ((*sc->current_phy->phy_status)(adapter->mii_media_active,
    399 	    sc->current_phy->phy_softc) == 0)
    400 		adapter->mii_media_status |= IFM_ACTIVE;
    401 }
    402