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