Esempio n. 1
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);
}
Esempio n. 2
0
int
smsc_wait_for_bits(struct smsc_softc *sc, uint32_t reg, uint32_t bits)
{
	uint32_t val;
	int err, i;

	for (i = 0; i < 100; i++) {
		if ((err = smsc_read_reg(sc, reg, &val)) != 0)
			return (err);
		if (!(val & bits))
			return (0);
		DELAY(5);
	}

	return (1);
}
Esempio n. 3
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);
}
Esempio n. 4
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);
}
Esempio n. 5
0
void
smsc_attach(struct device *parent, struct device *self, void *aux)
{
	struct smsc_softc *sc = (struct smsc_softc *)self;
	struct usb_attach_arg *uaa = aux;
	struct usbd_device *dev = uaa->device;
	usb_interface_descriptor_t *id;
	usb_endpoint_descriptor_t *ed;
	struct mii_data *mii;
	struct ifnet *ifp;
	int err, s, i;
	uint32_t mac_h, mac_l;

	sc->sc_udev = dev;

	err = usbd_set_config_no(dev, SMSC_CONFIG_INDEX, 1);

	/* Setup the endpoints for the SMSC LAN95xx device(s) */
	usb_init_task(&sc->sc_tick_task, smsc_tick_task, sc,
	    USB_TASK_TYPE_GENERIC);
	rw_init(&sc->sc_mii_lock, "smscmii");
	usb_init_task(&sc->sc_stop_task, (void (*)(void *))smsc_stop, sc,
	    USB_TASK_TYPE_GENERIC);

	err = usbd_device2interface_handle(dev, SMSC_IFACE_IDX, &sc->sc_iface);
	if (err) {
		printf("%s: getting interface handle failed\n",
		    sc->sc_dev.dv_xname);
		return;
	}

	id = usbd_get_interface_descriptor(sc->sc_iface);

	if (sc->sc_udev->speed >= USB_SPEED_HIGH)
		sc->sc_bufsz = SMSC_MAX_BUFSZ;
	else
		sc->sc_bufsz = SMSC_MIN_BUFSZ;

	/* Find endpoints. */
	for (i = 0; i < id->bNumEndpoints; i++) {
		ed = usbd_interface2endpoint_descriptor(sc->sc_iface, i);
		if (!ed) {
			printf("%s: couldn't get ep %d\n",
			    sc->sc_dev.dv_xname, i);
			return;
		}
		if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
		    UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) {
			sc->sc_ed[SMSC_ENDPT_RX] = ed->bEndpointAddress;
		} else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT &&
			   UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) {
			sc->sc_ed[SMSC_ENDPT_TX] = ed->bEndpointAddress;
		} else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
			   UE_GET_XFERTYPE(ed->bmAttributes) == UE_INTERRUPT) {
			sc->sc_ed[SMSC_ENDPT_INTR] = ed->bEndpointAddress;
		}
	}

	s = splnet();

	ifp = &sc->sc_ac.ac_if;
	ifp->if_softc = sc;
	strlcpy(ifp->if_xname, sc->sc_dev.dv_xname, IFNAMSIZ);
	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
	ifp->if_ioctl = smsc_ioctl;
	ifp->if_start = smsc_start;
	ifp->if_capabilities = IFCAP_VLAN_MTU;

	/* Setup some of the basics */
	sc->sc_phyno = 1;

	/*
	 * Attempt to get the mac address, if an EEPROM is not attached this
	 * will just return FF:FF:FF:FF:FF:FF, so in such cases we invent a MAC
	 * address based on urandom.
	 */
	memset(sc->sc_ac.ac_enaddr, 0xff, ETHER_ADDR_LEN);
	
	/* Check if there is already a MAC address in the register */
	if ((smsc_read_reg(sc, SMSC_MAC_ADDRL, &mac_l) == 0) &&
	    (smsc_read_reg(sc, SMSC_MAC_ADDRH, &mac_h) == 0)) {
		sc->sc_ac.ac_enaddr[5] = (uint8_t)((mac_h >> 8) & 0xff);
		sc->sc_ac.ac_enaddr[4] = (uint8_t)((mac_h) & 0xff);
		sc->sc_ac.ac_enaddr[3] = (uint8_t)((mac_l >> 24) & 0xff);
		sc->sc_ac.ac_enaddr[2] = (uint8_t)((mac_l >> 16) & 0xff);
		sc->sc_ac.ac_enaddr[1] = (uint8_t)((mac_l >> 8) & 0xff);
		sc->sc_ac.ac_enaddr[0] = (uint8_t)((mac_l) & 0xff);
	}
