diff options
Diffstat (limited to 'sys/dev/pci/if_che.c')
-rw-r--r-- | sys/dev/pci/if_che.c | 231 |
1 files changed, 222 insertions, 9 deletions
diff --git a/sys/dev/pci/if_che.c b/sys/dev/pci/if_che.c index 8731cb2d224..a5ab55848da 100644 --- a/sys/dev/pci/if_che.c +++ b/sys/dev/pci/if_che.c @@ -1,4 +1,4 @@ -/* $OpenBSD: if_che.c,v 1.6 2007/05/28 07:42:22 claudio Exp $ */ +/* $OpenBSD: if_che.c,v 1.7 2007/05/28 16:26:23 claudio Exp $ */ /* * Copyright (c) 2007 Claudio Jeker <claudio@openbsd.org> @@ -56,10 +56,57 @@ #define CHE_PCI_F_VPD_ADDR 0x80000000 #define CHE_PCI_VPD_BASE 0xc00 +#define CHE_REG_T3DBG_GPIO_EN 0xd0 +#define CHE_T3DBG_F_GPIO11_OEN 0x08000000 +#define CHE_T3DBG_F_GPIO10_OEN 0x04000000 +#define CHE_T3DBG_F_GPIO9_OEN 0x02000000 +#define CHE_T3DBG_F_GPIO8_OEN 0x01000000 +#define CHE_T3DBG_F_GPIO7_OEN 0x00800000 +#define CHE_T3DBG_F_GPIO6_OEN 0x00400000 +#define CHE_T3DBG_F_GPIO5_OEN 0x00200000 +#define CHE_T3DBG_F_GPIO4_OEN 0x00100000 +#define CHE_T3DBG_F_GPIO3_OEN 0x00080000 +#define CHE_T3DBG_F_GPIO2_OEN 0x00040000 +#define CHE_T3DBG_F_GPIO1_OEN 0x00020000 +#define CHE_T3DBG_F_GPIO0_OEN 0x00010000 +#define CHE_T3DBG_F_GPIO11_OUT_VAL 0x00000800 +#define CHE_T3DBG_F_GPIO10_OUT_VAL 0x00000400 +#define CHE_T3DBG_F_GPIO9_OUT_VAL 0x00000200 +#define CHE_T3DBG_F_GPIO8_OUT_VAL 0x00000100 +#define CHE_T3DBG_F_GPIO7_OUT_VAL 0x00000080 +#define CHE_T3DBG_F_GPIO6_OUT_VAL 0x00000040 +#define CHE_T3DBG_F_GPIO5_OUT_VAL 0x00000020 +#define CHE_T3DBG_F_GPIO4_OUT_VAL 0x00000010 +#define CHE_T3DBG_F_GPIO3_OUT_VAL 0x00000008 +#define CHE_T3DBG_F_GPIO2_OUT_VAL 0x00000004 +#define CHE_T3DBG_F_GPIO1_OUT_VAL 0x00000002 +#define CHE_T3DBG_F_GPIO0_OUT_VAL 0x00000001 +#define CHE_REG_I2C_CFG 0x6a0 +#define CHE_I2C_CLKDIV(_x) ((_x) && 0xfff) +#define CHE_REG_MI1_CFG 0x6b0 +#define CHE_REG_MI1_ADDR 0x6b4 +#define CHE_REG_MI1_DATA 0x6b8 +#define CHE_REG_MI1_OP 0x6bc +#define CHE_MI1_F_BUSY (1U << 31) +#define CHE_MI1_F_ST 0x8 +#define CHE_MI1_F_PREEN 0x4 +#define CHE_MI1_F_MDIINV 0x2 +#define CHE_MI1_F_MDIEN 0x1 +#define CHE_MI1_CLKDIV(_x) ((_x) << 5) +#define CHE_MI1_PHYADDR(_x) ((_x) << 5) +#define CHE_MI1_OP(_x) ((_x) & 0x3) #define CHE_REG_PL_RST 0x6f0 #define CHE_RST_F_CRSTWRM 0x2 #define CHE_RST_F_CRSTWRMMODE 0x1 #define CHE_REG_PL_REV 0x6f4 +#define CHE_REG_XGM_PORT_CFG 0x8b8 +#define CHE_XGMAC0_0_BASE_ADDR 0x800 +#define CHE_XGMAC0_1_BASE_ADDR 0xa00 +#define CHE_XGM_REG(_r, _i) \ + ((_r) + (_i) * (CHE_XGMAC0_1_BASE_ADDR - CHE_XGMAC0_0_BASE_ADDR)) +#define CHE_XGM_PORTSPEED(_x) ((_x) << 1) +#define CHE_XGM_F_ENRGMII 0x1 +#define CHE_XGM_F_CLKDIVRESET 0x8 /* serial flash and firmware definitions */ #define CHE_REG_SF_DATA 0x6d8 @@ -159,7 +206,11 @@ struct cheg_softc { bus_space_handle_t sc_memh; bus_size_t sc_mems; - u_int32_t sc_rev; /* card revision */ + u_int32_t sc_rev; /* card revision */ + u_int32_t sc_cclk; /* core clock */ + u_int32_t sc_mdc; /* mdio clock */ + + pci_vendor_id_t sc_product; }; int cheg_match(struct device *, void *, void *); @@ -221,6 +272,7 @@ int che_read_eeprom(struct cheg_softc *, struct pci_attach_args *, int che_get_vpd(struct cheg_softc *, struct pci_attach_args *, void *, size_t); void che_conv_lladdr(char *, u_int8_t *); +u_int32_t che_conv_num(char *, size_t); void che_reset(struct cheg_softc *); int che_ioctl(struct ifnet *, u_long, caddr_t); void che_watchdog(struct ifnet *); @@ -231,6 +283,8 @@ int che_ifmedia_upd(struct ifnet *); void che_ifmedia_sts(struct ifnet *, struct ifmediareq *); int che_miibus_readreg(struct device *, int, int); void che_miibus_writereg(struct device *, int, int, int); +int che_miibus_ind_readreg(struct device *, int, int); +void che_miibus_ind_writereg(struct device *, int, int, int); void che_miibus_statchg(struct device *); /* bus_space wrappers */ @@ -238,6 +292,9 @@ u_int32_t che_read(struct cheg_softc *, bus_size_t); void che_write(struct cheg_softc *, bus_size_t, u_int32_t); int che_waitfor(struct cheg_softc *, bus_size_t, u_int32_t, int); +/* HW low-level functions */ +void che_hw_init(struct cheg_softc *); + /* cheg */ struct cheg_device { pci_vendor_id_t cd_vendor; @@ -336,6 +393,12 @@ cheg_attach(struct device *parent, struct device *self, void *aux) FW_VERS_TYPE(vers) ? "T" : "N", FW_VERS_MAJOR(vers), FW_VERS_MINOR(vers), FW_VERS_MICRO(vers)); + sc->sc_product = PCI_PRODUCT(pa->pa_id); + sc->sc_cclk = che_conv_num(vpd.cclk_data, sizeof(vpd.cclk_data)); + sc->sc_mdc = che_conv_num(vpd.mdc_data, sizeof(vpd.mdc_data)); + + che_hw_init(sc); + caa.caa_pa = pa; che_conv_lladdr(vpd.na_data, caa.caa_lladdr); @@ -391,6 +454,8 @@ che_attach(struct device *parent, struct device *self, void *aux) sc->sc_port = caa->caa_port; bcopy(caa->caa_lladdr, sc->sc_ac.ac_enaddr, ETHER_ADDR_LEN); + printf(": address %s\n", ether_sprintf(sc->sc_ac.ac_enaddr)); + ifp = &sc->sc_ac.ac_if; ifp->if_softc = sc; ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; @@ -406,17 +471,24 @@ che_attach(struct device *parent, struct device *self, void *aux) che_ifmedia_upd, che_ifmedia_sts); sc->sc_mii.mii_ifp = ifp; - sc->sc_mii.mii_readreg = che_miibus_readreg; - sc->sc_mii.mii_writereg = che_miibus_writereg; + sc->sc_mii.mii_readreg = che_miibus_ind_readreg; + sc->sc_mii.mii_writereg = che_miibus_ind_writereg; sc->sc_mii.mii_statchg = che_miibus_statchg; mii_attach(self, &sc->sc_mii, 0xffffffff, MII_PHY_ANY, - MII_OFFSET_ANY, MIIF_DOPAUSE); + MII_OFFSET_ANY, MIIF_DOPAUSE | MIIF_HAVEFIBER); + + if (LIST_FIRST(&sc->sc_mii.mii_phys) == NULL) { + printf("%s: no PHY found!\n", sc->sc_dev.dv_xname); + ifmedia_add(&sc->sc_mii.mii_media, IFM_ETHER|IFM_MANUAL, + 0, NULL); + ifmedia_set(&sc->sc_mii.mii_media, IFM_ETHER|IFM_MANUAL); + } else + ifmedia_set(&sc->sc_mii.mii_media, IFM_ETHER|IFM_AUTO); if_attach(ifp); ether_ifattach(ifp); - printf(": address %s\n", ether_sprintf(sc->sc_ac.ac_enaddr)); return; } @@ -560,6 +632,21 @@ che_conv_lladdr(char *mac, u_int8_t *lladdr) } } +u_int32_t +che_conv_num(char *num, size_t len) +{ + size_t i; + u_int32_t n = 0; + + for (i = 0; i < len; i++) { + if (num[i] >= '0' && num[i] <= '9') + n = 10 * n + (num[i] - '0'); + else + break; + } + return (n); +} + void che_reset(struct cheg_softc *sc) { @@ -610,15 +697,66 @@ che_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr) int che_miibus_readreg(struct device *dev, int phy, int reg) { - //struct che_softc *sc = (struct che_softc *)dev; + struct che_softc *sc = (struct che_softc *)dev; + u_int32_t addr = CHE_MI1_PHYADDR(phy) | reg; - return (0); + che_write(sc->sc_cheg, CHE_REG_MI1_ADDR, addr); + che_write(sc->sc_cheg, CHE_REG_MI1_OP, CHE_MI1_OP(2)); + + if (che_waitfor(sc->sc_cheg, CHE_REG_MI1_OP, CHE_MI1_F_BUSY, 20)) + return (0); + + return ((int)che_read(sc->sc_cheg, CHE_REG_MI1_DATA)); } void che_miibus_writereg(struct device *dev, int phy, int reg, int val) { - //struct che_softc *sc = (struct che_softc *)dev; + struct che_softc *sc = (struct che_softc *)dev; + u_int32_t addr = CHE_MI1_PHYADDR(phy) | reg; + + che_write(sc->sc_cheg, CHE_REG_MI1_ADDR, addr); + che_write(sc->sc_cheg, CHE_REG_MI1_DATA, val); + che_write(sc->sc_cheg, CHE_REG_MI1_OP, CHE_MI1_OP(1)); + che_waitfor(sc->sc_cheg, CHE_REG_MI1_OP, CHE_MI1_F_BUSY, 20); +} + +int +che_miibus_ind_readreg(struct device *dev, int phy, int reg) +{ + struct che_softc *sc = (struct che_softc *)dev; + + che_write(sc->sc_cheg, CHE_REG_MI1_ADDR, CHE_MI1_PHYADDR(phy)); + che_write(sc->sc_cheg, CHE_REG_MI1_DATA, reg); + che_write(sc->sc_cheg, CHE_REG_MI1_OP, CHE_MI1_OP(0)); + + if (che_waitfor(sc->sc_cheg, CHE_REG_MI1_OP, CHE_MI1_F_BUSY, 20)) + return (0); + + che_write(sc->sc_cheg, CHE_REG_MI1_OP, CHE_MI1_OP(3)); + + if (che_waitfor(sc->sc_cheg, CHE_REG_MI1_OP, CHE_MI1_F_BUSY, 20)) + return (0); + + return ((int)che_read(sc->sc_cheg, CHE_REG_MI1_DATA)); +} + +void +che_miibus_ind_writereg(struct device *dev, int phy, int reg, int val) +{ + struct che_softc *sc = (struct che_softc *)dev; + + che_write(sc->sc_cheg, CHE_REG_MI1_ADDR, CHE_MI1_PHYADDR(phy)); + che_write(sc->sc_cheg, CHE_REG_MI1_DATA, reg); + che_write(sc->sc_cheg, CHE_REG_MI1_OP, CHE_MI1_OP(0)); + + if (che_waitfor(sc->sc_cheg, CHE_REG_MI1_OP, CHE_MI1_F_BUSY, 20)) + return; + + che_write(sc->sc_cheg, CHE_REG_MI1_DATA, val); + che_write(sc->sc_cheg, CHE_REG_MI1_OP, CHE_MI1_OP(1)); + + che_waitfor(sc->sc_cheg, CHE_REG_MI1_OP, CHE_MI1_F_BUSY, 20); } void @@ -660,3 +798,78 @@ che_waitfor(struct cheg_softc *sc, bus_size_t r, u_int32_t mask, int tries) } return (EAGAIN); } + +void +che_hw_init(struct cheg_softc *sc) +{ + u_int32_t mi1_reg; + u_int32_t i2c_reg; + u_int32_t gpio_reg; + u_int32_t port_reg; + + mi1_reg = CHE_MI1_F_PREEN | + CHE_MI1_CLKDIV(sc->sc_cclk / (2 * sc->sc_mdc) - 1); + + i2c_reg = CHE_I2C_CLKDIV(sc->sc_cclk / 80 - 1); /* 80KHz */ + + gpio_reg = CHE_T3DBG_F_GPIO0_OEN | CHE_T3DBG_F_GPIO0_OUT_VAL; + + switch (sc->sc_product) { + case PCI_PRODUCT_CHELSIO_PE9000: + gpio_reg |= CHE_T3DBG_F_GPIO2_OEN | CHE_T3DBG_F_GPIO2_OUT_VAL | + CHE_T3DBG_F_GPIO4_OEN | CHE_T3DBG_F_GPIO4_OUT_VAL; + port_reg = CHE_XGM_PORTSPEED(2); + break; + case PCI_PRODUCT_CHELSIO_T302E: + case PCI_PRODUCT_CHELSIO_T302X: + case PCI_PRODUCT_CHELSIO_T3B02: + gpio_reg |= CHE_T3DBG_F_GPIO2_OEN | CHE_T3DBG_F_GPIO2_OUT_VAL | + CHE_T3DBG_F_GPIO4_OEN | CHE_T3DBG_F_GPIO4_OUT_VAL; + port_reg = CHE_XGM_PORTSPEED(2); + break; + case PCI_PRODUCT_CHELSIO_T310E: + case PCI_PRODUCT_CHELSIO_T310X: + case PCI_PRODUCT_CHELSIO_T3B10: + mi1_reg |= CHE_MI1_F_ST; + gpio_reg |= CHE_T3DBG_F_GPIO1_OEN | CHE_T3DBG_F_GPIO1_OUT_VAL | + CHE_T3DBG_F_GPIO6_OEN | CHE_T3DBG_F_GPIO6_OUT_VAL | + CHE_T3DBG_F_GPIO7_OEN | + CHE_T3DBG_F_GPIO10_OEN | CHE_T3DBG_F_GPIO10_OUT_VAL; + port_reg = CHE_XGM_PORTSPEED(3); + port_reg |= CHE_XGM_F_ENRGMII; + break; + case PCI_PRODUCT_CHELSIO_T320X: + case PCI_PRODUCT_CHELSIO_T320E: + case PCI_PRODUCT_CHELSIO_T3B20: + mi1_reg |= CHE_MI1_F_ST; + gpio_reg |= CHE_T3DBG_F_GPIO1_OEN | CHE_T3DBG_F_GPIO1_OUT_VAL | + CHE_T3DBG_F_GPIO2_OEN | + CHE_T3DBG_F_GPIO4_OEN | + CHE_T3DBG_F_GPIO5_OEN | CHE_T3DBG_F_GPIO5_OUT_VAL | + CHE_T3DBG_F_GPIO6_OEN | CHE_T3DBG_F_GPIO6_OUT_VAL | + CHE_T3DBG_F_GPIO7_OEN | + CHE_T3DBG_F_GPIO10_OEN | CHE_T3DBG_F_GPIO10_OUT_VAL | + CHE_T3DBG_F_GPIO11_OEN; + port_reg = CHE_XGM_PORTSPEED(3); + port_reg |= CHE_XGM_F_ENRGMII; + break; + } + + if (sc->sc_rev == 0) + port_reg |= CHE_XGM_F_ENRGMII; + + /* write all registers */ + che_write(sc, CHE_REG_MI1_CFG, mi1_reg); + che_write(sc, CHE_REG_I2C_CFG, i2c_reg); + che_write(sc, CHE_REG_T3DBG_GPIO_EN, gpio_reg); + + che_write(sc, CHE_REG_XGM_PORT_CFG, port_reg); + (void)che_read(sc, CHE_REG_XGM_PORT_CFG); + + port_reg |= CHE_XGM_F_CLKDIVRESET; + + che_write(sc, CHE_REG_XGM_PORT_CFG, port_reg); + (void)che_read(sc, CHE_REG_XGM_PORT_CFG); + che_write(sc, CHE_XGM_REG(CHE_REG_XGM_PORT_CFG, 1), port_reg); + (void)che_read(sc, CHE_REG_XGM_PORT_CFG); +} |