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