Exemplo n.º 1
0
void
smsc_miibus_writereg(device_t dev, int phy, int reg, int val)
{
    struct smsc_softc *sc = device_private(dev);
    uint32_t addr;

    if (sc->sc_phyno != phy)
        return;

    smsc_lock_mii(sc);
    if (smsc_wait_for_bits(sc, SMSC_MII_ADDR, SMSC_MII_BUSY) != 0) {
        smsc_warn_printf(sc, "MII is busy\n");
        smsc_unlock_mii(sc);
        return;
    }

    smsc_write_reg(sc, SMSC_MII_DATA, val);

    addr = (phy << 11) | (reg << 6) | SMSC_MII_WRITE;
    smsc_write_reg(sc, SMSC_MII_ADDR, addr);
    smsc_unlock_mii(sc);

    if (smsc_wait_for_bits(sc, SMSC_MII_ADDR, SMSC_MII_BUSY) != 0)
        smsc_warn_printf(sc, "MII write timeout\n");
}
Exemplo n.º 2
0
void
smsc_setmulti(struct smsc_softc *sc)
{
    struct ifnet		*ifp = &sc->sc_ec.ec_if;
    struct ether_multi	*enm;
    struct ether_multistep	 step;
    uint32_t		 hashtbl[2] = { 0, 0 };
    uint32_t		 hash;

    if (sc->sc_dying)
        return;

    if (ifp->if_flags & (IFF_ALLMULTI | IFF_PROMISC)) {
allmulti:
        smsc_dbg_printf(sc, "receive all multicast enabled\n");
        sc->sc_mac_csr |= SMSC_MAC_CSR_MCPAS;
        sc->sc_mac_csr &= ~SMSC_MAC_CSR_HPFILT;
        smsc_write_reg(sc, SMSC_MAC_CSR, sc->sc_mac_csr);
        return;
    } else {
        sc->sc_mac_csr |= SMSC_MAC_CSR_HPFILT;
        sc->sc_mac_csr &= ~(SMSC_MAC_CSR_PRMS | SMSC_MAC_CSR_MCPAS);
    }

    ETHER_FIRST_MULTI(step, &sc->sc_ec, enm);
    while (enm != NULL) {
        if (memcmp(enm->enm_addrlo, enm->enm_addrhi,
                   ETHER_ADDR_LEN) != 0)
            goto allmulti;

        hash = smsc_hash(enm->enm_addrlo);
        hashtbl[hash >> 5] |= 1 << (hash & 0x1F);
        ETHER_NEXT_MULTI(step, enm);
    }

    /* Debug */
    if (sc->sc_mac_csr & SMSC_MAC_CSR_HPFILT) {
        smsc_dbg_printf(sc, "receive select group of macs\n");
    } else {
        smsc_dbg_printf(sc, "receive own packets only\n");
    }

    /* Write the hash table and mac control registers */
    ifp->if_flags &= ~IFF_ALLMULTI;
    smsc_write_reg(sc, SMSC_HASHH, hashtbl[1]);
    smsc_write_reg(sc, SMSC_HASHL, hashtbl[0]);
    smsc_write_reg(sc, SMSC_MAC_CSR, sc->sc_mac_csr);
}
Exemplo n.º 3
0
int
smsc_miibus_readreg(struct device *dev, int phy, int reg)
{
	struct smsc_softc *sc = (struct smsc_softc *)dev;
	uint32_t addr;
	uint32_t val = 0;

	smsc_lock_mii(sc);
	if (smsc_wait_for_bits(sc, SMSC_MII_ADDR, SMSC_MII_BUSY) != 0) {
		smsc_warn_printf(sc, "MII is busy\n");
		goto done;
	}

	addr = (phy << 11) | (reg << 6) | SMSC_MII_READ;
	smsc_write_reg(sc, SMSC_MII_ADDR, addr);

	if (smsc_wait_for_bits(sc, SMSC_MII_ADDR, SMSC_MII_BUSY) != 0)
		smsc_warn_printf(sc, "MII read timeout\n");

	smsc_read_reg(sc, SMSC_MII_DATA, &val);
	smsc_unlock_mii(sc);
	
done:
	return (val & 0xFFFF);
}
Exemplo n.º 4
0
void
smsc_iff(struct smsc_softc *sc)
{
	struct ifnet		*ifp = &sc->sc_ac.ac_if;
	struct arpcom		*ac = &sc->sc_ac;
	struct ether_multi	*enm;
	struct ether_multistep	 step;
	uint32_t		 hashtbl[2] = { 0, 0 };
	uint32_t		 hash;

	if (usbd_is_dying(sc->sc_udev))
		return;

	sc->sc_mac_csr &= ~(SMSC_MAC_CSR_HPFILT | SMSC_MAC_CSR_MCPAS |
	    SMSC_MAC_CSR_PRMS);
	ifp->if_flags &= ~IFF_ALLMULTI;

	if (ifp->if_flags & IFF_PROMISC || ac->ac_multirangecnt > 0) {
		ifp->if_flags |= IFF_ALLMULTI;
		sc->sc_mac_csr |= SMSC_MAC_CSR_MCPAS;
		if (ifp->if_flags & IFF_PROMISC)
			sc->sc_mac_csr |= SMSC_MAC_CSR_PRMS;
	} else {
		sc->sc_mac_csr |= SMSC_MAC_CSR_HPFILT;

		ETHER_FIRST_MULTI(step, ac, enm);
		while (enm != NULL) {
			hash = smsc_hash(enm->enm_addrlo);

			hashtbl[hash >> 5] |= 1 << (hash & 0x1F);

			ETHER_NEXT_MULTI(step, enm);
		}
	}

	/* Debug */
	if (sc->sc_mac_csr & SMSC_MAC_CSR_MCPAS)
		smsc_dbg_printf(sc, "receive all multicast enabled\n");
	else if (sc->sc_mac_csr & SMSC_MAC_CSR_HPFILT)
		smsc_dbg_printf(sc, "receive select group of macs\n");

	/* Write the hash table and mac control registers */
	smsc_write_reg(sc, SMSC_HASHH, hashtbl[1]);
	smsc_write_reg(sc, SMSC_HASHL, hashtbl[0]);
	smsc_write_reg(sc, SMSC_MAC_CSR, sc->sc_mac_csr);
}
Exemplo n.º 5
0
int
smsc_setmacaddress(struct smsc_softc *sc, const uint8_t *addr)
{
	int err;
	uint32_t val;

	smsc_dbg_printf(sc, "setting mac address to "
	    "%02x:%02x:%02x:%02x:%02x:%02x\n",
	    addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]);

	val = (addr[3] << 24) | (addr[2] << 16) | (addr[1] << 8) | addr[0];
	if ((err = smsc_write_reg(sc, SMSC_MAC_ADDRL, val)) != 0)
		goto done;
		
	val = (addr[5] << 8) | addr[4];
	err = smsc_write_reg(sc, SMSC_MAC_ADDRH, val);
	