Esempio n. 6
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);
}
Esempio n. 7
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);
}
Esempio n. 8
0
void
smsc_attach(device_t parent, device_t self, void *aux)
{
    struct smsc_softc *sc = device_private(self);
    struct usb_attach_arg *uaa = aux;
    usbd_device_handle dev = uaa->device;
    usb_interface_descriptor_t *id;
    usb_endpoint_descriptor_t *ed;
    char *devinfop;
    struct mii_data *mii;
    struct ifnet *ifp;
    int err, s, i;
    uint32_t mac_h, mac_l;

    sc->sc_dev = self;
    sc->sc_udev = dev;

    aprint_naive("\n");
    aprint_normal("\n");

    devinfop = usbd_devinfo_alloc(sc->sc_udev, 0);
    aprint_normal_dev(self, "%s\n", devinfop);
    usbd_devinfo_free(devinfop);

    err = usbd_set_config_no(dev, SMSC_CONFIG_INDEX, 1);
    if (err) {
        aprint_error_dev(self, "failed to set configuration"
                         ", err=%s\n", usbd_errstr(err));
        return;
    }
    /* Setup the endpoints for the SMSC LAN95xx device(s) */
    usb_init_task(&sc->sc_tick_task, smsc_tick_task, sc, 0);
    usb_init_task(&sc->sc_stop_task, (void (*)(void *))smsc_stop, sc, 0);
    mutex_init(&sc->sc_mii_lock, MUTEX_DEFAULT, IPL_NONE);

    err = usbd_device2interface_handle(dev, SMSC_IFACE_IDX, &sc->sc_iface);
    if (err) {
        aprint_error_dev(self, "getting interface handle failed\n");
        return;
    }

    id = usbd_get_interface_descriptor(sc->sc_iface);

    if (sc->sc_udev->speed >= USB_SPEED_HIGH)
        sc->sc_bufsz = SMSC_MAX_BUFSZ;
    else
        sc->sc_bufsz = SMSC_MIN_BUFSZ;

    /* Find endpoints. */
    for (i = 0; i < id->bNumEndpoints; i++) {
        ed = usbd_interface2endpoint_descriptor(sc->sc_iface, i);
        if (!ed) {
            aprint_error_dev(self, "couldn't get ep %d\n", i);
            return;
        }
        if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
                UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) {
            sc->sc_ed[SMSC_ENDPT_RX] = ed->bEndpointAddress;
        } else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT &&
                   UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) {
            sc->sc_ed[SMSC_ENDPT_TX] = ed->bEndpointAddress;
        } else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
                   UE_GET_XFERTYPE(ed->bmAttributes) == UE_INTERRUPT) {
            sc->sc_ed[SMSC_ENDPT_INTR] = ed->bEndpointAddress;
        }
    }

    s = splnet();

    ifp = &sc->sc_ec.ec_if;
    ifp->if_softc = sc;
    strlcpy(ifp->if_xname, device_xname(sc->sc_dev), IFNAMSIZ);
    ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
    ifp->if_init = smsc_init;
    ifp->if_ioctl = smsc_ioctl;
    ifp->if_start = smsc_start;
    ifp->if_stop = smsc_stop;

#ifdef notyet
    /*
     * We can do TCPv4, and UDPv4 checksums in hardware.
     */
    ifp->if_capabilities |=
        /*IFCAP_CSUM_TCPv4_Tx |*/ IFCAP_CSUM_TCPv4_Rx |
        /*IFCAP_CSUM_UDPv4_Tx |*/ IFCAP_CSUM_UDPv4_Rx;
#endif

    sc->sc_ec.ec_capabilities = ETHERCAP_VLAN_MTU;

    /* Setup some of the basics */
    sc->sc_phyno = 1;

    /*
     * Attempt to get the mac address, if an EEPROM is not attached this
     * will just return FF:FF:FF:FF:FF:FF, so in such cases we invent a MAC
     * address based on urandom.
     */
    memset(sc->sc_enaddr, 0xff, ETHER_ADDR_LEN);

    prop_dictionary_t dict = device_properties(self);
    prop_data_t eaprop = prop_dictionary_get(dict, "mac-address");

    if (eaprop != NULL) {
        KASSERT(prop_object_type(eaprop) == PROP_TYPE_DATA);
        KASSERT(prop_data_size(eaprop) == ETHER_ADDR_LEN);
        memcpy(sc->sc_enaddr, prop_data_data_nocopy(eaprop),
               ETHER_ADDR_LEN);
    } else
        /* Check if there is already a MAC address in the register */
        if ((smsc_read_reg(sc, SMSC_MAC_ADDRL, &mac_l) == 0) &&
                (smsc_read_reg(sc, SMSC_MAC_ADDRH, &mac_h) == 0)) {
            sc->sc_enaddr[5] = (uint8_t)((mac_h >> 8) & 0xff);
            sc->sc_enaddr[4] = (uint8_t)((mac_h) & 0xff);
            sc->sc_enaddr[3] = (uint8_t)((mac_l >> 24) & 0xff);
            sc->sc_enaddr[2] = (uint8_t)((mac_l >> 16) & 0xff);
            sc->sc_enaddr[1] = (uint8_t)((mac_l >> 8) & 0xff);
            sc->sc_enaddr[0] = (uint8_t)((mac_l) & 0xff);
        }