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