Bridged MDIO reads verify freshness: a late async fetch serves the previous response as the wrong register (observed STATUS 0x0004 as 0.4dB SNR); unchanged-or-zero results re-read bare without re-arming until stable, first read always verified, identify retries against the constant PHY ID
This commit is contained in:
@@ -40,12 +40,14 @@ const (
|
||||
bcmPHYIDHi = 0x3590
|
||||
bcmPHYIDLo = 0x5081
|
||||
|
||||
bcmReadDelayUs = 3000
|
||||
bcmRetryDelayUs = 10000
|
||||
bcmReadDelayUs = 3000
|
||||
|
||||
bcmStatusPoll = 100 * time.Millisecond
|
||||
bcmStatusTries = 30
|
||||
|
||||
bcmVerifyGap = 2 * time.Millisecond
|
||||
bcmVerifyTries = 8
|
||||
|
||||
ecdPoll = 200 * time.Millisecond
|
||||
ecdDeadline = 50 * time.Second
|
||||
|
||||
@@ -70,6 +72,10 @@ type bcm struct {
|
||||
// Guards multi-op sequences only; single reads are already atomic on the
|
||||
// wire through the compound op.
|
||||
mu sync.Mutex
|
||||
|
||||
rmu sync.Mutex
|
||||
lastBuf uint16
|
||||
haveBuf bool
|
||||
}
|
||||
|
||||
func openBCM(ifname string) (*bcm, error) {
|
||||
@@ -155,14 +161,50 @@ func (b *bcm) mdioReadDelay(devad, reg uint16, delayUs int) (uint16, error) {
|
||||
return uint16(d[0])<<8 | uint16(d[1]), nil
|
||||
}
|
||||
|
||||
// 0x0000 is also the bridge's not-ready signature, so a zero is read again at
|
||||
// a longer delay before being believed.
|
||||
func (b *bcm) mdioRead(devad, reg uint16) (uint16, error) {
|
||||
v, err := b.mdioReadDelay(devad, reg, bcmReadDelayUs)
|
||||
if err != nil || v != 0 {
|
||||
return v, err
|
||||
func (b *bcm) bufRead() (uint16, error) {
|
||||
resp, err := b.op(fmt.Sprintf("r %02x 2", bcmI2CRead))
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return b.mdioReadDelay(devad, reg, bcmRetryDelayUs)
|
||||
d, err := parseHexBytes(resp, 2)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return uint16(d[0])<<8 | uint16(d[1]), nil
|
||||
}
|
||||
|
||||
// The bridge fetch is asynchronous: one that outruns the delay leaves the
|
||||
// previous response in the buffer, served silently as the wrong register's
|
||||
// data. A changed buffer value proves a fresh fetch; an unchanged or zero one
|
||||
// is re-read bare — never re-armed, so clear-on-read registers keep their
|
||||
// data — until it stabilizes.
|
||||
func (b *bcm) mdioRead(devad, reg uint16) (uint16, error) {
|
||||
b.rmu.Lock()
|
||||
defer b.rmu.Unlock()
|
||||
|
||||
v, err := b.mdioReadDelay(devad, reg, bcmReadDelayUs)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if b.haveBuf && v != 0 && v != b.lastBuf {
|
||||
b.lastBuf = v
|
||||
return v, nil
|
||||
}
|
||||
stable := 0
|
||||
for i := 0; i < bcmVerifyTries && stable < 2; i++ {
|
||||
time.Sleep(bcmVerifyGap)
|
||||
r, err := b.bufRead()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if r == v {
|
||||
stable++
|
||||
} else {
|
||||
v, stable = r, 0
|
||||
}
|
||||
}
|
||||
b.haveBuf, b.lastBuf = true, v
|
||||
return v, nil
|
||||
}
|
||||
|
||||
func (b *bcm) mdioWrite(devad, reg, val uint16) error {
|
||||
@@ -229,24 +271,29 @@ func (b *bcm) command(code uint16, params ...uint16) ([5]uint16, error) {
|
||||
return data, nil
|
||||
}
|
||||
|
||||
// Retried against the known constant: the first reads after a process start
|
||||
// can land while the bridge still holds a dead process's pending fetch.
|
||||
func (b *bcm) identify() (string, error) {
|
||||
hi, err := b.mdioRead(1, 2)
|
||||
if err != nil {
|
||||
return "", err
|
||||
var hi, lo uint16
|
||||
var err error
|
||||
for i := 0; i < 5; i++ {
|
||||
if hi, err = b.mdioRead(1, 2); err != nil {
|
||||
return "", err
|
||||
}
|
||||
if lo, err = b.mdioRead(1, 3); err != nil {
|
||||
return "", err
|
||||
}
|
||||
if hi == bcmPHYIDHi && lo == bcmPHYIDLo {
|
||||
sn, err := b.eeprom(68, 16)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return "BCM84891L sn " + strings.TrimSpace(string(sn)), nil
|
||||
}
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
}
|
||||
lo, err := b.mdioRead(1, 3)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if hi != bcmPHYIDHi || lo != bcmPHYIDLo {
|
||||
return "", fmt.Errorf("%s: PHY ID %#04x:%#04x, want %#04x:%#04x",
|
||||
b.ifname, hi, lo, bcmPHYIDHi, bcmPHYIDLo)
|
||||
}
|
||||
sn, err := b.eeprom(68, 16)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return "BCM84891L sn " + strings.TrimSpace(string(sn)), nil
|
||||
return "", fmt.Errorf("%s: PHY ID %#04x:%#04x, want %#04x:%#04x",
|
||||
b.ifname, hi, lo, bcmPHYIDHi, bcmPHYIDLo)
|
||||
}
|
||||
|
||||
// PMA 1.1 latches low, so the first read reports any drop since it was last
|
||||
|
||||
Reference in New Issue
Block a user