Source-Changes-HG archive
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]
[src/trunk]: src/sys/dev/pci o Add support for accessing the PHY through MDIO...
details: https://anonhg.NetBSD.org/src/rev/a2ba116797fe
branches: trunk
changeset: 555891:a2ba116797fe
user: cube <cube%NetBSD.org@localhost>
date: Wed Dec 03 21:58:49 2003 +0000
description:
o Add support for accessing the PHY through MDIO for recent SiS chips
o Add support for the recent SiS96x chipsets that have a new revision.
That includes a new bit of code to access the EEPROM, since it is
shared with the ieee1394 controller on those chipsets.
Mostly taken from FreeBSD (rev. 1.62 and 1.64 of sys/pci/if_sip.c). I
tried to make the code look less ugly, but couldn't invent documentation.
Fix PR #23481. Thanks to Stephane ENGEL <sengel AT melshake DOT com> for
the report and the cheerful testing.
diffstat:
sys/dev/pci/if_sip.c | 260 +++++++++++++++++++++++++++++++++++++++++++----
sys/dev/pci/if_sipreg.h | 18 ++-
2 files changed, 252 insertions(+), 26 deletions(-)
diffs (truncated from 390 to 300 lines):
diff -r 30016a9857af -r a2ba116797fe sys/dev/pci/if_sip.c
--- a/sys/dev/pci/if_sip.c Wed Dec 03 20:24:51 2003 +0000
+++ b/sys/dev/pci/if_sip.c Wed Dec 03 21:58:49 2003 +0000
@@ -1,4 +1,4 @@
-/* $NetBSD: if_sip.c,v 1.83 2003/10/29 03:31:22 mycroft Exp $ */
+/* $NetBSD: if_sip.c,v 1.84 2003/12/03 21:58:49 cube Exp $ */
/*-
* Copyright (c) 2001, 2002 The NetBSD Foundation, Inc.
@@ -80,7 +80,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: if_sip.c,v 1.83 2003/10/29 03:31:22 mycroft Exp $");
+__KERNEL_RCSID(0, "$NetBSD: if_sip.c,v 1.84 2003/12/03 21:58:49 cube Exp $");
#include "bpfilter.h"
#include "rnd.h"
@@ -424,6 +424,7 @@
void SIP_DECL(dp83820_read_macaddr)(struct sip_softc *,
const struct pci_attach_args *, u_int8_t *);
#else
+static void SIP_DECL(sis900_eeprom_delay)(struct sip_softc *sc);
void SIP_DECL(sis900_read_macaddr)(struct sip_softc *,
const struct pci_attach_args *, u_int8_t *);
void SIP_DECL(dp83815_read_macaddr)(struct sip_softc *,
@@ -439,6 +440,8 @@
void SIP_DECL(dp83820_mii_writereg)(struct device *, int, int, int);
void SIP_DECL(dp83820_mii_statchg)(struct device *);
#else
+static void SIP_DECL(sis900_mii_sync)(struct sip_softc *);
+static void SIP_DECL(sis900_mii_send)(struct sip_softc *, u_int32_t, int);
int SIP_DECL(sis900_mii_readreg)(struct device *, int, int);
void SIP_DECL(sis900_mii_writereg)(struct device *, int, int, int);
void SIP_DECL(sis900_mii_statchg)(struct device *);
@@ -2678,6 +2681,7 @@
crc = ether_crc32_be(enm->enm_addrlo, ETHER_ADDR_LEN);
if (SIP_SIS900_REV(sc, SIS_REV_635) ||
+ SIP_SIS900_REV(sc, SIS_REV_960) ||
SIP_SIS900_REV(sc, SIS_REV_900B)) {
/* Just want the 8 most significant bits. */
crc >>= 24;
@@ -2727,6 +2731,7 @@
FILTER_EMIT(RFCR_RFADDR_MC6, mchash[6]);
FILTER_EMIT(RFCR_RFADDR_MC7, mchash[7]);
if (SIP_SIS900_REV(sc, SIS_REV_635) ||
+ SIP_SIS900_REV(sc, SIS_REV_960) ||
SIP_SIS900_REV(sc, SIS_REV_900B)) {
FILTER_EMIT(RFCR_RFADDR_MC8, mchash[8]);
FILTER_EMIT(RFCR_RFADDR_MC9, mchash[9]);
@@ -3053,6 +3058,56 @@
bus_space_write_4(sc->sc_st, sc->sc_sh, SIP_EROMAR, val);
}
#else /* ! DP83820 */
+
+/* SiS MII functions */
+
+#define SIS_SET_EROMAR(x,y) bus_space_write_4(x->sc_st, x->sc_sh, SIP_EROMAR, \
+ bus_space_read_4(x->sc_st, x->sc_sh, SIP_EROMAR) | (y))
+
+#define SIS_CLR_EROMAR(x,y) bus_space_write_4(x->sc_st, x->sc_sh, SIP_EROMAR, \
+ bus_space_read_4(x->sc_st, x->sc_sh, SIP_EROMAR) & ~(y))
+
+/*
+ * Sync the PHYs by setting data bit and strobing the clock 32 times.
+ */
+static void
+SIP_DECL(sis900_mii_sync)(struct sip_softc *sc)
+{
+ register int i;
+
+ SIS_SET_EROMAR(sc, EROMAR_MDDIR | EROMAR_MDIO);
+
+ for (i = 0; i < 32; i++) {
+ SIS_SET_EROMAR(sc, EROMAR_MDC);
+ DELAY(1);
+ SIS_CLR_EROMAR(sc, EROMAR_MDC);
+ DELAY(1);
+ }
+}
+
+/*
+ * Clock a series of bits through the MII.
+ */
+static void
+SIP_DECL(sis900_mii_send)(struct sip_softc *sc, u_int32_t bits, int cnt)
+{
+ int i;
+
+ SIS_CLR_EROMAR(sc, EROMAR_MDC);
+
+ /* Send first cnt bits of 'bits' */
+ for (i = (0x1 << (cnt - 1)); i; i >>= 1) {
+ if (bits & i)
+ SIS_SET_EROMAR(sc, EROMAR_MDIO);
+ else
+ SIS_CLR_EROMAR(sc, EROMAR_MDIO);
+ DELAY(1);
+ SIS_CLR_EROMAR(sc, EROMAR_MDC);
+ DELAY(1);
+ SIS_SET_EROMAR(sc, EROMAR_MDC);
+ }
+}
+
/*
* sip_sis900_mii_readreg: [mii interface function]
*
@@ -3062,23 +3117,90 @@
SIP_DECL(sis900_mii_readreg)(struct device *self, int phy, int reg)
{
struct sip_softc *sc = (struct sip_softc *) self;
- u_int32_t enphy;
+ u_int32_t ack, val = 0;
+ int s, i;
/*
* The SiS 900 has only an internal PHY on the MII. Only allow
* MII address 0.
*/
- if (sc->sc_model->sip_product == PCI_PRODUCT_SIS_900 &&
- sc->sc_rev < SIS_REV_635 && phy != 0)
- return (0);
-
- bus_space_write_4(sc->sc_st, sc->sc_sh, SIP_ENPHY,
- (phy << ENPHY_PHYADDR_SHIFT) | (reg << ENPHY_REGADDR_SHIFT) |
- ENPHY_RWCMD | ENPHY_ACCESS);
- do {
- enphy = bus_space_read_4(sc->sc_st, sc->sc_sh, SIP_ENPHY);
- } while (enphy & ENPHY_ACCESS);
- return ((enphy & ENPHY_PHYDATA) >> ENPHY_DATA_SHIFT);
+ if (sc->sc_model->sip_product != PCI_PRODUCT_SIS_900 ||
+ sc->sc_rev < SIS_REV_635) {
+ if (sc->sc_model->sip_product == PCI_PRODUCT_SIS_900 && phy != 0)
+ return (0);
+
+ bus_space_write_4(sc->sc_st, sc->sc_sh, SIP_ENPHY,
+ (phy << ENPHY_PHYADDR_SHIFT) | (reg << ENPHY_REGADDR_SHIFT) |
+ ENPHY_RWCMD | ENPHY_ACCESS);
+ do {
+ val = bus_space_read_4(sc->sc_st, sc->sc_sh, SIP_ENPHY);
+ } while (val & ENPHY_ACCESS);
+ return ((val & ENPHY_PHYDATA) >> ENPHY_DATA_SHIFT);
+ }
+
+ s = splnet();
+
+ /* Use mdio access from FreeBSD (apparently inspired by Linux) */
+ SIS_SET_EROMAR(sc, EROMAR_MDDIR);
+
+ SIP_DECL(sis900_mii_sync)(sc);
+
+ /*
+ * Send command/address info.
+ */
+ SIP_DECL(sis900_mii_send)(sc, SIS_MII_STARTDELIM, 2);
+ SIP_DECL(sis900_mii_send)(sc, SIS_MII_READOP, 2);
+ SIP_DECL(sis900_mii_send)(sc, phy, 5);
+ SIP_DECL(sis900_mii_send)(sc, reg, 5);
+
+ /* Idle bit */
+ SIS_CLR_EROMAR(sc, EROMAR_MDC | EROMAR_MDIO);
+ DELAY(1);
+ SIS_SET_EROMAR(sc, EROMAR_MDC);
+ DELAY(1);
+
+ /* Turn off xmit. */
+ SIS_CLR_EROMAR(sc, EROMAR_MDDIR);
+
+ /* Check for ack */
+ SIS_CLR_EROMAR(sc, EROMAR_MDC);
+ DELAY(1);
+
+ ack = bus_space_read_4(sc->sc_st, sc->sc_sh, SIP_EROMAR) & EROMAR_MDIO;
+
+ SIS_SET_EROMAR(sc, EROMAR_MDC);
+ DELAY(1);
+
+ /*
+ * Now try reading data bits. If the ack failed, we still
+ * need to clock through 16 cycles to keep the PHY(s) in sync.
+ */
+ if (ack)
+ for (i = 0; i < 16; i++) {
+ SIS_CLR_EROMAR(sc, EROMAR_MDC);
+ DELAY(1);
+ SIS_SET_EROMAR(sc, EROMAR_MDC);
+ DELAY(1);
+ }
+ else
+ for (i = 0x8000; i; i >>= 1) {
+ SIS_CLR_EROMAR(sc, EROMAR_MDC);
+ DELAY(1);
+ if (bus_space_read_4(sc->sc_st, sc->sc_sh, SIP_EROMAR) & EROMAR_MDIO)
+ val |= i;
+ DELAY(1);
+ SIS_SET_EROMAR(sc, EROMAR_MDC);
+ DELAY(1);
+ }
+
+ SIS_CLR_EROMAR(sc, EROMAR_MDC);
+ DELAY(1);
+ SIS_SET_EROMAR(sc, EROMAR_MDC);
+ DELAY(1);
+
+ splx(s);
+
+ return(val);
}
/*
@@ -3091,21 +3213,54 @@
{
struct sip_softc *sc = (struct sip_softc *) self;
u_int32_t enphy;
+ int s;
/*
* The SiS 900 has only an internal PHY on the MII. Only allow
* MII address 0.
*/
- if (sc->sc_model->sip_product == PCI_PRODUCT_SIS_900 &&
- sc->sc_rev < SIS_REV_635 && phy != 0)
+ if (sc->sc_model->sip_product != PCI_PRODUCT_SIS_900 ||
+ sc->sc_rev < SIS_REV_635) {
+ if (sc->sc_model->sip_product == PCI_PRODUCT_SIS_900 && phy != 0)
+ return;
+
+ bus_space_write_4(sc->sc_st, sc->sc_sh, SIP_ENPHY,
+ (val << ENPHY_DATA_SHIFT) | (phy << ENPHY_PHYADDR_SHIFT) |
+ (reg << ENPHY_REGADDR_SHIFT) | ENPHY_ACCESS);
+ do {
+ enphy = bus_space_read_4(sc->sc_st, sc->sc_sh, SIP_ENPHY);
+ } while (enphy & ENPHY_ACCESS);
return;
-
- bus_space_write_4(sc->sc_st, sc->sc_sh, SIP_ENPHY,
- (val << ENPHY_DATA_SHIFT) | (phy << ENPHY_PHYADDR_SHIFT) |
- (reg << ENPHY_REGADDR_SHIFT) | ENPHY_ACCESS);
- do {
- enphy = bus_space_read_4(sc->sc_st, sc->sc_sh, SIP_ENPHY);
- } while (enphy & ENPHY_ACCESS);
+ }
+
+ s = splnet();
+
+ /*
+ * Turn on data output.
+ */
+ SIS_SET_EROMAR(sc, EROMAR_MDDIR);
+
+ SIP_DECL(sis900_mii_sync)(sc);
+
+ SIP_DECL(sis900_mii_send)(sc, SIS_MII_STARTDELIM, 2);
+ SIP_DECL(sis900_mii_send)(sc, SIS_MII_WRITEOP, 2);
+ SIP_DECL(sis900_mii_send)(sc, phy, 5);
+ SIP_DECL(sis900_mii_send)(sc, reg, 5);
+ SIP_DECL(sis900_mii_send)(sc, SIS_MII_TURNAROUND, 2);
+ SIP_DECL(sis900_mii_send)(sc, val, 16);
+
+ /* Idle bit. */
+ SIS_SET_EROMAR(sc, EROMAR_MDC);
+ DELAY(1);
+ SIS_CLR_EROMAR(sc, EROMAR_MDC);
+ DELAY(1);
+
+ /*
+ * Turn off xmit.
+ */
+ SIS_CLR_EROMAR(sc, EROMAR_MDDIR);
+
+ splx(s);
}
/*
@@ -3309,6 +3464,19 @@
enaddr[5] = eeprom_data[SIP_DP83820_EEPROM_PMATCH0 / 2] >> 8;
}
#else /* ! DP83820 */
+static void
+SIP_DECL(sis900_eeprom_delay)(struct sip_softc *sc)
+{
+ int i;
+
+ /*
+ * FreeBSD goes from (300/33)+1 [10] to 0. There must be
+ * a reason, but I don't know it.
+ */
+ for (i = 0; i < 10; i++)
+ bus_space_read_4(sc->sc_st, sc->sc_sh, SIP_CR);
+}
+
void
SIP_DECL(sis900_read_macaddr)(struct sip_softc *sc,
const struct pci_attach_args *pa, u_int8_t *enaddr)
@@ -3345,6 +3513,52 @@
0xffff;
break;
+ case SIS_REV_960:
Home |
Main Index |
Thread Index |
Old Index