ref: 9b69f546334e94ec191d35b15dcaace98fbdcb1b
dir: /sys/src/9/port/ethermii.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"
/* hook for devmii */
static void dummy(Mii*){}
void (*addmiibus)(Mii*) = dummy;
void (*delmiibus)(Mii*) = dummy;
uint
mii(Mii* mii, uint mask)
{
MiiPhy *phy;
int oui, phyno;
uint bit, rmask, id;
qlock(mii);
if(up != nil && waserror()){
qunlock(mii);
nexterror();
}
/*
* 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 = 1U<<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((phy = malloc(sizeof(MiiPhy))) == nil)
continue;
phy->mii = mii;
phy->id = id;
phy->oui = oui;
phy->phyno = phyno;
phy->anar = ~0;
phy->fc = ~0;
phy->mscr = ~0;
mii->phy[phyno] = phy;
if(mii->curphy == nil)
mii->curphy = phy;
mii->mask |= bit;
mii->nphy++;
rmask |= bit;
}
qunlock(mii);
if(up != nil) poperror();
return rmask;
}
int
miimir(MiiPhy *phy, int r)
{
Mii *mii;
int ret;
if(phy == nil || (mii = phy->mii) == nil)
return -1;
qlock(mii);
if(up != nil && waserror()){
qunlock(mii);
nexterror();
}
ret = (*mii->mir)(mii, phy->phyno, r & 0x1F);
qunlock(mii);
if(up != nil) poperror();
return ret;
}
int
miimiw(MiiPhy *phy, int r, int data)
{
Mii *mii;
int ret;
if(phy == nil || (mii = phy->mii) == nil)
return -1;
qlock(mii);
if(up != nil && waserror()){
qunlock(mii);
nexterror();
}
ret = (*mii->miw)(mii, phy->phyno, r & 0x1F, data & 0xFFFF);
qunlock(mii);
if(up != nil) poperror();
return ret;
}
int
miireset(MiiPhy *phy)
{
int bmcr;
bmcr = miimir(phy, Bmcr);
if(bmcr == -1)
return -1;
bmcr |= BmcrR;
miimiw(phy, Bmcr, bmcr);
microdelay(1);
return 0;
}
int
miiane(MiiPhy *phy, int a, int p, int e)
{
int anar, bmsr, mscr, r;
bmsr = miimir(phy, Bmsr);
if(bmsr == -1)
return -1;
if(!(bmsr & BmsrAna))
return -1;
if(a != ~0)
anar = (AnaTXFD|AnaTXHD|Ana10FD|Ana10HD) & a;
else if(phy->anar != ~0)
anar = phy->anar;
else{
anar = miimir(phy, 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;
}
phy->anar = anar;
if(p != ~0)
anar |= (AnaAP|AnaP) & p;
else if(phy->fc != ~0)
anar |= phy->fc;
phy->fc = (AnaAP|AnaP) & anar;
if(bmsr & BmsrEs){
mscr = miimir(phy, Mscr);
if(mscr == -1)
return -1;
mscr &= ~(Mscr1000TFD|Mscr1000THD);
if(e != ~0)
mscr |= (Mscr1000TFD|Mscr1000THD) & e;
else if(phy->mscr != ~0)
mscr = phy->mscr;
else{
r = miimir(phy, Esr);
if(r == -1)
return -1;
if(r & Esr1000THD)
mscr |= Mscr1000THD;
if(r & Esr1000TFD)
mscr |= Mscr1000TFD;
}
phy->mscr = mscr;
miimiw(phy, Mscr, mscr);
}
if(miimiw(phy, Anar, anar) == -1)
return -1;
r = miimir(phy, Bmcr);
if(r == -1)
return -1;
if(!(r & BmcrR)){
r |= BmcrAne|BmcrRan;
miimiw(phy, Bmcr, r);
}
return 0;
}
int
miianec45(MiiPhy* phy, int m)
{
int r;
phy->mgcr = m;
phy->mgcr &= (MMDanmgcr2500T|MMDanmgcr5000T|MMDanmgcr10000T);
r = miimmdr(phy, MMDan, MMDanmgcr);
if (r == -1)
return -1;
r &= ~phy->mgcr;
r |= phy->mgcr;
if (miimmdw(phy, MMDan, MMDanmgcr, r) == -1)
return -1;
return 0;
}
int
miistatus(MiiPhy* phy)
{
int anlpar, bmsr, p, r;
/*
* Check Auto-Negotiation is complete and link is up.
* (Read status twice as the Ls bit is sticky).
*/
bmsr = miimir(phy, Bmsr);
if(bmsr == -1)
return -1;
if(!(bmsr & (BmsrAnc|BmsrAna))) {
// print("miistatus: auto-neg incomplete\n");
return -1;
}
bmsr = miimir(phy, 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 = miimir(phy, 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 = miimir(phy, 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
miistatusc45(MiiPhy *phy)
{
int r;
r = miimmdr(phy, MMDan, MMDanmgsr);
if (r == -1)
return -1;
if (r & MMDanmgsr2500T) {
phy->speed = 2500;
phy->fd = 1;
}
if (r & MMDanmgsr5000T) {
phy->speed = 5000;
phy->fd = 1;
}
if (r & MMDanmgsr10000T) {
phy->speed = 10000;
phy->fd = 1;
}
return 0;
}
int
miimmdr(MiiPhy *phy, int a, int r)
{
Mii *mii;
int ret;
if(phy == nil || (mii = phy->mii) == nil)
return -1;
qlock(mii);
if(up != nil && waserror()){
qunlock(mii);
nexterror();
}
a &= 0x1F;
if((ret = (*mii->miw)(mii, phy->phyno, Mmdctrl, a)) == -1)
goto out;
if((ret = (*mii->miw)(mii, phy->phyno, Mmddata, r & 0xFFFF)) == -1)
goto out;
if((ret = (*mii->miw)(mii, phy->phyno, Mmdctrl, a | 0x4000)) == -1)
goto out;
ret = (*mii->mir)(mii, phy->phyno, Mmddata);
out:
qunlock(mii);
if(up != nil) poperror();
return ret;
}
int
miimmdw(MiiPhy *phy, int a, int r, int data)
{
Mii *mii;
int ret;
if(phy == nil || (mii = phy->mii) == nil)
return -1;
qlock(mii);
if(up != nil && waserror()){
qunlock(mii);
nexterror();
}
a &= 0x1F;
if((ret = (*mii->miw)(mii, phy->phyno, Mmdctrl, a)) == -1)
goto out;
if((ret = (*mii->miw)(mii, phy->phyno, Mmddata, r & 0xFFFF)) == -1)
goto out;
if((ret = (*mii->miw)(mii, phy->phyno, Mmdctrl, a | 0x4000)) == -1)
goto out;
ret = (*mii->miw)(mii, phy->phyno, Mmddata, data & 0xFFFF);
out:
qunlock(mii);
if(up != nil) poperror();
return ret;
}