ref: db821f2de25161522bee09d69f9a9e0962f7ca06
parent: d1a416fc43cc0cbab3a881531d24f839ff722fee
author: cinap_lenrek <cinap_lenrek@felloff.net>
date: Sat Apr 19 13:32:27 EDT 2025
kernel: add experimental devmii phy debug driver This drivers allows poking at phy registers for the drivers that register their mii bus by calling addmiibus(). This requires some changes to port/ethermii.c: - Register access needs to be serialized (using a QLock) - Provide hooks for addmiibus()/delmiibus() - Added Mii.name field which needs to be populated by driver Devmii exports the following: + Qbus, /* #Φ/busN */ + Qphy, /* #Φ/busN/phyN */ + Qctl, /* #Φ/busN/phyN/ctl */ + Qmii, /* #Φ/busN/phyN/mii (clause22) */ + Qmmd, /* #Φ/busN/phyN/mmd (clause45) */ In its root, it lists all the mii buses (usually named by the ethernet interface name that registers it). In the root, it shows numbered directories for all the phy's on the bus. And each phy directory contains a textual ctl file, that when read returns status of the phy. The following commands can be written: - reset (reset the phy) - status (update phy status) - autoneg (restart auto-negotiation) The "mii" file allows raw clause22 register access (register number encoded in the offset 0-31). The "mmd" file allows raw clause45 register access (register number encoded in lower 16-bits, upper bits [16-20] encode the device address, so accessing ID1/2 registers 1.1/1.2 is offset 0x10001 and 0x10002 respectively.
--- a/sys/src/9/bcm64/ethergenet.c
+++ b/sys/src/9/bcm64/ethergenet.c
@@ -835,6 +835,7 @@
}
if(waserror()){
print("#l%d: %s\n", edev->ctlrno, up->errstr);
+ delmiibus(ctlr->mii);
shutdown(edev);
freebufs(ctlr);
qunlock(ctlr);
@@ -873,13 +874,17 @@
REG(ctlr->regs[SysPortCtrl]) = PortModeExtGphy;
REG(ctlr->regs[ExtRgmiiOobCtrl]) |= RgmiiModeEn | IdModeDis;
+ ctlr->mii->name = edev->name;
ctlr->mii->ctlr = ctlr;
ctlr->mii->mir = mdior;
ctlr->mii->miw = mdiow;
+ addmiibus(ctlr->mii);
+
mii(ctlr->mii, ~0);
phy = ctlr->mii->curphy;
if(phy == nil)
error("no phy");
+
print("#l%d: phy%d id %.8ux oui %x\n",
edev->ctlrno, phy->phyno, phy->id, phy->oui);
--- a/sys/src/9/imx8/etherimx.c
+++ b/sys/src/9/imx8/etherimx.c
@@ -237,7 +237,8 @@
struct {
Mii;
int done;
- Rendez;
+ Rendez io; /* for i/o done */
+ Rendez link; /* for link-change */
} mii[1];
int attached;
@@ -259,7 +260,7 @@
int i;
for(i = 0; i < 200; i++){
- tsleep(ctlr->mii, mdiodone, ctlr, 5);
+ tsleep(&ctlr->mii->io, mdiodone, ctlr, 5);
if(mdiodone(ctlr))
return 0;
}
@@ -306,7 +307,8 @@
if(e & INT_TXF) wakeup(ctlr->tx);
if(e & INT_MII) {
ctlr->mii->done = 1;
- wakeup(ctlr->mii);
+ wakeup(&ctlr->mii->io);
+ wakeup(&ctlr->mii->link);
}
wr(ctlr, ENET_EIR, e);
}
@@ -478,7 +480,7 @@
for(;;){
miistatus(phy);
if(phy->link == link){
- tsleep(ctlr->mii, return0, nil, 5000);
+ tsleep(&ctlr->mii->link, return0, nil, 5000);
continue;
}
link = phy->link;
@@ -552,10 +554,11 @@
ctlr->intmask |= INT_MII;
wr(ctlr, ENET_EIMR, ctlr->intmask);
- mii(ctlr->mii, ~0);
+ mii(ctlr->mii, ~0);
if(ctlr->mii->curphy == nil)
error("no phy");
+ addmiibus(ctlr->mii);
print("#l%d: phy%d id %.8ux oui %x\n",
edev->ctlrno, ctlr->mii->curphy->phyno,
@@ -704,6 +707,7 @@
ctlr->regs = (u32int*)(VIRTIO + 0xbe0000);
+ ctlr->mii->name = edev->name;
ctlr->mii->ctlr = ctlr;
ctlr->mii->mir = mdior;
ctlr->mii->miw = mdiow;
--- a/sys/src/9/mt7688/ether7688.c
+++ b/sys/src/9/mt7688/ether7688.c
@@ -547,6 +547,7 @@
if((ctlr->mii = malloc(sizeof(Mii))) == nil)
return -1;
+ ctlr->mii->name = edev->name;
ctlr->mii->ctlr = ctlr;
ctlr->mii->mir = miird;
ctlr->mii->miw = miiwr;
@@ -557,7 +558,8 @@
ctlr->mii = nil;
return -1;
}
-
+ addmiibus(ctlr->mii);
+
iprint("#l%d: phy%d id %.8ux oui %x\n",
edev->ctlrno, ctlr->mii->curphy->phyno,
ctlr->mii->curphy->id, ctlr->mii->curphy->oui);
--- a/sys/src/9/pc/ether8169.c
+++ b/sys/src/9/pc/ether8169.c
@@ -402,6 +402,8 @@
ctlr->mii->mir = rtl8169miimir;
ctlr->mii->miw = rtl8169miimiw;
ctlr->mii->ctlr = ctlr;
+ ctlr->mii->name = edev->name;
+ addmiibus(ctlr->mii);
/*
* PHY wakeup
--- a/sys/src/9/pc/etherdp83820.c
+++ b/sys/src/9/pc/etherdp83820.c
@@ -691,6 +691,7 @@
if(waserror()){
if(ctlr->mii != nil){
+ delmiibus(ctlr->mii);
free(ctlr->mii);
ctlr->mii = nil;
}
@@ -705,9 +706,11 @@
if(!(ctlr->cfg & Tbien)){
if((ctlr->mii = malloc(sizeof(Mii))) == nil)
error(Enomem);
+ ctlr->mii->name = edev->name;
ctlr->mii->ctlr = ctlr;
ctlr->mii->mir = dp83820miimir;
ctlr->mii->miw = dp83820miimiw;
+ addmiibus(ctlr->mii);
if(mii(ctlr->mii, ~0) == 0)
error("no PHY");
ctlr->cfg |= Dupstsien|Lnkstsien|Spdstsien;
--- a/sys/src/9/pc/etheri225.c
+++ b/sys/src/9/pc/etheri225.c
@@ -581,10 +581,12 @@
c->mii->ctlr = c;
c->mii->mir = i225miir;
c->mii->miw = i225miiw;
+ c->mii->name = c->edev->name;
if (mii(c->mii, ~0) == 0 || (phy = c->mii->curphy) == nil) {
free(c->mii); c->mii = nil;
error("phy");
}
+ addmiibus(c->mii);
/* configure for auto-negotiated link */
csr32w(c, Rdevctrl, csr32r(c, Rdevctrl) | DClink | DClinkauto);
--- a/sys/src/9/pc/etherigbe.c
+++ b/sys/src/9/pc/etherigbe.c
@@ -1475,7 +1475,6 @@
ctlr->mii = nil;
return -1;
}
-
if(mii(ctlr->mii, ~0) == 0 || (phy = ctlr->mii->curphy) == nil){
free(ctlr->mii);
ctlr->mii = nil;
--- /dev/null
+++ b/sys/src/9/port/devmii.c
@@ -1,0 +1,379 @@
+/* ethernet MII/SMI/MDIO phy bus debug driver */
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "../port/error.h"
+#include "../port/ethermii.h"
+
+enum {
+ Qdir = 0, /* #Φ */
+ Qbus, /* #Φ/busN */
+ Qphy, /* #Φ/busN/phyN */
+ Qctl, /* #Φ/busN/phyN/ctl */
+ Qmii, /* #Φ/busN/phyN/mii (clause22) */
+ Qmmd, /* #Φ/busN/phyN/mmd (clause45) */
+};
+
+#define TYPE(q) ((ulong)(q).path & 0xF)
+#define BUS(q) (((ulong)(q).path>>4) & 0xFF)
+#define PHY(q) (((ulong)(q).path>>12) & 0x1F)
+#define QID(b, p, t) (((p)<<12)|((b)<<4)|(t))
+
+static Lock buseslock;
+static Mii *buses[32];
+
+static void
+addbus(Mii *mii)
+{
+ int i;
+
+ if(mii == nil || mii->name == nil)
+ return;
+
+ lock(&buseslock);
+ for(i = 0; i < nelem(buses); i++){
+ if(buses[i] == nil)
+ continue;
+ if(buses[i] == mii || strcmp(buses[i]->name, mii->name) == 0){
+ buses[i] = mii;
+ goto out;
+ }
+ }
+ for(i = 0; i < nelem(buses); i++){
+ if(buses[i] == nil){
+ buses[i] = mii;
+ goto out;
+ }
+ }
+out:
+ unlock(&buseslock);
+ return;
+}
+
+static void
+delbus(Mii *mii)
+{
+ int i;
+
+ lock(&buseslock);
+ for(i = 0; i < nelem(buses); i++){
+ if(buses[i] == mii){
+ buses[i] = nil;
+ break;
+ }
+ }
+ unlock(&buseslock);
+}
+
+static Mii*
+getbus(int x)
+{
+ Mii *mii;
+
+ if((uint)x >= nelem(buses))
+ return nil;
+
+ lock(&buseslock);
+ mii = buses[x];
+ unlock(&buseslock);
+
+ if(mii != nil && mii->name == nil)
+ return nil;
+
+ return mii;
+}
+
+static MiiPhy*
+getphy(Chan *c)
+{
+ Mii *mii;
+ MiiPhy *phy;
+
+ if(TYPE(c->qid) < Qphy)
+ error(Egreg);
+ mii = getbus(BUS(c->qid));
+ if(mii == nil)
+ error(Egreg);
+ phy = mii->phy[PHY(c->qid)];
+ if(phy == nil)
+ error(Egreg);
+ return phy;
+}
+
+static void
+linkage(void)
+{
+ addmiibus = addbus;
+ delmiibus = delbus;
+}
+
+static int
+miigen(Chan *c, char *, Dirtab*, int, int s, Dir *dp)
+{
+ Mii *mii;
+ Qid q;
+
+ switch(TYPE(c->qid)){
+ case Qdir:
+ if(s == DEVDOTDOT){
+ Top:
+ mkqid(&q, QID(0, 0, Qdir), 0, QTDIR);
+ devdir(c, q, "#Φ", 0, eve, 0500, dp);
+ return 1;
+ }
+ if((uint)s >= nelem(buses))
+ return -1;
+ mii = getbus(s);
+ if(mii == nil)
+ return 0; /* continue search */
+ mkqid(&q, QID(s, 0, Qbus), 0, QTDIR);
+ devdir(c, q, mii->name, 0, eve, 0500, dp);
+ return 1;
+ case Qbus:
+ if(s == DEVDOTDOT)
+ goto Top;
+ if((uint)s >= nelem(mii->phy))
+ return -1;
+ mii = getbus(BUS(c->qid));
+ if(mii == nil)
+ return -1;
+ if(mii->phy[s] == nil)
+ return 0; /* continue search */
+ mkqid(&q, QID(BUS(c->qid), s, Qphy), 0, QTDIR);
+ snprint(up->genbuf, sizeof up->genbuf, "%d", s);
+ devdir(c, q, up->genbuf, 0, eve, 0500, dp);
+ return 1;
+ case Qphy:
+ if(s == DEVDOTDOT){
+ mkqid(&q, QID(BUS(c->qid), 0, Qbus), 0, QTDIR);
+ mii = getbus(BUS(c->qid));
+ if(mii == nil)
+ return -1;
+ devdir(c, q, mii->name, 0, eve, 0500, dp);
+ return 1;
+ }
+ if(s == 0) {
+ mkqid(&q, QID(BUS(c->qid), PHY(c->qid), Qctl), 0, 0);
+ devdir(c, q, "ctl", 0, eve, 0600, dp);
+ return 1;
+ }
+ if(s == 1) {
+ mkqid(&q, QID(BUS(c->qid), PHY(c->qid), Qmii), 0, 0);
+ devdir(c, q, "mii", 0x20, eve, 0600, dp);
+ return 1;
+ }
+ if(s == 2) {
+ mkqid(&q, QID(BUS(c->qid), PHY(c->qid), Qmmd), 0, 0);
+ devdir(c, q, "mmd", 0x200000, eve, 0600, dp);
+ return 1;
+ }
+ break;
+ }
+ return -1;
+}
+
+static Chan*
+miiattach(char *spec)
+{
+ return devattach(L'Φ', spec);
+}
+
+static Chan*
+miiopen(Chan *c, int mode)
+{
+ return devopen(c, mode, nil, 0, miigen);
+}
+
+static int
+getword(uchar *data)
+{
+ return data[0] | (int)data[1] << 8;
+}
+
+static void
+putword(uchar *data, int w)
+{
+ data[0] = w & 0xFF;
+ data[1] = w >> 8;
+}
+
+static char*
+phystatus(MiiPhy *phy, char *s, char *e)
+{
+ int i;
+
+ s = seprint(s, e, "id %.8uX\n", phy->id);
+ s = seprint(s, e, "oui %.5X\n", phy->oui);
+ s = seprint(s, e, "link %d\n", phy->link);
+ s = seprint(s, e, "speed %d\n", phy->speed);
+ s = seprint(s, e, "fd %d\n", phy->fd);
+
+ s = seprint(s, e, "dump");
+ for(i = 0; i < NMiiPhyr; i++){
+ if((i & 0x07) == 0)
+ s = seprint(s, e, "\n\t");
+ s = seprint(s, e, " %4.4uX", miimir(phy, i) & 0xFFFF);
+ }
+ s = seprint(s, e, "\n");
+
+ return s;
+}
+
+enum {
+ CMreset,
+ CMstatus,
+ CMautoneg,
+};
+
+static Cmdtab phyctlmsg[] =
+{
+ CMreset, "reset", 1,
+ CMstatus, "status", 1,
+ CMautoneg, "autoneg", 0,
+};
+
+static long
+phyctl(MiiPhy *phy, void *data, long len)
+{
+ Cmdbuf *cb;
+ Cmdtab *ct;
+ int a[3];
+
+ cb = parsecmd(data, len);
+ if(waserror()){
+ free(cb);
+ nexterror();
+ }
+ ct = lookupcmd(cb, phyctlmsg, nelem(phyctlmsg));
+ switch(ct->index){
+ case CMreset:
+ miireset(phy);
+ break;
+ case CMstatus:
+ miistatus(phy);
+ break;
+ case CMautoneg:
+ a[0] = a[1] = a[2] = ~0;
+ switch(cb->nf){
+ case 4: a[2] = strtoul(cb->f[3], nil, 0);
+ case 3: a[1] = strtoul(cb->f[2], nil, 0);
+ case 2: a[0] = strtoul(cb->f[1], nil, 0);
+ }
+ miiane(phy, a[0], a[1], a[2]);
+ break;
+ default:
+ cmderror(cb, Ebadarg);
+ }
+ free(cb);
+ poperror();
+ return len;
+}
+
+static long
+miiwrite(Chan *c, void *data, long len, vlong offset)
+{
+ switch(TYPE(c->qid)){
+ case Qctl:
+ return phyctl(getphy(c), data, len);
+ case Qmii:
+ if(len != 2)
+ error(Eshort);
+ if(offset >= 0x20)
+ return 0;
+ miimiw(getphy(c), (int)offset, getword(data));
+ return len;
+ case Qmmd:
+ if(len != 2)
+ error(Eshort);
+ if(offset >= 0x200000)
+ return 0;
+ miimmdw(getphy(c), (offset >> 16) & 0x1F, offset & 0xFFFF, getword(data));
+ return len;
+ }
+ error(Egreg);
+}
+
+static long
+miiread(Chan *c, void *data, long len, vlong offset)
+{
+ char *buf;
+ int w;
+
+ if(c->qid.type == QTDIR)
+ return devdirread(c, data, len, nil, 0, miigen);
+
+ switch(TYPE(c->qid)){
+ case Qctl:
+ buf = smalloc(READSTR);
+ if(waserror()){
+ free(buf);
+ nexterror();
+ }
+ phystatus(getphy(c), buf, buf+READSTR);
+ len = readstr((ulong)offset, data, len, buf);
+ poperror();
+ return len;
+ case Qmii:
+ if(len != 2)
+ error(Eshort);
+ if(offset >= 0x20)
+ return 0;
+ w = miimir(getphy(c), (int)offset);
+ if(w == -1)
+ error(Eio);
+ putword(data, w);
+ return len;
+ case Qmmd:
+ if(len != 2)
+ error(Eshort);
+ if(offset >= 0x200000)
+ return 0;
+ w = miimmdr(getphy(c), (offset >> 16) & 0x1F, offset & 0xFFFF);
+ if(w == -1)
+ error(Eio);
+ putword(data, w);
+ return len;
+ }
+ error(Egreg);
+}
+
+static void
+miiclose(Chan*)
+{
+}
+
+static Walkqid*
+miiwalk(Chan *c, Chan *nc, char **name, int nname)
+{
+ return devwalk(c, nc, name, nname, nil, 0, miigen);
+}
+
+static int
+miistat(Chan *c, uchar *dp, int n)
+{
+ return devstat(c, dp, n, nil, 0, miigen);
+}
+
+Dev miidevtab = {
+ L'Φ',
+ "mii",
+
+ linkage,
+ devinit,
+ devshutdown,
+ miiattach,
+ miiwalk,
+ miistat,
+ miiopen,
+ devcreate,
+ miiclose,
+ miiread,
+ devbread,
+ miiwrite,
+ devbwrite,
+ devremove,
+ devwstat,
+ devpower,
+};
--- a/sys/src/9/port/ethermii.c
+++ b/sys/src/9/port/ethermii.c
@@ -10,6 +10,11 @@
#include "ethermii.h"
+/* hook for devmii */
+static void dummy(Mii*){}
+void (*addmiibus)(Mii*) = dummy;
+void (*delmiibus)(Mii*) = dummy;
+
uint
mii(Mii* mii, uint mask)
{
@@ -17,6 +22,12 @@
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.
@@ -32,10 +43,10 @@
rmask |= bit;
continue;
}
- if(mii->mir(mii, phyno, Bmsr) == -1)
+ if((*mii->mir)(mii, phyno, Bmsr) == -1)
continue;
- id = mii->mir(mii, phyno, Phyidr1) << 16;
- id |= mii->mir(mii, phyno, Phyidr2);
+ id = (*mii->mir)(mii, phyno, Phyidr1) << 16;
+ id |= (*mii->mir)(mii, phyno, Phyidr2);
oui = (id & 0x3FFFFC00)>>10;
if(oui == 0xFFFFF || oui == 0)
continue;
@@ -60,6 +71,10 @@
rmask |= bit;
}
+
+ qunlock(mii);
+ if(up != nil) poperror();
+
return rmask;
}
@@ -67,10 +82,19 @@
miimir(MiiPhy *phy, int r)
{
Mii *mii;
+ int ret;
- if(phy == nil || (mii = phy->mii) == nil || mii->ctlr == nil)
+ if(phy == nil || (mii = phy->mii) == nil)
return -1;
- return mii->mir(mii, phy->phyno, r & 0x1F);
+ 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
@@ -77,10 +101,19 @@
miimiw(MiiPhy *phy, int r, int data)
{
Mii *mii;
+ int ret;
- if(phy == nil || (mii = phy->mii) == nil || mii->ctlr == nil)
+ if(phy == nil || (mii = phy->mii) == nil)
return -1;
- return mii->miw(mii, phy->phyno, r & 0x1F, data & 0xFFFF);
+ 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
@@ -251,25 +284,53 @@
int
miimmdr(MiiPhy *phy, int a, int r)
{
- a &= 0x1F;
- if(miimiw(phy, Mmdctrl, a) == -1)
+ Mii *mii;
+ int ret;
+
+ if(phy == nil || (mii = phy->mii) == nil)
return -1;
- if(miimiw(phy, Mmddata, r) == -1)
- return -1;
- if(miimiw(phy, Mmdctrl, a | 0x4000) == -1)
- return -1;
- return miimir(phy, Mmddata);
+ 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)
{
- a &= 0x1F;
- if(miimiw(phy, Mmdctrl, a) == -1)
+ Mii *mii;
+ int ret;
+
+ if(phy == nil || (mii = phy->mii) == nil)
return -1;
- if(miimiw(phy, Mmddata, r) == -1)
- return -1;
- if(miimiw(phy, Mmdctrl, a | 0x4000) == -1)
- return -1;
- return miimiw(phy, Mmddata, data);
+ 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;
}
--- a/sys/src/9/port/ethermii.h
+++ b/sys/src/9/port/ethermii.h
@@ -83,18 +83,21 @@
};
typedef struct Mii {
+ QLock;
+
int nphy;
uint mask;
- MiiPhy* phy[NMiiPhy];
- MiiPhy* curphy;
+ MiiPhy *phy[NMiiPhy];
+ MiiPhy *curphy;
- void* ctlr;
+ char *name;
+ void *ctlr;
int (*mir)(Mii*, int, int);
int (*miw)(Mii*, int, int, int);
} Mii;
typedef struct MiiPhy {
- Mii* mii;
+ Mii *mii;
uint id;
int oui;
int phyno;
@@ -118,3 +121,7 @@
extern int miistatus(MiiPhy*);
extern int miimmdr(MiiPhy*, int, int);
extern int miimmdw(MiiPhy*, int, int, int);
+
+extern void (*addmiibus)(Mii*);
+extern void (*delmiibus)(Mii*);
+
--- a/sys/src/9/port/portmkfile
+++ b/sys/src/9/port/portmkfile
@@ -119,4 +119,4 @@
devether.$O: ../ip/ip.h ../ip/ipv6.h
devether.$O ethersink.$O etheriwl.$O ethervirtio10.$O wifi.$O: ../port/netif.h ../port/etherif.h
etheriwl.$O wifi.$O: ../port/wifi.h
-ethermii.$O: ../port/ethermii.h
+ethermii.$O devmii.$O: ../port/ethermii.h
--
⑨