/* $FabBSD$ */ /* $OpenBSD: if_che.c,v 1.8 2007/05/30 05:11:53 reyk Exp $ */ /* * Copyright (c) 2007 Claudio Jeker * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* registers & defines */ #define CHE_PCI_BAR 0x10 #define CHE_PCI_CAP_ID_VPD 0x03 #define CHE_PCI_VPD_DATA 0x4 #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 #define CHE_REG_SF_OP 0x6dc #define CHE_SF_SEC_SIZE (64 * 1024) /* serial flash sector size */ #define CHE_SF_SIZE (8 * CHE_SF_SEC_SIZE) /* serial flash size */ #define CHE_SF_PROG_PAGE 2 #define CHE_SF_WR_DISABLE 4 #define CHE_SF_RD_STATUS 5 /* read status register */ #define CHE_SF_WR_ENABLE 6 #define CHE_SF_RD_DATA 11 #define CHE_SF_SEC_ERASE 216 #define CHE_SF_F_BUSY (1U << 31) #define CHE_SF_F_OP 0x1 #define CHE_SF_CONT(_x) ((_x) << 3) #define CHE_SF_BYTECNT_MASK 0x3 #define CHE_SF_BYTECNT(_x) (((_x) & CHE_SF_BYTECNT_MASK) << 1) #define FW_FLASH_BOOT_ADDR 0x70000 /* start address of FW in flash */ #define FW_VERS_ADDR 0x77ffc /* flash address holding FW version */ #define FW_VERS_TYPE_N3 0 #define FW_VERS_TYPE_T3 1 #define FW_VERS_TYPE(_x) (((_x) >> 28) & 0xf) #define FW_VERS_MAJOR(_x) (((_x) >> 16) & 0xfff) #define FW_VERS_MINOR(_x) (((_x) >> 8) & 0xff) #define FW_VERS_MICRO(_x) ((_x) & 0xff) /* Partial EEPROM Vital Product Data structure. */ struct che_vpd { u_int8_t id_tag; u_int8_t id_len[2]; u_int8_t id_data[16]; u_int8_t vpdr_tag; u_int8_t vpdr_len[2]; u_int8_t pn_name[2]; /* part number */ u_int8_t pn_len; u_int8_t pn_data[16]; u_int8_t ec_name[2]; /* EC level */ u_int8_t ec_len; u_int8_t ec_data[16]; u_int8_t sn_name[2]; /* serial number */ u_int8_t sn_len; u_int8_t sn_data[16]; u_int8_t na_name[2]; /* MAC address base */ u_int8_t na_len; u_int8_t na_data[12]; u_int8_t cclk_name[2]; /* core clock */ u_int8_t cclk_len; u_int8_t cclk_data[6]; u_int8_t mclk_name[2]; /* mem clock */ u_int8_t mclk_len; u_int8_t mclk_data[6]; u_int8_t uclk_name[2]; /* uP clock */ u_int8_t uclk_len; u_int8_t uclk_data[6]; u_int8_t mdc_name[2]; /* MDIO clock */ u_int8_t mdc_len; u_int8_t mdc_data[6]; u_int8_t mt_name[2]; /* mem timing */ u_int8_t mt_len; u_int8_t mt_data[2]; u_int8_t xaui0cfg_name[2]; /* XAUI0 config */ u_int8_t xaui0cfg_len; u_int8_t xaui0cfg_data[6]; u_int8_t xaui1cfg_name[2]; /* XAUI1 config */ u_int8_t xaui1cfg_len; u_int8_t xaui1cfg_data[6]; u_int8_t port0_name[2]; /* PHY0 */ u_int8_t port0_len; u_int8_t port0_data[2]; u_int8_t port1_name[2]; /* PHY1 */ u_int8_t port1_len; u_int8_t port1_data[2]; u_int8_t port2_name[2]; /* PHY2 */ u_int8_t port2_len; u_int8_t port2_data[2]; u_int8_t port3_name[2]; /* PHY3 */ u_int8_t port3_len; u_int8_t port3_data[2]; u_int8_t rv_name[2]; /* csum */ u_int8_t rv_len; u_int8_t rv_data[1]; u_int8_t pad[4]; /* for multiple-of-4 sizing */ } __packed; #define DEVNAME(_sc) ((_sc)->sc_dev.dv_xname) /* the pci controller */ struct cheg_softc { struct device sc_dev; bus_dma_tag_t sc_dmat; bus_space_tag_t sc_memt; bus_space_handle_t sc_memh; bus_size_t sc_mems; 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 *); void cheg_attach(struct device *, struct device *, void *); int cheg_print(void *, const char *); struct cfattach cheg_ca = { sizeof(struct cheg_softc), cheg_match, cheg_attach }; struct cfdriver cheg_cd = { NULL, "cheg", DV_DULL }; /* glue between the controller and the port */ struct che_attach_args { struct pci_attach_args *caa_pa; pci_intr_handle_t caa_ih; int caa_port; u_int8_t caa_lladdr[ETHER_ADDR_LEN]; }; /* che itself */ struct che_softc { struct device sc_dev; struct arpcom sc_ac; struct mii_data sc_mii; struct cheg_softc *sc_cheg; void *sc_ih; int sc_port; }; int che_match(struct device *, void *, void *); void che_attach(struct device *, struct device *, void *); struct cfattach che_ca = { sizeof(struct che_softc), che_match, che_attach }; struct cfdriver che_cd = { NULL, "che", DV_IFNET }; int che_write_flash_reg(struct cheg_softc *, size_t, int, u_int32_t); int che_read_flash_reg(struct cheg_softc *, size_t, int, u_int32_t *); int che_read_flash_multi4(struct cheg_softc *, u_int, u_int32_t *, size_t); int che_read_eeprom(struct cheg_softc *, struct pci_attach_args *, pcireg_t, pcireg_t *); 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 *); void che_start(struct ifnet *); /* ifmedia & mii helper functions */ 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 */ 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; pci_vendor_id_t cd_product; u_int cd_nports; }; const struct cheg_device *cheg_lookup(struct pci_attach_args *); const struct cheg_device che_devices[] = { { PCI_VENDOR_CHELSIO, PCI_PRODUCT_CHELSIO_PE9000, 2 }, { PCI_VENDOR_CHELSIO, PCI_PRODUCT_CHELSIO_T302E, 2 }, { PCI_VENDOR_CHELSIO, PCI_PRODUCT_CHELSIO_T302X, 2 }, { PCI_VENDOR_CHELSIO, PCI_PRODUCT_CHELSIO_T310E, 1 }, { PCI_VENDOR_CHELSIO, PCI_PRODUCT_CHELSIO_T310X, 1 }, { PCI_VENDOR_CHELSIO, PCI_PRODUCT_CHELSIO_T320E, 2 }, { PCI_VENDOR_CHELSIO, PCI_PRODUCT_CHELSIO_T320X, 2 }, { PCI_VENDOR_CHELSIO, PCI_PRODUCT_CHELSIO_T3B02, 2 }, { PCI_VENDOR_CHELSIO, PCI_PRODUCT_CHELSIO_T3B10, 1 }, { PCI_VENDOR_CHELSIO, PCI_PRODUCT_CHELSIO_T3B20, 2 } }; const struct cheg_device * cheg_lookup(struct pci_attach_args *pa) { int i; const struct cheg_device *cd; for (i = 0; i < sizeof(che_devices)/sizeof(che_devices[0]); i++) { cd = &che_devices[i]; if (cd->cd_vendor == PCI_VENDOR(pa->pa_id) && cd->cd_product == PCI_PRODUCT(pa->pa_id)) return (cd); } return (NULL); } int cheg_match(struct device *parent, void *match, void *aux) { struct pci_attach_args *pa = aux; if (cheg_lookup(pa) != NULL) return (1); return (0); } void cheg_attach(struct device *parent, struct device *self, void *aux) { struct cheg_softc *sc = (struct cheg_softc *)self; struct pci_attach_args *pa = aux; const struct cheg_device *cd; struct che_attach_args caa; struct che_vpd vpd; pcireg_t memtype; u_int32_t vers; u_int i; bzero(&caa, sizeof(caa)); cd = cheg_lookup(pa); sc->sc_dmat = pa->pa_dmat; memtype = pci_mapreg_type(pa->pa_pc, pa->pa_tag, CHE_PCI_BAR); if (pci_mapreg_map(pa, CHE_PCI_BAR, memtype, 0, &sc->sc_memt, &sc->sc_memh, NULL, &sc->sc_mems, 0) != 0) { printf(": unable to map host registers\n"); return; } if (pci_intr_map(pa, &caa.caa_ih) != 0) { printf(": unable to map interrupt\n"); goto unmap; } sc->sc_rev = che_read(sc, CHE_REG_PL_REV); /* reset the beast */ che_reset(sc); if (che_read_flash_multi4(sc, FW_VERS_ADDR, &vers, 1) != 0) { printf(": unable to read flash version\n"); goto unmap; } if (che_get_vpd(sc, pa, &vpd, sizeof(vpd)/sizeof(u_int32_t)) != 0) { printf(": unable to get vital product data\n"); goto unmap; } printf(": %s revision %d firmware %s-%d.%d.%d\n", pci_intr_string(pa->pa_pc, caa.caa_ih), sc->sc_rev, 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); for (i = 0; i < cd->cd_nports; i++) { caa.caa_port = i; config_found(self, &caa, cheg_print); /* * The VPD EEPROM stores only the base Ethernet address for the * card. The last octet is increased by one for every additional * port. */ caa.caa_lladdr[5] += 1; } return; unmap: bus_space_unmap(sc->sc_memt, sc->sc_memh, sc->sc_mems); sc->sc_mems = 0; } int cheg_print(void *aux, const char *pnp) { struct che_attach_args *caa = aux; if (pnp != NULL) printf("\"%s\" at %s", che_cd.cd_name, pnp); printf(" port %d", caa->caa_port); return (UNCONF); } int che_match(struct device *parent, void *match, void *aux) { return (1); } void che_attach(struct device *parent, struct device *self, void *aux) { struct cheg_softc *gsc = (struct cheg_softc *)parent; struct che_softc *sc = (struct che_softc *)self; struct che_attach_args *caa = aux; struct ifnet *ifp; sc->sc_cheg = gsc; 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; ifp->if_ioctl = che_ioctl; ifp->if_start = che_start; ifp->if_watchdog = che_watchdog; ifp->if_hardmtu = MCLBYTES - ETHER_HDR_LEN - ETHER_CRC_LEN; /* XXX */ strlcpy(ifp->if_xname, DEVNAME(sc), IFNAMSIZ); IFQ_SET_MAXLEN(&ifp->if_snd, 400); IFQ_SET_READY(&ifp->if_snd); ifmedia_init(&sc->sc_mii.mii_media, 0, che_ifmedia_upd, che_ifmedia_sts); sc->sc_mii.mii_ifp = ifp; 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 | 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); return; } int che_write_flash_reg(struct cheg_softc *sc, size_t bcnt, int cont, u_int32_t v) { if (che_read(sc, CHE_REG_SF_OP) & CHE_SF_F_BUSY) return (EBUSY); che_write(sc, CHE_REG_SF_DATA, v); che_write(sc, CHE_REG_SF_OP, CHE_SF_CONT(cont) | CHE_SF_BYTECNT(bcnt - 1) | CHE_SF_F_OP); return (che_waitfor(sc, CHE_REG_SF_OP, CHE_SF_F_BUSY, 5)); } int che_read_flash_reg(struct cheg_softc *sc, size_t bcnt, int cont, u_int32_t *vp) { if (che_read(sc, CHE_REG_SF_OP) & CHE_SF_F_BUSY) return (EBUSY); che_write(sc, CHE_REG_SF_OP, CHE_SF_CONT(cont) | CHE_SF_BYTECNT(bcnt - 1)); if (che_waitfor(sc, CHE_REG_SF_OP, CHE_SF_F_BUSY, 5)) return (EAGAIN); *vp = che_read(sc, CHE_REG_SF_DATA); return (0); } int che_read_flash_multi4(struct cheg_softc *sc, u_int addr, u_int32_t *datap, size_t count) { int rv; if (addr + count * sizeof(u_int32_t) > CHE_SF_SIZE || (addr & 3)) panic("%s: che_read_flash_multi4 bad params\n", DEVNAME(sc)); addr = swap32(addr) | CHE_SF_RD_DATA; if ((rv = che_write_flash_reg(sc, 4, 1, addr))) return (rv); if ((rv = che_read_flash_reg(sc, 1, 1, datap))) return (rv); while (count) { if ((rv = che_read_flash_reg(sc, 4, count > 1, datap))) return (rv); count--; datap++; } return (0); } int che_read_eeprom(struct cheg_softc *sc, struct pci_attach_args *pa, pcireg_t addr, pcireg_t *dp) { pcireg_t rv, base; int i = 4; if (!pci_get_capability(pa->pa_pc, pa->pa_tag, CHE_PCI_CAP_ID_VPD, &base, NULL)) { printf("%s: VPD EEPROM not found\n", DEVNAME(sc), addr); return EIO; } addr <<= 16; pci_conf_write(pa->pa_pc, pa->pa_tag, base, addr); while(i--) { delay(10); rv = pci_conf_read(pa->pa_pc, pa->pa_tag, base); if (rv & CHE_PCI_F_VPD_ADDR) break; } if (!(rv & CHE_PCI_F_VPD_ADDR)) { printf("%s: reading EEPROM address 0x%x failed\n", DEVNAME(sc), addr); return EIO; } *dp = pci_conf_read(pa->pa_pc, pa->pa_tag, base + CHE_PCI_VPD_DATA); return (0); } int che_get_vpd(struct cheg_softc *sc, struct pci_attach_args *pa, void *vpd, size_t dwords) { pcireg_t dw0, *dw = vpd; int i; u_int16_t addr; /* * Card information is normally at CHE_PCI_VPD_BASE but some early * cards had it at 0. */ if (che_read_eeprom(sc, pa, CHE_PCI_VPD_BASE, &dw0)) return (1); /* we compare the id_tag which is least significant byte */ addr = ((dw0 & 0xff) == 0x82) ? CHE_PCI_VPD_BASE : 0; for (i = 0; i < dwords; i++) { if (che_read_eeprom(sc, pa, addr + i * 4, &dw[i])) return (1); } return (0); } /* * VPD mac addr is stored as ASCII string so we need to convert it to a * sane representation form. */ void che_conv_lladdr(char *mac, u_int8_t *lladdr) { int i; u_int8_t digit; bzero(lladdr, ETHER_ADDR_LEN); for (i = 0; i < ETHER_ADDR_LEN * 2; i++) { if (mac[i] >= '0' && mac[i] <= '9') digit = mac[i] - '0'; else if (mac[i] >= 'A' && mac[i] <= 'F') digit = mac[i] - 'A' + 10; else if (mac[i] >= 'a' && mac[i] <= 'f') digit = mac[i] - 'a' + 10; if ((i & 1) == 0) digit <<= 4; lladdr[i/2] |= digit; } } 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) { che_write(sc, CHE_REG_PL_RST, CHE_RST_F_CRSTWRM | CHE_RST_F_CRSTWRMMODE); /* Give the card some time to boot */ delay(500); } int che_ioctl(struct ifnet *ifp, u_long cmd, caddr_t addr) { return (EIO); } void che_watchdog(struct ifnet *ifp) { /* XXX */ } void che_start(struct ifnet *ifp) { /* XXX */ } int che_ifmedia_upd(struct ifnet *ifp) { struct che_softc *sc = ifp->if_softc; mii_mediachg(&sc->sc_mii); return (0); } void che_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr) { struct che_softc *sc = ifp->if_softc; mii_pollstat(&sc->sc_mii); ifmr->ifm_active = sc->sc_mii.mii_media_active; ifmr->ifm_status = sc->sc_mii.mii_media_status; } int che_miibus_readreg(struct device *dev, int phy, int reg) { 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_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; 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 che_miibus_statchg(struct device *dev) { struct che_softc *sc = (struct che_softc *)dev; //struct mii_data *mii = &sc->sc_mii; printf("%s: che_miibus_statchg\n", DEVNAME(sc)); } u_int32_t che_read(struct cheg_softc *sc, bus_size_t r) { bus_space_barrier(sc->sc_memt, sc->sc_memh, r, 4, BUS_SPACE_BARRIER_READ); return (bus_space_read_4(sc->sc_memt, sc->sc_memh, r)); } void che_write(struct cheg_softc *sc, bus_size_t r, u_int32_t v) { bus_space_write_4(sc->sc_memt, sc->sc_memh, r, v); bus_space_barrier(sc->sc_memt, sc->sc_memh, r, 4, BUS_SPACE_BARRIER_WRITE); } int che_waitfor(struct cheg_softc *sc, bus_size_t r, u_int32_t mask, int tries) { u_int32_t v; int i; for (i = 0; i < tries; i++) { v = che_read(sc, r); if ((v & mask) == 0) return (0); delay(10); } 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); }