f0fc84aba3
mdio interrupt command completion handling was broken, as the interrupt handler would clear the mii status register before mdiodone() sees it. handle errors in miistatus() and miiane(), to not get confused.
284 lines
5.4 KiB
C
284 lines
5.4 KiB
C
#include "u.h"
|
|
#include "../port/lib.h"
|
|
#include "mem.h"
|
|
#include "dat.h"
|
|
#include "fns.h"
|
|
#include "io.h"
|
|
#include "../port/error.h"
|
|
#include "../port/netif.h"
|
|
#include "../port/etherif.h"
|
|
|
|
#include "ethermii.h"
|
|
|
|
int
|
|
mii(Mii* mii, int mask)
|
|
{
|
|
MiiPhy *miiphy;
|
|
int bit, oui, phyno, rmask;
|
|
u32int id;
|
|
|
|
/*
|
|
* 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->mir(mii, phyno, Bmsr) == -1)
|
|
continue;
|
|
id = mii->mir(mii, phyno, Phyidr1) << 16;
|
|
id |= mii->mir(mii, phyno, Phyidr2);
|
|
oui = (id & 0x3FFFFC00)>>10;
|
|
if(oui == 0xFFFFF || oui == 0)
|
|
continue;
|
|
|
|
if((miiphy = malloc(sizeof(MiiPhy))) == nil)
|
|
continue;
|
|
|
|
miiphy->mii = mii;
|
|
miiphy->id = id;
|
|
miiphy->oui = oui;
|
|
miiphy->phyno = phyno;
|
|
|
|
miiphy->anar = ~0;
|
|
miiphy->fc = ~0;
|
|
miiphy->mscr = ~0;
|
|
|
|
mii->phy[phyno] = miiphy;
|
|
if(mii->curphy == nil)
|
|
mii->curphy = miiphy;
|
|
mii->mask |= bit;
|
|
mii->nphy++;
|
|
|
|
rmask |= bit;
|
|
}
|
|
return rmask;
|
|
}
|
|
|
|
int
|
|
miimir(Mii* mii, int r)
|
|
{
|
|
if(mii == nil || mii->ctlr == nil || mii->curphy == nil)
|
|
return -1;
|
|
return mii->mir(mii, mii->curphy->phyno, r);
|
|
}
|
|
|
|
int
|
|
miimiw(Mii* mii, int r, int data)
|
|
{
|
|
if(mii == nil || mii->ctlr == nil || mii->curphy == nil)
|
|
return -1;
|
|
return mii->miw(mii, mii->curphy->phyno, r, data);
|
|
}
|
|
|
|
int
|
|
miireset(Mii* mii)
|
|
{
|
|
int bmcr;
|
|
|
|
if(mii == nil || mii->ctlr == nil || mii->curphy == nil)
|
|
return -1;
|
|
bmcr = mii->mir(mii, mii->curphy->phyno, Bmcr);
|
|
if(bmcr == -1)
|
|
return -1;
|
|
bmcr |= BmcrR;
|
|
mii->miw(mii, mii->curphy->phyno, Bmcr, bmcr);
|
|
microdelay(1);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
miiane(Mii* mii, int a, int p, int e)
|
|
{
|
|
int anar, bmsr, mscr, r, phyno;
|
|
|
|
if(mii == nil || mii->ctlr == nil || mii->curphy == nil)
|
|
return -1;
|
|
phyno = mii->curphy->phyno;
|
|
|
|
bmsr = mii->mir(mii, phyno, Bmsr);
|
|
if(bmsr == -1)
|
|
return -1;
|
|
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->mir(mii, phyno, Anar);
|
|
if(anar == -1)
|
|
return -1;
|
|
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->mir(mii, phyno, Mscr);
|
|
if(mscr == -1)
|
|
return -1;
|
|
mscr &= ~(Mscr1000TFD|Mscr1000THD);
|
|
if(e != ~0)
|
|
mscr |= (Mscr1000TFD|Mscr1000THD) & e;
|
|
else if(mii->curphy->mscr != ~0)
|
|
mscr = mii->curphy->mscr;
|
|
else{
|
|
r = mii->mir(mii, phyno, Esr);
|
|
if(r == -1)
|
|
return -1;
|
|
if(r & Esr1000THD)
|
|
mscr |= Mscr1000THD;
|
|
if(r & Esr1000TFD)
|
|
mscr |= Mscr1000TFD;
|
|
}
|
|
mii->curphy->mscr = mscr;
|
|
mii->miw(mii, phyno, Mscr, mscr);
|
|
}
|
|
if(mii->miw(mii, phyno, Anar, anar) == -1)
|
|
return -1;
|
|
|
|
r = mii->mir(mii, phyno, Bmcr);
|
|
if(r == -1)
|
|
return -1;
|
|
if(!(r & BmcrR)){
|
|
r |= BmcrAne|BmcrRan;
|
|
mii->miw(mii, phyno, Bmcr, r);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
miistatus(Mii* mii)
|
|
{
|
|
MiiPhy *phy;
|
|
int anlpar, bmsr, p, r, phyno;
|
|
|
|
if(mii == nil || mii->ctlr == nil || mii->curphy == nil)
|
|
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->mir(mii, phyno, Bmsr);
|
|
if(bmsr == -1)
|
|
return -1;
|
|
if(!(bmsr & (BmsrAnc|BmsrAna))) {
|
|
// print("miistatus: auto-neg incomplete\n");
|
|
return -1;
|
|
}
|
|
|
|
bmsr = mii->mir(mii, phyno, Bmsr);
|
|
if(bmsr == -1)
|
|
return -1;
|
|
if(!(bmsr & BmsrLs)){
|
|
// print("miistatus: link down\n");
|
|
phy->link = 0;
|
|
return -1;
|
|
}
|
|
|
|
phy->speed = phy->fd = phy->rfc = phy->tfc = 0;
|
|
if(phy->mscr){
|
|
r = mii->mir(mii, phyno, Mssr);
|
|
if(r == -1)
|
|
return -1;
|
|
if((phy->mscr & Mscr1000TFD) && (r & Mssr1000TFD)){
|
|
phy->speed = 1000;
|
|
phy->fd = 1;
|
|
}
|
|
else if((phy->mscr & Mscr1000THD) && (r & Mssr1000THD))
|
|
phy->speed = 1000;
|
|
}
|
|
|
|
anlpar = mii->mir(mii, phyno, Anlpar);
|
|
if(anlpar == -1)
|
|
return -1;
|
|
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) {
|
|
// print("miistatus: phy speed 0\n");
|
|
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;
|
|
}
|
|
|
|
int
|
|
miimmdr(Mii* mii, int a, int r)
|
|
{
|
|
a &= 0x1F;
|
|
if(miimiw(mii, Mmdctrl, a) == -1)
|
|
return -1;
|
|
if(miimiw(mii, Mmddata, r) == -1)
|
|
return -1;
|
|
if(miimiw(mii, Mmdctrl, a | 0x4000) == -1)
|
|
return -1;
|
|
return miimir(mii, Mmddata);
|
|
}
|
|
|
|
int
|
|
miimmdw(Mii* mii, int a, int r, int data)
|
|
{
|
|
a &= 0x1F;
|
|
if(miimiw(mii, Mmdctrl, a) == -1)
|
|
return -1;
|
|
if(miimiw(mii, Mmddata, r) == -1)
|
|
return -1;
|
|
if(miimiw(mii, Mmdctrl, a | 0x4000) == -1)
|
|
return -1;
|
|
return miimiw(mii, Mmddata, data);
|
|
}
|