|  | /* This file is part of the UCB release of Plan 9. It is subject to the license | 
|  | * terms in the LICENSE file found in the top-level directory of this | 
|  | * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No | 
|  | * part of the UCB release of Plan 9, including this file, may be copied, | 
|  | * modified, propagated, or distributed except according to the terms contained | 
|  | * in the LICENSE file. */ | 
|  |  | 
|  | #include <assert.h> | 
|  | #include <cpio.h> | 
|  | #include <error.h> | 
|  | #include <kmalloc.h> | 
|  | #include <kref.h> | 
|  | #include <pmap.h> | 
|  | #include <slab.h> | 
|  | #include <smp.h> | 
|  | #include <stdio.h> | 
|  | #include <string.h> | 
|  |  | 
|  | #include "ethermii.h" | 
|  |  | 
|  | static int miiprobe(struct mii *mii, int mask) | 
|  | { | 
|  | struct miiphy *miiphy; | 
|  | int bit, oui, phyno, r, rmask; | 
|  |  | 
|  | /* | 
|  | * Probe through mii for PHYs in mask; | 
|  | * return the mask of those found in the current probe. | 
|  | * If the PHY has not already been probed, update | 
|  | * the Mii information. | 
|  | */ | 
|  | rmask = 0; | 
|  | for (phyno = 0; phyno < NMiiPhy; phyno++) { | 
|  | bit = 1 << phyno; | 
|  | if (!(mask & bit)) | 
|  | continue; | 
|  | if (mii->mask & bit) { | 
|  | rmask |= bit; | 
|  | continue; | 
|  | } | 
|  | if (mii->rw(mii, 0, phyno, Bmsr, 0) == -1) | 
|  | continue; | 
|  | r = mii->rw(mii, 0, phyno, Phyidr1, 0) << 16; | 
|  | r |= mii->rw(mii, 0, phyno, Phyidr2, 0); | 
|  | oui = (r >> 10) & 0xffff; | 
|  | if (oui == 0xffff || oui == 0) | 
|  | continue; | 
|  |  | 
|  | if ((miiphy = kzmalloc(sizeof(struct miiphy), 0)) == NULL) | 
|  | continue; | 
|  |  | 
|  | miiphy->mii = mii; | 
|  | miiphy->phyno = phyno; | 
|  | miiphy->phyid = r; | 
|  | miiphy->oui = oui; | 
|  |  | 
|  | miiphy->anar = ~0; | 
|  | miiphy->fc = ~0; | 
|  | miiphy->mscr = ~0; | 
|  |  | 
|  | mii->phy[phyno] = miiphy; | 
|  | if (mii->curphy == NULL) | 
|  | mii->curphy = miiphy; | 
|  | mii->mask |= bit; | 
|  | mii->nphy++; | 
|  |  | 
|  | rmask |= bit; | 
|  | } | 
|  | return rmask; | 
|  | } | 
|  |  | 
|  | int miimir(struct mii *mii, int r) | 
|  | { | 
|  | if (mii == NULL || mii->ctlr == NULL || mii->curphy == NULL) | 
|  | return -1; | 
|  | return mii->rw(mii, 0, mii->curphy->phyno, r, 0); | 
|  | } | 
|  |  | 
|  | int miimiw(struct mii *mii, int r, int data) | 
|  | { | 
|  | if (mii == NULL || mii->ctlr == NULL || mii->curphy == NULL) | 
|  | return -1; | 
|  | return mii->rw(mii, 1, mii->curphy->phyno, r, data); | 
|  | } | 
|  |  | 
|  | int miireset(struct mii *mii) | 
|  | { | 
|  | int bmcr, timeo; | 
|  |  | 
|  | if (mii == NULL || mii->ctlr == NULL || mii->curphy == NULL) | 
|  | return -1; | 
|  | bmcr = mii->rw(mii, 0, mii->curphy->phyno, Bmcr, 0); | 
|  | mii->rw(mii, 1, mii->curphy->phyno, Bmcr, BmcrR | bmcr); | 
|  | for (timeo = 0; timeo < 1000; timeo++) { | 
|  | bmcr = mii->rw(mii, 0, mii->curphy->phyno, Bmcr, 0); | 
|  | if (!(bmcr & BmcrR)) | 
|  | break; | 
|  | udelay(1); | 
|  | } | 
|  | if (bmcr & BmcrR) | 
|  | return -1; | 
|  | if (bmcr & BmcrI) | 
|  | mii->rw(mii, 1, mii->curphy->phyno, Bmcr, bmcr & ~BmcrI); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | int miiane(struct mii *mii, int a, int p, int e) | 
|  | { | 
|  | int anar, bmsr, mscr, r, phyno; | 
|  |  | 
|  | if (mii == NULL || mii->ctlr == NULL || mii->curphy == NULL) | 
|  | return -1; | 
|  | phyno = mii->curphy->phyno; | 
|  |  | 
|  | mii->rw(mii, 1, phyno, Bmsr, 0); | 
|  | bmsr = mii->rw(mii, 0, phyno, Bmsr, 0); | 
|  | if (!(bmsr & BmsrAna)) | 
|  | return -1; | 
|  |  | 
|  | if (a != ~0) | 
|  | anar = (AnaTXFD | AnaTXHD | Ana10FD | Ana10HD) & a; | 
|  | else if (mii->curphy->anar != ~0) | 
|  | anar = mii->curphy->anar; | 
|  | else { | 
|  | anar = mii->rw(mii, 0, phyno, Anar, 0); | 
|  | anar &= ~(AnaAP | AnaP | AnaT4 | AnaTXFD | AnaTXHD | Ana10FD | | 
|  | Ana10HD); | 
|  | if (bmsr & Bmsr10THD) | 
|  | anar |= Ana10HD; | 
|  | if (bmsr & Bmsr10TFD) | 
|  | anar |= Ana10FD; | 
|  | if (bmsr & Bmsr100TXHD) | 
|  | anar |= AnaTXHD; | 
|  | if (bmsr & Bmsr100TXFD) | 
|  | anar |= AnaTXFD; | 
|  | } | 
|  | mii->curphy->anar = anar; | 
|  |  | 
|  | if (p != ~0) | 
|  | anar |= (AnaAP | AnaP) & p; | 
|  | else if (mii->curphy->fc != ~0) | 
|  | anar |= mii->curphy->fc; | 
|  | mii->curphy->fc = (AnaAP | AnaP) & anar; | 
|  |  | 
|  | if (bmsr & BmsrEs) { | 
|  | mscr = mii->rw(mii, 0, phyno, Mscr, 0); | 
|  | mscr &= ~(Mscr1000TFD | Mscr1000THD); | 
|  | if (e != ~0) | 
|  | mscr |= (Mscr1000TFD | Mscr1000THD) & e; | 
|  | else if (mii->curphy->mscr != ~0) | 
|  | mscr = mii->curphy->mscr; | 
|  | else { | 
|  | r = mii->rw(mii, 0, phyno, Esr, 0); | 
|  | if (r & Esr1000THD) | 
|  | mscr |= Mscr1000THD; | 
|  | if (r & Esr1000TFD) | 
|  | mscr |= Mscr1000TFD; | 
|  | } | 
|  | mii->curphy->mscr = mscr; | 
|  | mii->rw(mii, 1, phyno, Mscr, mscr); | 
|  | } else | 
|  | mii->curphy->mscr = 0; | 
|  | mii->rw(mii, 1, phyno, Anar, anar); | 
|  |  | 
|  | r = mii->rw(mii, 0, phyno, Bmcr, 0); | 
|  | if (!(r & BmcrR)) { | 
|  | r |= BmcrAne | BmcrRan; | 
|  | mii->rw(mii, 1, phyno, Bmcr, r); | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | int miistatus(struct mii *mii) | 
|  | { | 
|  | struct miiphy *phy; | 
|  | int anlpar, bmsr, p, r, phyno; | 
|  |  | 
|  | if (mii == NULL || mii->ctlr == NULL || mii->curphy == NULL) | 
|  | return -1; | 
|  | phy = mii->curphy; | 
|  | phyno = phy->phyno; | 
|  |  | 
|  | /* | 
|  | * Check Auto-Negotiation is complete and link is up. | 
|  | * (Read status twice as the Ls bit is sticky). | 
|  | */ | 
|  | bmsr = mii->rw(mii, 0, phyno, Bmsr, 0); | 
|  | if (!(bmsr & (BmsrAnc | BmsrAna))) | 
|  | return -1; | 
|  |  | 
|  | bmsr = mii->rw(mii, 0, phyno, Bmsr, 0); | 
|  | if (!(bmsr & BmsrLs)) { | 
|  | phy->link = 0; | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | phy->speed = phy->fd = phy->rfc = phy->tfc = 0; | 
|  | if (phy->mscr) { | 
|  | r = mii->rw(mii, 0, phyno, Mssr, 0); | 
|  | if ((phy->mscr & Mscr1000TFD) && (r & Mssr1000TFD)) { | 
|  | phy->speed = 1000; | 
|  | phy->fd = 1; | 
|  | } else if ((phy->mscr & Mscr1000THD) && (r & Mssr1000THD)) | 
|  | phy->speed = 1000; | 
|  | } | 
|  |  | 
|  | anlpar = mii->rw(mii, 0, phyno, Anlpar, 0); | 
|  | if (phy->speed == 0) { | 
|  | r = phy->anar & anlpar; | 
|  | if (r & AnaTXFD) { | 
|  | phy->speed = 100; | 
|  | phy->fd = 1; | 
|  | } else if (r & AnaTXHD) | 
|  | phy->speed = 100; | 
|  | else if (r & Ana10FD) { | 
|  | phy->speed = 10; | 
|  | phy->fd = 1; | 
|  | } else if (r & Ana10HD) | 
|  | phy->speed = 10; | 
|  | } | 
|  | if (phy->speed == 0) | 
|  | return -1; | 
|  |  | 
|  | if (phy->fd) { | 
|  | p = phy->fc; | 
|  | r = anlpar & (AnaAP | AnaP); | 
|  | if (p == AnaAP && r == (AnaAP | AnaP)) | 
|  | phy->tfc = 1; | 
|  | else if (p == (AnaAP | AnaP) && r == AnaAP) | 
|  | phy->rfc = 1; | 
|  | else if ((p & AnaP) && (r & AnaP)) | 
|  | phy->rfc = phy->tfc = 1; | 
|  | } | 
|  |  | 
|  | phy->link = 1; | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | char *miidumpphy(struct mii *mii, char *p, char *e) | 
|  | { | 
|  | int i, r; | 
|  |  | 
|  | if (mii == NULL || mii->curphy == NULL) | 
|  | return p; | 
|  |  | 
|  | p = seprintf(p, e, "phy:   "); | 
|  | for (i = 0; i < NMiiPhyr; i++) { | 
|  | if (i && ((i & 0x07) == 0)) | 
|  | p = seprintf(p, e, "\n       "); | 
|  | r = mii->rw(mii, 0, mii->curphy->phyno, i, 0); | 
|  | p = seprintf(p, e, " %4.4ux", r); | 
|  | } | 
|  | p = seprintf(p, e, "\n"); | 
|  |  | 
|  | return p; | 
|  | } | 
|  |  | 
|  | void miidetach(struct mii *mii) | 
|  | { | 
|  | int i; | 
|  |  | 
|  | for (i = 0; i < NMiiPhy; i++) { | 
|  | if (mii->phy[i] == NULL) | 
|  | continue; | 
|  | kfree(mii); | 
|  | mii->phy[i] = NULL; | 
|  | } | 
|  | kfree(mii); | 
|  | } | 
|  |  | 
|  | struct mii *miiattach(void *ctlr, int mask, | 
|  | int (*rw)(struct mii *, int unused_int, int, int, int)) | 
|  | { | 
|  | struct mii *mii; | 
|  |  | 
|  | if ((mii = kzmalloc(sizeof(struct mii), 0)) == NULL) | 
|  | return NULL; | 
|  | mii->ctlr = ctlr; | 
|  | mii->rw = rw; | 
|  |  | 
|  | if (miiprobe(mii, mask) == 0) { | 
|  | kfree(mii); | 
|  | mii = NULL; | 
|  | } | 
|  |  | 
|  | return mii; | 
|  | } |