diff --git a/sys/dev/fxp/if_fxp.c b/sys/dev/fxp/if_fxp.c index d98a0f210b8b..94ae0f139121 100644 --- a/sys/dev/fxp/if_fxp.c +++ b/sys/dev/fxp/if_fxp.c @@ -232,6 +232,7 @@ static void fxp_watchdog __P((struct ifnet *)); static int fxp_add_rfabuf __P((struct fxp_softc *, struct mbuf *)); static int fxp_mdi_read __P((struct fxp_softc *, int, int)); static void fxp_mdi_write __P((struct fxp_softc *, int, int, int)); +static void fxp_autosize_eeprom __P((struct fxp_softc *)); static void fxp_read_eeprom __P((struct fxp_softc *, u_int16_t *, int, int)); static int fxp_attach_common __P((struct fxp_softc *, u_int8_t *)); @@ -747,6 +748,11 @@ fxp_attach_common(sc, enaddr) } } + /* + * Find out how large of an SEEPROM we have. + */ + fxp_autosize_eeprom(sc); + /* * Get info about the primary PHY */ @@ -801,6 +807,76 @@ fxp_attach_common(sc, enaddr) return (ENOMEM); } +/* + * From NetBSD: + * + * Figure out EEPROM size. + * + * 559's can have either 64-word or 256-word EEPROMs, the 558 + * datasheet only talks about 64-word EEPROMs, and the 557 datasheet + * talks about the existance of 16 to 256 word EEPROMs. + * + * The only known sizes are 64 and 256, where the 256 version is used + * by CardBus cards to store CIS information. + * + * The address is shifted in msb-to-lsb, and after the last + * address-bit the EEPROM is supposed to output a `dummy zero' bit, + * after which follows the actual data. We try to detect this zero, by + * probing the data-out bit in the EEPROM control register just after + * having shifted in a bit. If the bit is zero, we assume we've + * shifted enough address bits. The data-out should be tri-state, + * before this, which should translate to a logical one. + * + * Other ways to do this would be to try to read a register with known + * contents with a varying number of address bits, but no such + * register seem to be available. The high bits of register 10 are 01 + * on the 558 and 559, but apparently not on the 557. + * + * The Linux driver computes a checksum on the EEPROM data, but the + * value of this checksum is not very well documented. + */ +static void +fxp_autosize_eeprom(sc) + struct fxp_softc *sc; +{ + u_int16_t reg; + int x; + + CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, FXP_EEPROM_EECS); + /* + * Shift in read opcode. + */ + for (x = 3; x > 0; x--) { + if (FXP_EEPROM_OPC_READ & (1 << (x - 1))) { + reg = FXP_EEPROM_EECS | FXP_EEPROM_EEDI; + } else { + reg = FXP_EEPROM_EECS; + } + CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, reg); + CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, + reg | FXP_EEPROM_EESK); + DELAY(1); + CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, reg); + DELAY(1); + } + /* + * Shift in address. + * Wait for the dummy zero following a correct address shift. + */ + for (x = 1; x <= 8; x++) { + CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, FXP_EEPROM_EECS); + CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, + FXP_EEPROM_EECS | FXP_EEPROM_EESK); + DELAY(1); + if ((CSR_READ_2(sc, FXP_CSR_EEPROMCONTROL) & FXP_EEPROM_EEDO) == 0) + break; + CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, FXP_EEPROM_EECS); + DELAY(1); + } + CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, 0); + DELAY(1); + sc->eeprom_size = x; +} /* * Read from the serial EEPROM. Basically, you manually shift in * the read opcode (one bit at a time) and then shift in the address, @@ -839,7 +915,7 @@ fxp_read_eeprom(sc, data, offset, words) /* * Shift in address. */ - for (x = 6; x > 0; x--) { + for (x = sc->eeprom_size; x > 0; x--) { if ((i + offset) & (1 << (x - 1))) { reg = FXP_EEPROM_EECS | FXP_EEPROM_EEDI; } else { diff --git a/sys/dev/fxp/if_fxpvar.h b/sys/dev/fxp/if_fxpvar.h index 97c2b4019ff0..7ef7149305f4 100644 --- a/sys/dev/fxp/if_fxpvar.h +++ b/sys/dev/fxp/if_fxpvar.h @@ -67,6 +67,7 @@ struct fxp_softc { int phy_primary_addr; /* address of primary PHY */ int phy_primary_device; /* device type of primary PHY */ int phy_10Mbps_only; /* PHY is 10Mbps-only device */ + int eeprom_size; /* size of serial EEPROM */ }; /* Macros to ease CSR access. */ diff --git a/sys/pci/if_fxp.c b/sys/pci/if_fxp.c index d98a0f210b8b..94ae0f139121 100644 --- a/sys/pci/if_fxp.c +++ b/sys/pci/if_fxp.c @@ -232,6 +232,7 @@ static void fxp_watchdog __P((struct ifnet *)); static int fxp_add_rfabuf __P((struct fxp_softc *, struct mbuf *)); static int fxp_mdi_read __P((struct fxp_softc *, int, int)); static void fxp_mdi_write __P((struct fxp_softc *, int, int, int)); +static void fxp_autosize_eeprom __P((struct fxp_softc *)); static void fxp_read_eeprom __P((struct fxp_softc *, u_int16_t *, int, int)); static int fxp_attach_common __P((struct fxp_softc *, u_int8_t *)); @@ -747,6 +748,11 @@ fxp_attach_common(sc, enaddr) } } + /* + * Find out how large of an SEEPROM we have. + */ + fxp_autosize_eeprom(sc); + /* * Get info about the primary PHY */ @@ -801,6 +807,76 @@ fxp_attach_common(sc, enaddr) return (ENOMEM); } +/* + * From NetBSD: + * + * Figure out EEPROM size. + * + * 559's can have either 64-word or 256-word EEPROMs, the 558 + * datasheet only talks about 64-word EEPROMs, and the 557 datasheet + * talks about the existance of 16 to 256 word EEPROMs. + * + * The only known sizes are 64 and 256, where the 256 version is used + * by CardBus cards to store CIS information. + * + * The address is shifted in msb-to-lsb, and after the last + * address-bit the EEPROM is supposed to output a `dummy zero' bit, + * after which follows the actual data. We try to detect this zero, by + * probing the data-out bit in the EEPROM control register just after + * having shifted in a bit. If the bit is zero, we assume we've + * shifted enough address bits. The data-out should be tri-state, + * before this, which should translate to a logical one. + * + * Other ways to do this would be to try to read a register with known + * contents with a varying number of address bits, but no such + * register seem to be available. The high bits of register 10 are 01 + * on the 558 and 559, but apparently not on the 557. + * + * The Linux driver computes a checksum on the EEPROM data, but the + * value of this checksum is not very well documented. + */ +static void +fxp_autosize_eeprom(sc) + struct fxp_softc *sc; +{ + u_int16_t reg; + int x; + + CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, FXP_EEPROM_EECS); + /* + * Shift in read opcode. + */ + for (x = 3; x > 0; x--) { + if (FXP_EEPROM_OPC_READ & (1 << (x - 1))) { + reg = FXP_EEPROM_EECS | FXP_EEPROM_EEDI; + } else { + reg = FXP_EEPROM_EECS; + } + CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, reg); + CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, + reg | FXP_EEPROM_EESK); + DELAY(1); + CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, reg); + DELAY(1); + } + /* + * Shift in address. + * Wait for the dummy zero following a correct address shift. + */ + for (x = 1; x <= 8; x++) { + CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, FXP_EEPROM_EECS); + CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, + FXP_EEPROM_EECS | FXP_EEPROM_EESK); + DELAY(1); + if ((CSR_READ_2(sc, FXP_CSR_EEPROMCONTROL) & FXP_EEPROM_EEDO) == 0) + break; + CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, FXP_EEPROM_EECS); + DELAY(1); + } + CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, 0); + DELAY(1); + sc->eeprom_size = x; +} /* * Read from the serial EEPROM. Basically, you manually shift in * the read opcode (one bit at a time) and then shift in the address, @@ -839,7 +915,7 @@ fxp_read_eeprom(sc, data, offset, words) /* * Shift in address. */ - for (x = 6; x > 0; x--) { + for (x = sc->eeprom_size; x > 0; x--) { if ((i + offset) & (1 << (x - 1))) { reg = FXP_EEPROM_EECS | FXP_EEPROM_EEDI; } else { diff --git a/sys/pci/if_fxpvar.h b/sys/pci/if_fxpvar.h index 97c2b4019ff0..7ef7149305f4 100644 --- a/sys/pci/if_fxpvar.h +++ b/sys/pci/if_fxpvar.h @@ -67,6 +67,7 @@ struct fxp_softc { int phy_primary_addr; /* address of primary PHY */ int phy_primary_device; /* device type of primary PHY */ int phy_10Mbps_only; /* PHY is 10Mbps-only device */ + int eeprom_size; /* size of serial EEPROM */ }; /* Macros to ease CSR access. */