From 7d3b9fd6c302ca1b966ca566630e7f51537603bd Mon Sep 17 00:00:00 2001 From: Gerasim Troeglazov <3deyes@gmail.com> Date: Thu, 17 Sep 2009 05:38:11 +0000 Subject: [PATCH] Update to actual AGE driver version. (Tested on real hardware - work fine) git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@33158 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- .../network/attansic_l1/dev/age/glue.c | 2 +- .../network/attansic_l1/dev/age/if_age.c | 311 ++++++++---------- .../network/attansic_l1/dev/age/if_agereg.h | 9 +- 3 files changed, 146 insertions(+), 176 deletions(-) diff --git a/src/add-ons/kernel/drivers/network/attansic_l1/dev/age/glue.c b/src/add-ons/kernel/drivers/network/attansic_l1/dev/age/glue.c index 9ea581a145..c7a3440f6d 100644 --- a/src/add-ons/kernel/drivers/network/attansic_l1/dev/age/glue.c +++ b/src/add-ons/kernel/drivers/network/attansic_l1/dev/age/glue.c @@ -7,7 +7,7 @@ #include -HAIKU_FBSD_DRIVER_GLUE(attansic_l1, age, pci); +HAIKU_FBSD_DRIVER_GLUE(atl1, age, pci); HAIKU_DRIVER_REQUIREMENTS(FBSD_TASKQUEUES | FBSD_SWI_TASKQUEUE); diff --git a/src/add-ons/kernel/drivers/network/attansic_l1/dev/age/if_age.c b/src/add-ons/kernel/drivers/network/attansic_l1/dev/age/if_age.c index 47ae13ad3e..e84192a28d 100644 --- a/src/add-ons/kernel/drivers/network/attansic_l1/dev/age/if_age.c +++ b/src/add-ons/kernel/drivers/network/attansic_l1/dev/age/if_age.c @@ -28,7 +28,7 @@ /* Driver for Attansic Technology Corp. L1 Gigabit Ethernet. */ #include -__FBSDID("$FreeBSD$"); +__FBSDID("$FreeBSD: src/sys/dev/age/if_age.c,v 1.6 2008/11/07 07:02:28 yongari Exp $"); #include #include @@ -67,7 +67,6 @@ __FBSDID("$FreeBSD$"); #include #include -#include #include "if_agereg.h" #include "if_agevar.h" @@ -93,6 +92,8 @@ __FBSDID("$FreeBSD$"); #define roundup(x, y) ((((x)+((y)-1))/(y))*(y)) #endif +#define AGE_CSUM_FEATURES (CSUM_TCP | CSUM_UDP) + MODULE_DEPEND(age, pci, 1, 1, 1); MODULE_DEPEND(age, ether, 1, 1, 1); MODULE_DEPEND(age, miibus, 1, 1, 1); @@ -120,8 +121,6 @@ static int age_miibus_writereg(device_t, int, int, int); static void age_miibus_statchg(device_t); static void age_mediastatus(struct ifnet *, struct ifmediareq *); static int age_mediachange(struct ifnet *); -static int age_read_vpd_word(struct age_softc *, uint32_t, uint32_t, - uint32_t *); static int age_probe(device_t); static void age_get_macaddr(struct age_softc *); static void age_phy_reset(struct age_softc *); @@ -334,29 +333,6 @@ age_mediachange(struct ifnet *ifp) return (error); } -static int -age_read_vpd_word(struct age_softc *sc, uint32_t vpdc, uint32_t offset, - uint32_t *word) -{ - int i; - - pci_write_config(sc->age_dev, vpdc + PCIR_VPD_ADDR, offset, 2); - for (i = AGE_TIMEOUT; i > 0; i--) { - DELAY(10); - if ((pci_read_config(sc->age_dev, vpdc + PCIR_VPD_ADDR, 2) & - 0x8000) == 0x8000) - break; - } - if (i == 0) { - device_printf(sc->age_dev, "VPD read timeout!\n"); - *word = 0; - return (ETIMEDOUT); - } - - *word = pci_read_config(sc->age_dev, vpdc + PCIR_VPD_DATA, 4); - return (0); -} - static int age_probe(device_t dev) { @@ -382,8 +358,8 @@ age_probe(device_t dev) static void age_get_macaddr(struct age_softc *sc) { - uint32_t ea[2], off, reg, word; - int vpd_error, match, vpdc; + uint32_t ea[2], reg; + int i, vpdc; reg = CSR_READ_4(sc, AGE_SPI_CTRL); if ((reg & SPI_VPD_ENB) != 0) { @@ -392,130 +368,120 @@ age_get_macaddr(struct age_softc *sc) CSR_WRITE_4(sc, AGE_SPI_CTRL, reg); } - vpd_error = 0; - ea[0] = ea[1] = 0; - if ((vpd_error = pci_find_extcap(sc->age_dev, PCIY_VPD, &vpdc)) == 0) { + if (pci_find_extcap(sc->age_dev, PCIY_VPD, &vpdc) == 0) { /* - * PCI VPD capability exists, but it seems that it's - * not in the standard form as stated in PCI VPD - * specification such that driver could not use - * pci_get_vpd_readonly(9) with keyword 'NA'. - * Search VPD data starting at address 0x0100. The data - * chwould be used as initializers to set AGE_PAR0, - * AGE_PAR1 register including other PCI configuration - * registers. + * PCI VPD capability found, let TWSI reload EEPROM. + * This will set ethernet address of controller. */ - word = 0; - match = 0; - reg = 0; - for (off = AGE_VPD_REG_CONF_START; off < AGE_VPD_REG_CONF_END; - off += sizeof(uint32_t)) { - vpd_error = age_read_vpd_word(sc, vpdc, off, &word); - if (vpd_error != 0) - break; - if (match != 0) { - switch (reg) { - case AGE_PAR0: - ea[0] = word; - break; - case AGE_PAR1: - ea[1] = word; - break; - default: - break; - } - match = 0; - } else if ((word & 0xFF) == AGE_VPD_REG_CONF_SIG) { - match = 1; - reg = word >> 16; - } else + CSR_WRITE_4(sc, AGE_TWSI_CTRL, CSR_READ_4(sc, AGE_TWSI_CTRL) | + TWSI_CTRL_SW_LD_START); + for (i = 100; i > 0; i--) { + DELAY(1000); + reg = CSR_READ_4(sc, AGE_TWSI_CTRL); + if ((reg & TWSI_CTRL_SW_LD_START) == 0) break; } - if (off >= AGE_VPD_REG_CONF_END) - vpd_error = ENOENT; - if (vpd_error == 0) { - /* - * Don't blindly trust ethernet address obtained - * from VPD. Check whether ethernet address is - * valid one. Otherwise fall-back to reading - * PAR register. - */ - ea[1] &= 0xFFFF; - if ((ea[0] == 0 && ea[1] == 0) || - (ea[0] == 0xFFFFFFFF && ea[1] == 0xFFFF)) { - if (1 || bootverbose) - device_printf(sc->age_dev, - "invalid ethernet address " - "returned from VPD.\n"); - vpd_error = EINVAL; - } - } - if (vpd_error != 0 && (1 || bootverbose)) - device_printf(sc->age_dev, "VPD access failure!\n"); + if (i == 0) + device_printf(sc->age_dev, + "reloading EEPROM timeout!\n"); } else { - if (1 || bootverbose) + if (bootverbose) device_printf(sc->age_dev, "PCI VPD capability not found!\n"); } - /* - * It seems that L1 also provides a way to extract ethernet - * address via SPI flash interface. Because SPI flash memory - * device of different vendors vary in their instruction - * codes for read ID instruction, it's very hard to get - * instructions codes without detailed information for the - * flash memory device used on ethernet controller. To simplify - * code, just read AGE_PAR0/AGE_PAR1 register to get ethernet - * address which is supposed to be set by hardware during - * power on reset. - */ - if (vpd_error != 0) { - /* - * VPD is mapped to SPI flash memory or BIOS set it. - */ - ea[0] = CSR_READ_4(sc, AGE_PAR0); - ea[1] = CSR_READ_4(sc, AGE_PAR1); - } - - ea[1] &= 0xFFFF; - if ((ea[0] == 0 && ea[1] == 0) || - (ea[0] == 0xFFFFFFFF && ea[1] == 0xFFFF)) { - device_printf(sc->age_dev, - "generating fake ethernet address.\n"); - -#ifdef __HAIKU__ - ea[0] = random(); -#else - ea[0] = arc4random(); -#endif - /* Set OUI to ASUSTek COMPUTER INC. */ - sc->age_eaddr[0] = 0x00; - sc->age_eaddr[1] = 0x1B; - sc->age_eaddr[2] = 0xFC; - sc->age_eaddr[3] = (ea[0] >> 16) & 0xFF; - sc->age_eaddr[4] = (ea[0] >> 8) & 0xFF; - sc->age_eaddr[5] = (ea[0] >> 0) & 0xFF; - } else { - sc->age_eaddr[0] = (ea[1] >> 8) & 0xFF; - sc->age_eaddr[1] = (ea[1] >> 0) & 0xFF; - sc->age_eaddr[2] = (ea[0] >> 24) & 0xFF; - sc->age_eaddr[3] = (ea[0] >> 16) & 0xFF; - sc->age_eaddr[4] = (ea[0] >> 8) & 0xFF; - sc->age_eaddr[5] = (ea[0] >> 0) & 0xFF; - } + ea[0] = CSR_READ_4(sc, AGE_PAR0); + ea[1] = CSR_READ_4(sc, AGE_PAR1); + sc->age_eaddr[0] = (ea[1] >> 8) & 0xFF; + sc->age_eaddr[1] = (ea[1] >> 0) & 0xFF; + sc->age_eaddr[2] = (ea[0] >> 24) & 0xFF; + sc->age_eaddr[3] = (ea[0] >> 16) & 0xFF; + sc->age_eaddr[4] = (ea[0] >> 8) & 0xFF; + sc->age_eaddr[5] = (ea[0] >> 0) & 0xFF; } static void age_phy_reset(struct age_softc *sc) { + uint16_t reg, pn; + int i, linkup; /* Reset PHY. */ CSR_WRITE_4(sc, AGE_GPHY_CTRL, GPHY_CTRL_RST); -// pause("agephy", hz / 1000); - DELAY(1000); + DELAY(2000); CSR_WRITE_4(sc, AGE_GPHY_CTRL, GPHY_CTRL_CLR); - DELAY(1000); - //pause("agephy", hz / 1000); + DELAY(2000); + +#define ATPHY_DBG_ADDR 0x1D +#define ATPHY_DBG_DATA 0x1E +#define ATPHY_CDTC 0x16 +#define PHY_CDTC_ENB 0x0001 +#define PHY_CDTC_POFF 8 +#define ATPHY_CDTS 0x1C +#define PHY_CDTS_STAT_OK 0x0000 +#define PHY_CDTS_STAT_SHORT 0x0100 +#define PHY_CDTS_STAT_OPEN 0x0200 +#define PHY_CDTS_STAT_INVAL 0x0300 +#define PHY_CDTS_STAT_MASK 0x0300 + + /* Check power saving mode. Magic from Linux. */ + age_miibus_writereg(sc->age_dev, sc->age_phyaddr, MII_BMCR, BMCR_RESET); + for (linkup = 0, pn = 0; pn < 4; pn++) { + age_miibus_writereg(sc->age_dev, sc->age_phyaddr, ATPHY_CDTC, + (pn << PHY_CDTC_POFF) | PHY_CDTC_ENB); + for (i = 200; i > 0; i--) { + DELAY(1000); + reg = age_miibus_readreg(sc->age_dev, sc->age_phyaddr, + ATPHY_CDTC); + if ((reg & PHY_CDTC_ENB) == 0) + break; + } + DELAY(1000); + reg = age_miibus_readreg(sc->age_dev, sc->age_phyaddr, + ATPHY_CDTS); + if ((reg & PHY_CDTS_STAT_MASK) != PHY_CDTS_STAT_OPEN) { +#if 1 + device_printf(sc->age_dev, "link found!\n"); +#endif + linkup++; + break; + } + } + age_miibus_writereg(sc->age_dev, sc->age_phyaddr, MII_BMCR, + BMCR_RESET | BMCR_AUTOEN | BMCR_STARTNEG); + if (linkup == 0) { +#if 1 + device_printf(sc->age_dev, "waking up PHY\n"); +#endif + age_miibus_writereg(sc->age_dev, sc->age_phyaddr, + ATPHY_DBG_ADDR, 0); + age_miibus_writereg(sc->age_dev, sc->age_phyaddr, + ATPHY_DBG_DATA, 0x124E); + age_miibus_writereg(sc->age_dev, sc->age_phyaddr, + ATPHY_DBG_ADDR, 1); + reg = age_miibus_readreg(sc->age_dev, sc->age_phyaddr, + ATPHY_DBG_DATA); + age_miibus_writereg(sc->age_dev, sc->age_phyaddr, + ATPHY_DBG_DATA, reg | 0x03); + /* XXX */ + DELAY(1500 * 1000); + age_miibus_writereg(sc->age_dev, sc->age_phyaddr, + ATPHY_DBG_ADDR, 0); + age_miibus_writereg(sc->age_dev, sc->age_phyaddr, + ATPHY_DBG_DATA, 0x024E); + } + +#undef ATPHY_DBG_ADDR +#undef ATPHY_DBG_DATA +#undef ATPHY_CDTC +#undef PHY_CDTC_ENB +#undef PHY_CDTC_POFF +#undef ATPHY_CDTS +#undef PHY_CDTS_STAT_OK +#undef PHY_CDTS_STAT_SHORT +#undef PHY_CDTS_STAT_OPEN +#undef PHY_CDTS_STAT_INVAL +#undef PHY_CDTS_STAT_MASK } static int @@ -559,8 +525,9 @@ age_attach(device_t dev) sc->age_rev = pci_get_revid(dev); sc->age_chip_rev = CSR_READ_4(sc, AGE_MASTER_CFG) >> MASTER_CHIP_REV_SHIFT; - if (1 || bootverbose) { - device_printf(dev, "PCI device revision : 0x%04x\n", sc->age_rev); + if (bootverbose) { + device_printf(dev, "PCI device revision : 0x%04x\n", + sc->age_rev); device_printf(dev, "Chip id/revision : 0x%04x\n", sc->age_chip_rev); } @@ -586,7 +553,7 @@ age_attach(device_t dev) /* Allocate IRQ resources. */ msixc = pci_msix_count(dev); msic = pci_msi_count(dev); - if (1 || bootverbose) { + if (bootverbose) { device_printf(dev, "MSIX count : %d\n", msixc); device_printf(dev, "MSI count : %d\n", msic); } @@ -633,7 +600,7 @@ age_attach(device_t dev) /* Max payload size. */ sc->age_dma_wr_burst = ((burst >> 5) & 0x07) << DMA_CFG_WR_BURST_SHIFT; - if (1 || bootverbose) { + if (bootverbose) { device_printf(dev, "Read request size : %d bytes.\n", 128 << ((burst >> 12) & 0x07)); device_printf(dev, "TLP payload size : %d bytes.\n", @@ -1390,7 +1357,7 @@ age_setwol(struct age_softc *sc) AGE_LOCK_ASSERT(sc); - if (pci_find_extcap(sc->age_dev, PCIY_PMG, &pmc) == 0) { + if (pci_find_extcap(sc->age_dev, PCIY_PMG, &pmc) != 0) { CSR_WRITE_4(sc, AGE_WOL_CFG, 0); /* * No PME capability, PHY power down. @@ -1448,7 +1415,7 @@ age_setwol(struct age_softc *sc) MII_BMCR, BMCR_RESET | BMCR_AUTOEN | BMCR_STARTNEG); DELAY(1000); if (aneg != 0) { - /* Poll link state until jme(4) get a 10/100 link. */ + /* Poll link state until age(4) get a 10/100 link. */ for (i = 0; i < MII_ANEGTICKS_GIGE; i++) { mii_pollstat(mii); if ((mii->mii_media_status & IFM_AVALID) != 0) { @@ -1463,8 +1430,11 @@ age_setwol(struct age_softc *sc) } } AGE_UNLOCK(sc); - //pause("agelnk", hz); +#ifdef __HAIKU__ DELAY(1); +#else + pause("agelnk", hz); +#endif AGE_LOCK(sc); } if (i == MII_ANEGTICKS_GIGE) @@ -1546,6 +1516,9 @@ age_resume(device_t dev) cmd &= ~0x0400; pci_write_config(sc->age_dev, PCIR_COMMAND, cmd, 2); } + AGE_UNLOCK(sc); + age_phy_reset(sc); + AGE_LOCK(sc); ifp = sc->age_ifp; if ((ifp->if_flags & IFF_UP) != 0) age_init_locked(sc); @@ -1579,12 +1552,13 @@ age_encap(struct age_softc *sc, struct mbuf **m_head) ip_off = poff = 0; if ((m->m_pkthdr.csum_flags & (AGE_CSUM_FEATURES | CSUM_TSO)) != 0) { /* - * L1 requires TCP/UDP payload offset in its Tx descriptor - * to perform hardware Tx checksum offload. Additionally - * TSO requires IP/TCP header size and modification of - * IP/TCP header in order to make TSO engine work. This - * kind of operation takes many CPU cycles so fast host - * CPU is needed to get smooth TSO performance. + * L1 requires offset of TCP/UDP payload in its Tx + * descriptor to perform hardware Tx checksum offload. + * Additionally, TSO requires IP/TCP header size and + * modification of IP/TCP header in order to make TSO + * engine work. This kind of operation takes many CPU + * cycles on FreeBSD so fast host CPU is needed to get + * smooth TSO performance. */ struct ether_header *eh; @@ -1633,11 +1607,18 @@ age_encap(struct age_softc *sc, struct mbuf **m_head) } tcp = (struct tcphdr *)(mtod(m, char *) + poff); /* - * L1 requires IP/TCP header size and offset as well - * as TCP pseudo checksum which all complicates TSO - * configuration. Hopefully this wouldn't be much - * burden on modern CPUs. - * Reset IP checksum and recompute TCP pseudo checksum. + * L1 requires IP/TCP header size and offset as + * well as TCP pseudo checksum which complicates + * TSO configuration. I guess this comes from the + * adherence to Microsoft NDIS Large Send + * specification which requires insertion of + * pseudo checksum by upper stack. The pseudo + * checksum that NDIS refers to doesn't include + * TCP payload length so age(4) should recompute + * the pseudo checksum here. Hopefully this wouldn't + * be much burden on modern CPUs. + * Reset IP checksum and recompute TCP pseudo + * checksum as NDIS specification said. */ ip->ip_sum = 0; if (poff + (tcp->th_off << 2) == m->m_pkthdr.len) @@ -2492,9 +2473,6 @@ age_rxintr(struct age_softc *sc, int rr_prod, int count) bus_dmamap_sync(sc->age_cdata.age_rr_ring_tag, sc->age_cdata.age_rr_ring_map, BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); - bus_dmamap_sync(sc->age_cdata.age_rx_ring_tag, - sc->age_cdata.age_rx_ring_map, - BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); for (prog = 0; rr_cons != rr_prod; prog++) { if (count <= 0) @@ -2527,9 +2505,6 @@ age_rxintr(struct age_softc *sc, int rr_prod, int count) sc->age_cdata.age_rr_cons = rr_cons; /* Sync descriptors. */ - bus_dmamap_sync(sc->age_cdata.age_rx_ring_tag, - sc->age_cdata.age_rx_ring_map, - BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); bus_dmamap_sync(sc->age_cdata.age_rr_ring_tag, sc->age_cdata.age_rr_ring_map, BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); @@ -2564,14 +2539,8 @@ age_reset(struct age_softc *sc) int i; CSR_WRITE_4(sc, AGE_MASTER_CFG, MASTER_RESET); - for (i = AGE_RESET_TIMEOUT; i > 0; i--) { - DELAY(1); - if ((CSR_READ_4(sc, AGE_MASTER_CFG) & MASTER_RESET) == 0) - break; - } - if (i == 0) - device_printf(sc->age_dev, "master reset timeout!\n"); - + CSR_READ_4(sc, AGE_MASTER_CFG); + DELAY(1000); for (i = AGE_RESET_TIMEOUT; i > 0; i--) { if ((reg = CSR_READ_4(sc, AGE_IDLE_STATUS)) == 0) break; @@ -2705,7 +2674,7 @@ age_init_locked(struct age_softc *sc) else reg |= MASTER_ITIMER_ENB; CSR_WRITE_4(sc, AGE_MASTER_CFG, reg); - if (1 || bootverbose) + if (bootverbose) device_printf(sc->age_dev, "interrupt moderation is %d us.\n", sc->age_int_mod); CSR_WRITE_2(sc, AGE_INTR_CLR_TIMER, AGE_USECS(1000)); @@ -3231,7 +3200,8 @@ sysctl_age_stats(SYSCTL_HANDLER_ARGS) return (error); sc = (struct age_softc *)arg1; - stats = &sc->age_stat; /* + stats = &sc->age_stat; +#if 0 printf("%s statistics:\n", device_get_nameunit(sc->age_dev)); printf("Transmit good frames : %ju\n", (uintmax_t)stats->tx_frames); @@ -3331,7 +3301,8 @@ sysctl_age_stats(SYSCTL_HANDLER_ARGS) printf("Receive frames with alignment errors : %u\n", stats->rx_alignerrs); printf("Receive frames dropped due to address filtering : %ju\n", - (uint64_t)stats->rx_pkts_filtered);*/ + (uint64_t)stats->rx_pkts_filtered); +#endif return (error); } diff --git a/src/add-ons/kernel/drivers/network/attansic_l1/dev/age/if_agereg.h b/src/add-ons/kernel/drivers/network/attansic_l1/dev/age/if_agereg.h index 725237ed98..5ff2c6aa17 100644 --- a/src/add-ons/kernel/drivers/network/attansic_l1/dev/age/if_agereg.h +++ b/src/add-ons/kernel/drivers/network/attansic_l1/dev/age/if_agereg.h @@ -24,7 +24,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD + * $FreeBSD: src/sys/dev/age/if_agereg.h,v 1.1 2008/05/19 01:39:59 yongari Exp $ */ #ifndef _IF_AGEREG_H @@ -91,6 +91,9 @@ #define AGE_SPI_OP_READ 0x217 /* 8bits */ #define AGE_TWSI_CTRL 0x218 +#define TWSI_CTRL_SW_LD_START 0x00000800 +#define TWSI_CTRL_HW_LD_START 0x00001000 +#define TWSI_CTRL_LD_EXIST 0x00400000 #define AGE_DEV_MISC_CTRL 0x21C @@ -483,10 +486,6 @@ (INTR_SMB | INTR_DMA_RD_TO_RST | INTR_DMA_WR_TO_RST | \ INTR_CMB_TX | INTR_CMB_RX) -#define AGE_RD_RRD_IDX 0x1800 - -#define AGE_TPD_IDX 0x1804 - /* Statistics counters collected by the MAC. */ struct smb { /* Rx stats. */