done:
	return (err);
}
Exemplo n.º 6
0
int
smsc_sethwcsum(struct smsc_softc *sc)
{
    struct ifnet *ifp = &sc->sc_ec.ec_if;
    uint32_t val;
    int err;

    if (!ifp)
        return EIO;

    err = smsc_read_reg(sc, SMSC_COE_CTRL, &val);
    if (err != 0) {
        smsc_warn_printf(sc, "failed to read SMSC_COE_CTRL (err=%d)\n",
                         err);
        return (err);
    }

    /* Enable/disable the Rx checksum */
    if (ifp->if_capenable & (IFCAP_CSUM_TCPv4_Rx|IFCAP_CSUM_UDPv4_Rx))
        val |= (SMSC_COE_CTRL_RX_EN | SMSC_COE_CTRL_RX_MODE);
    else
        val &= ~(SMSC_COE_CTRL_RX_EN | SMSC_COE_CTRL_RX_MODE);

    /* Enable/disable the Tx checksum (currently not supported) */
    if (ifp->if_capenable & (IFCAP_CSUM_TCPv4_Tx|IFCAP_CSUM_UDPv4_Tx))
        val |= SMSC_COE_CTRL_TX_EN;
    else
        val &= ~SMSC_COE_CTRL_TX_EN;

    sc->sc_coe_ctrl = val;

    err = smsc_write_reg(sc, SMSC_COE_CTRL, val);
    if (err != 0) {
        smsc_warn_printf(sc, "failed to write SMSC_COE_CTRL (err=%d)\n",
                         err);
        return (err);
    }

    return (0);
}
Exemplo n.º 7
0
int
smsc_sethwcsum(struct smsc_softc *sc)
{
	struct ifnet *ifp = &sc->sc_ac.ac_if;
	uint32_t val;
	int err;

	if (!ifp)
		return (-EIO);

	err = smsc_read_reg(sc, SMSC_COE_CTRL, &val);
	if (err != 0) {
		smsc_warn_printf(sc, "failed to read SMSC_COE_CTRL (err=%d)\n",
		    err);
		return (err);
	}

	/* Enable/disable the Rx checksum */
	if (ifp->if_capabilities & IFCAP_CSUM_IPv4)
		val |= SMSC_COE_CTRL_RX_EN;
	else
		val &= ~SMSC_COE_CTRL_RX_EN;

	/* Enable/disable the Tx checksum (currently not supported) */
	if (ifp->if_capabilities & IFCAP_CSUM_IPv4)
		val |= SMSC_COE_CTRL_TX_EN;
	else
		val &= ~SMSC_COE_CTRL_TX_EN;

	err = smsc_write_reg(sc, SMSC_COE_CTRL, val);
	if (err != 0) {
		smsc_warn_printf(sc, "failed to write SMSC_COE_CTRL (err=%d)\n",
		    err);
		return (err);
	}

	return (0);
}
Exemplo n.º 8
0
int
smsc_chip_init(struct smsc_softc *sc)
{
	int err;
	uint32_t reg_val;
	int burst_cap;

	/* Enter H/W config mode */
	smsc_write_reg(sc, SMSC_HW_CFG, SMSC_HW_CFG_LRST);

	if ((err = smsc_wait_for_bits(sc, SMSC_HW_CFG,
	    SMSC_HW_CFG_LRST)) != 0) {
		smsc_warn_printf(sc, "timed-out waiting for reset to "
		    "complete\n");
		goto init_failed;
	}

	/* Reset the PHY */
	smsc_write_reg(sc, SMSC_PM_CTRL, SMSC_PM_CTRL_PHY_RST);

	if ((err = smsc_wait_for_bits(sc, SMSC_PM_CTRL,
	    SMSC_PM_CTRL_PHY_RST) != 0)) {
		smsc_warn_printf(sc, "timed-out waiting for phy reset to "
		    "complete\n");
		goto init_failed;
	}
	usbd_delay_ms(sc->sc_udev, 40);

	/* Set the mac address */
	if ((err = smsc_setmacaddress(sc, sc->sc_ac.ac_enaddr)) != 0) {
		smsc_warn_printf(sc, "failed to set the MAC address\n");
		goto init_failed;
	}

	/*
	 * Don't know what the HW_CFG_BIR bit is, but following the reset
	 * sequence as used in the Linux driver.
	 */
	if ((err = smsc_read_reg(sc, SMSC_HW_CFG, &reg_val)) != 0) {
		smsc_warn_printf(sc, "failed to read HW_CFG: %d\n", err);
		goto init_failed;
	}
	reg_val |= SMSC_HW_CFG_BIR;
	smsc_write_reg(sc, SMSC_HW_CFG, reg_val);

	/*
	 * There is a so called 'turbo mode' that the linux driver supports, it
	 * seems to allow you to jam multiple frames per Rx transaction.
	 * By default this driver supports that and therefore allows multiple
	 * frames per URB.
	 *
	 * The xfer buffer size needs to reflect this as well, therefore based
	 * on the calculations in the Linux driver the RX bufsize is set to
	 * 18944,
	 *     bufsz = (16 * 1024 + 5 * 512)
	 *
	 * Burst capability is the number of URBs that can be in a burst of
	 * data/ethernet frames.
	 */
#ifdef SMSC_TURBO
	if (sc->sc_udev->speed == USB_SPEED_HIGH)
		burst_cap = 37;
	else
		burst_cap = 128;
#else
	burst_cap = 0;
#endif

	smsc_write_reg(sc, SMSC_BURST_CAP, burst_cap);

	/* Set the default bulk in delay (magic value from Linux driver) */
	smsc_write_reg(sc, SMSC_BULK_IN_DLY, 0x00002000);



	/*
	 * Initialise the RX interface
	 */
	if ((err = smsc_read_reg(sc, SMSC_HW_CFG, &reg_val)) < 0) {
		smsc_warn_printf(sc, "failed to read HW_CFG: (err = %d)\n",
		    err);
		goto init_failed;
	}

	/*
	 * The following setings are used for 'turbo mode', a.k.a multiple
	 * frames per Rx transaction (again info taken form Linux driver).
	 */
#ifdef SMSC_TURBO
	reg_val |= (SMSC_HW_CFG_MEF | SMSC_HW_CFG_BCE);
#endif

	smsc_write_reg(sc, SMSC_HW_CFG, reg_val);

	/* Clear the status register ? */
	smsc_write_reg(sc, SMSC_INTR_STATUS, 0xffffffff);

	/* Read and display the revision register */
	if ((err = smsc_read_reg(sc, SMSC_ID_REV, &sc->sc_rev_id)) < 0) {
		smsc_warn_printf(sc, "failed to read ID_REV (err = %d)\n", err);
		goto init_failed;
	}

	/* GPIO/LED setup */
	reg_val = SMSC_LED_GPIO_CFG_SPD_LED | SMSC_LED_GPIO_CFG_LNK_LED | 
	          SMSC_LED_GPIO_CFG_FDX_LED;
	smsc_write_reg(sc, SMSC_LED_GPIO_CFG, reg_val);

	/*
	 * Initialise the TX interface
	 */
	smsc_write_reg(sc, SMSC_FLOW, 0);

	smsc_write_reg(sc, SMSC_AFC_CFG, AFC_CFG_DEFAULT);

	/* Read the current MAC configuration */
	if ((err = smsc_read_reg(sc, SMSC_MAC_CSR, &sc->sc_mac_csr)) < 0) {
		smsc_warn_printf(sc, "failed to read MAC_CSR (err=%d)\n", err);
		goto init_failed;
	}
	
	/* Vlan */
	smsc_write_reg(sc, SMSC_VLAN1, (uint32_t)ETHERTYPE_VLAN);

	/*
	 * Start TX
	 */
	sc->sc_mac_csr |= SMSC_MAC_CSR_TXEN;
	smsc_write_reg(sc, SMSC_MAC_CSR, sc->sc_mac_csr);
	smsc_write_reg(sc, SMSC_TX_CFG, SMSC_TX_CFG_ON);

	/*
	 * Start RX
	 */
	sc->sc_mac_csr |= SMSC_MAC_CSR_RXEN;
	smsc_write_reg(sc, SMSC_MAC_CSR, sc->sc_mac_csr);

	return (0);
	
init_failed:
	smsc_err_printf(sc, "smsc_chip_init failed (err=%d)\n", err);
	return (err);
}
Exemplo n.º 9
0
void
smsc_miibus_statchg(struct device *dev)
{
	struct smsc_softc *sc = (struct smsc_softc *)dev;
	struct mii_data *mii = &sc->sc_mii;
	struct ifnet *ifp = &sc->sc_ac.ac_if;
	int err;
	uint32_t flow;
	uint32_t afc_cfg;

	if (mii == NULL || ifp == NULL ||
	    (ifp->if_flags & IFF_RUNNING) == 0)
		return;

	/* Use the MII status to determine link status */
	sc->sc_flags &= ~SMSC_FLAG_LINK;
	if ((mii->mii_media_status & (IFM_ACTIVE | IFM_AVALID)) ==
	    (IFM_ACTIVE | IFM_AVALID)) {
		switch (IFM_SUBTYPE(mii->mii_media_active)) {
			case IFM_10_T:
			case IFM_100_TX:
				sc->sc_flags |= SMSC_FLAG_LINK;
				break;
			case IFM_1000_T:
				/* Gigabit ethernet not supported by chipset */
				break;
			default:
				break;
		}
	}

	/* Lost link, do nothing. */
	if ((sc->sc_flags & SMSC_FLAG_LINK) == 0) {
		smsc_dbg_printf(sc, "link flag not set\n");
		return;
	}
	
	err = smsc_read_reg(sc, SMSC_AFC_CFG, &afc_cfg);
	if (err) {
		smsc_warn_printf(sc, "failed to read initial AFC_CFG, "
		    "error %d\n", err);
		return;
	}
	
	/* Enable/disable full duplex operation and TX/RX pause */
	if ((IFM_OPTIONS(mii->mii_media_active) & IFM_FDX) != 0) {
		smsc_dbg_printf(sc, "full duplex operation\n");
		sc->sc_mac_csr &= ~SMSC_MAC_CSR_RCVOWN;
		sc->sc_mac_csr |= SMSC_MAC_CSR_FDPX;

		if ((IFM_OPTIONS(mii->mii_media_active) & IFM_ETH_RXPAUSE) != 0)
			flow = 0xffff0002;
		else
			flow = 0;
			
		if ((IFM_OPTIONS(mii->mii_media_active) & IFM_ETH_TXPAUSE) != 0)
			afc_cfg |= 0xf;
		else
			afc_cfg &= ~0xf;
		
	} else {
		smsc_dbg_printf(sc, "half duplex operation\n");
		sc->sc_mac_csr &= ~SMSC_MAC_CSR_FDPX;
		sc->sc_mac_csr |= SMSC_MAC_CSR_RCVOWN;
		
		flow = 0;
		afc_cfg |= 0xf;
	}

	err = smsc_write_reg(sc, SMSC_MAC_CSR, sc->sc_mac_csr);
	err += smsc_write_reg(sc, SMSC_FLOW, flow);
	err += smsc_write_reg(sc, SMSC_AFC_CFG, afc_cfg);
	if (err)
		smsc_warn_printf(sc, "media change failed, error %d\n", err);
}
Exemplo n.º 10
0
int
smsc_ioctl(struct ifnet *ifp, u_long cmd, void *data)
{
    struct smsc_softc	*sc = ifp->if_softc;
    struct ifreq /*const*/	*ifr = data;
    int			s, error = 0;

    if (sc->sc_dying)
        return EIO;

    s = splnet();

    switch(cmd) {
    case SIOCSIFFLAGS:
        if ((error = ifioctl_common(ifp, cmd, data)) != 0)
            break;

        switch (ifp->if_flags & (IFF_UP | IFF_RUNNING)) {
        case IFF_RUNNING:
            smsc_stop(ifp, 1);
            break;
        case IFF_UP:
            smsc_init(ifp);
            break;
        case IFF_UP | IFF_RUNNING:
            if (ifp->if_flags & IFF_PROMISC &&
                    !(sc->sc_if_flags & IFF_PROMISC)) {
                sc->sc_mac_csr |= SMSC_MAC_CSR_PRMS;
                smsc_write_reg(sc, SMSC_MAC_CSR,
                               sc->sc_mac_csr);
                smsc_setmulti(sc);
            } else if (!(ifp->if_flags & IFF_PROMISC) &&
                       sc->sc_if_flags & IFF_PROMISC) {
                sc->sc_mac_csr &= ~SMSC_MAC_CSR_PRMS;
                smsc_write_reg(sc, SMSC_MAC_CSR,
                               sc->sc_mac_csr);
                smsc_setmulti(sc);
            } else {
                smsc_init(ifp);
            }
            break;
        }
        sc->sc_if_flags = ifp->if_flags;
        break;

    case SIOCGIFMEDIA:
    case SIOCSIFMEDIA:
        error = ifmedia_ioctl(ifp, ifr, &sc->sc_mii.mii_media, cmd);
        break;

    default:
        if ((error = ether_ioctl(ifp, cmd, data)) != ENETRESET)
            break;

        error = 0;

        if (cmd == SIOCADDMULTI || cmd == SIOCDELMULTI)
            smsc_setmulti(sc);

    }
    splx(s);

    return error;
}