blob: 6aa46cefbf28572bdd79e9b8a178cb25d0669edc [file] [log] [blame]
/* 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;
}