| /* 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; |
| } |