[PATCH] usb: smsc95xx: Add initial implementation of smsc95xx driver

486 views
Skip to first unread message

Wayne Kim

unread,
Jun 20, 2017, 1:01:09 AM6/20/17
to xvisor...@googlegroups.com, Wayne Kim
Add initial implementation of smsc95xx usb ethernet driver
on raspberry pi 3 device.

-- [Patchset 1] May/16/2017
Brought up the device but not actual send/recv packets.
You can see the device named sms0 by typing,
XVisor# net ports

-- [Patchset 2] May/17/2017
Now you can use network device through sms0 from the guest.
To test type below command with CONFIG_NET_STACK_LWIP config.
Make sure the device should under a dhcp-supported router.
XVisor# ipconfig dhcp
XVisor# ping 8.8.8.8
---
.../board/generic/dts/bcm2837/bcm2837-rpi-3-b.dtsi | 5 +
arch/arm/board/generic/dts/bcm2837/bcm2837.dtsi | 4 +-
.../generic/dts/bcm2837/bcm283x-rpi-smsc9514.dtsi | 20 +
arch/arm/configs/generic-v8-defconfig | 2 +
drivers/usb/devices/smsc95xx.c | 1083 ++++++++++++++++++++
drivers/usb/devices/smsc95xx.h | 364 +++++++
drivers/usb/storage/objects.mk | 2 +-
drivers/usb/storage/openconf.cfg | 12 +
emulators/net/virtio_net.c | 5 +
9 files changed, 1495 insertions(+), 2 deletions(-)
create mode 100644 arch/arm/board/generic/dts/bcm2837/bcm283x-rpi-smsc9514.dtsi
create mode 100644 drivers/usb/devices/smsc95xx.c
create mode 100644 drivers/usb/devices/smsc95xx.h

diff --git a/arch/arm/board/generic/dts/bcm2837/bcm2837-rpi-3-b.dtsi b/arch/arm/board/generic/dts/bcm2837/bcm2837-rpi-3-b.dtsi
index 4d37c11..b46b923 100644
--- a/arch/arm/board/generic/dts/bcm2837/bcm2837-rpi-3-b.dtsi
+++ b/arch/arm/board/generic/dts/bcm2837/bcm2837-rpi-3-b.dtsi
@@ -1,5 +1,6 @@

/include/ "bcm2837.dtsi"
+/include/ "bcm283x-rpi-smsc9514.dtsi"

/ {
compatible = "raspberrypi,3-model-b", "brcm,bcm2837";
@@ -27,3 +28,7 @@
status = "okay";
bus-width = <4>;
};
+
+&usb {
+ status = "okay";
+};
diff --git a/arch/arm/board/generic/dts/bcm2837/bcm2837.dtsi b/arch/arm/board/generic/dts/bcm2837/bcm2837.dtsi
index 33560ee..b04d976 100644
--- a/arch/arm/board/generic/dts/bcm2837/bcm2837.dtsi
+++ b/arch/arm/board/generic/dts/bcm2837/bcm2837.dtsi
@@ -161,7 +161,9 @@
usb: usb@3f980000 {
compatible = "brcm,bcm2836-usb";
reg = <0x3f980000 0x10000>;
- interrupts = <2 9>;
+ interrupts = <1 9>;
+ #address-cells = <1>;
+ #size-cells = <0>;
status = "disabled";
};
};
diff --git a/arch/arm/board/generic/dts/bcm2837/bcm283x-rpi-smsc9514.dtsi b/arch/arm/board/generic/dts/bcm2837/bcm283x-rpi-smsc9514.dtsi
new file mode 100644
index 0000000..cd934c0
--- /dev/null
+++ b/arch/arm/board/generic/dts/bcm2837/bcm283x-rpi-smsc9514.dtsi
@@ -0,0 +1,20 @@
+/ {
+ aliases {
+ ethernet = &ethernet;
+ };
+};
+
+&usb {
+ usb1@1 {
+ compatible = "usb424,9514";
+ reg = <1>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ ethernet: usbether@1 {
+ compatible = "usb424,ec00";
+ reg = <1>;
+ switch = "br0";
+ };
+ };
+};
diff --git a/arch/arm/configs/generic-v8-defconfig b/arch/arm/configs/generic-v8-defconfig
index 1e80f66..d38fe5a 100644
--- a/arch/arm/configs/generic-v8-defconfig
+++ b/arch/arm/configs/generic-v8-defconfig
@@ -48,6 +48,8 @@ CONFIG_MMC_SDHCI=y
CONFIG_MMC_SDHCI_BCM2835=y
CONFIG_USB=y
CONFIG_USB_STORAGE=y
+CONFIG_USB_HOST_ETHER=y
+CONFIG_USB_ETHER_SMSC95XX=y
CONFIG_USB_DWC2_HCD=y
CONFIG_INPUT=y
CONFIG_FB=y
diff --git a/drivers/usb/devices/smsc95xx.c b/drivers/usb/devices/smsc95xx.c
new file mode 100644
index 0000000..fdf96ef
--- /dev/null
+++ b/drivers/usb/devices/smsc95xx.c
@@ -0,0 +1,1083 @@
+/*
+ * Copyright (c) 2015 Google, Inc
+ * Copyright (c) 2011 The Chromium OS Authors.
+ * Copyright (C) 2009 NVIDIA, Corporation
+ * Copyright (C) 2007-2008 SMSC (Steve Glendinning)
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <vmm_modules.h>
+#include <vmm_stdio.h>
+#include <vmm_wallclock.h>
+#include <vmm_delay.h>
+#include <net/vmm_protocol.h>
+#include <libs/unaligned.h>
+
+#include <arch_cache.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/spinlock.h>
+#include <linux/mii.h>
+#include <linux/workqueue.h>
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include <linux/of_net.h>
+
+#include <asm/io.h>
+#include <asm/byteorder.h>
+#include <linux/jiffies.h>
+#include <linux/io.h>
+#include <linux/mii.h>
+#include <drv/usb.h>
+#include <smsc95xx.h>
+
+#define MODULE_DESC "smsc95xx ethernet Driver"
+#define MODULE_AUTHOR "Wayne Kim"
+#define MODULE_LICENSE "GPL"
+#define MODULE_IPRIORITY (USB_CORE_IPRIORITY + 2)
+#define MODULE_INIT smsc95xx_eth_init
+#define MODULE_EXIT smsc95xx_eth_exit
+
+#undef DEBUG
+
+#if DEBUG
+#define debug(fmt, args...) vmm_printf("%s: " fmt, __FUNCTION__ , ## args)
+#else
+#define debug(fmt, args...)
+#endif
+
+/* (WDK) Adaptation */
+#define CONFIG_DM_ETH 1
+
+typedef unsigned long int uintptr_t;
+#define ARCH_DMA_MINALIGN ARCH_CACHE_LINE_SIZE
+
+#define ROUND(a,b) (((a) + (b) - 1) & ~((b) - 1))
+#define PAD_COUNT(s, pad) (((s) - 1) / (pad) + 1)
+#define PAD_SIZE(s, pad) (PAD_COUNT(s, pad) * pad)
+#define ALLOC_ALIGN_BUFFER_PAD(type, name, size, align, pad) \
+ char __##name[ROUND(PAD_SIZE((size) * sizeof(type), pad), align) \
+ + (align - 1)]; \
+ \
+ type *name = (type *)ALIGN((uintptr_t)__##name, align)
+#define ALLOC_ALIGN_BUFFER(type, name, size, align) \
+ ALLOC_ALIGN_BUFFER_PAD(type, name, size, align, 1)
+#define ALLOC_CACHE_ALIGN_BUFFER_PAD(type, name, size, pad) \
+ ALLOC_ALIGN_BUFFER_PAD(type, name, size, ARCH_DMA_MINALIGN, pad)
+#define ALLOC_CACHE_ALIGN_BUFFER(type, name, size) \
+ ALLOC_ALIGN_BUFFER(type, name, size, ARCH_DMA_MINALIGN)
+
+struct ueth_data {
+ /* eth info */
+ int phy_id; /* mii phy id */
+ struct vmm_thread *rx_thread; /* rx poll thread */
+
+ /* usb info */
+ struct usb_device *pusb_dev; /* this usb_device */
+ unsigned char ifnum; /* interface number */
+ unsigned char ep_in; /* in endpoint */
+ unsigned char ep_out; /* out ....... */
+ unsigned char ep_int; /* interrupt . */
+ unsigned char subclass; /* as in overview */
+ unsigned char protocol; /* .............. */
+ unsigned char irqinterval; /* Intervall for IRQ Pipe */
+};
+
+/* Some extra defines */
+#define HS_USB_PKT_SIZE 512
+#define FS_USB_PKT_SIZE 64
+
+/* 5/33 is lower limit for BURST_CAP to work */
+#define DEFAULT_HS_BURST_CAP_SIZE (5 * HS_USB_PKT_SIZE)
+#define DEFAULT_FS_BURST_CAP_SIZE (33 * FS_USB_PKT_SIZE)
+#define DEFAULT_BULK_IN_DELAY 0x00002000
+#define MAX_SINGLE_PACKET_SIZE 2048
+#define EEPROM_MAC_OFFSET 0x01
+#define SMSC95XX_INTERNAL_PHY_ID 1
+#define ETH_P_8021Q 0x8100 /* 802.1Q VLAN Extended Header */
+
+/* local defines */
+#define SMSC95XX_BASE_NAME "sms"
+#define USB_CTRL_SET_TIMEOUT 5000
+#define USB_CTRL_GET_TIMEOUT 5000
+#define USB_BULK_SEND_TIMEOUT 5000
+#define USB_BULK_RECV_TIMEOUT 5000
+
+#define RX_URB_SIZE DEFAULT_HS_BURST_CAP_SIZE
+#define PHY_CONNECT_TIMEOUT 5000
+
+/* driver private */
+struct smsc95xx_private {
+#ifdef CONFIG_DM_ETH
+ struct ueth_data ueth;
+#endif
+ size_t rx_urb_size; /* maximum USB URB size */
+ u32 mac_cr; /* MAC control register value */
+ int have_hwaddr; /* 1 if we have a hardware MAC address */
+};
+
+/*
+ * Smsc95xx infrastructure commands
+ */
+static int smsc95xx_write_reg(struct usb_device *udev, u32 index, u32 data)
+{
+ int ret, len;
+ ALLOC_CACHE_ALIGN_BUFFER(u32, tmpbuf, 1);
+
+ tmpbuf[0] = cpu_to_le32(data);
+
+ ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+ USB_VENDOR_REQUEST_WRITE_REGISTER,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0, index, tmpbuf, sizeof(data), &len,
+ USB_CTRL_SET_TIMEOUT);
+
+ if (ret != VMM_OK || len != sizeof(data)) {
+ debug("smsc95xx_write_reg failed: index=%d, data=%d, len=%d\n",
+ index, data, len);
+ return -EIO;
+ }
+ return 0;
+}
+
+static int smsc95xx_read_reg(struct usb_device *udev, u32 index, u32 *data)
+{
+ int ret, len;
+ ALLOC_CACHE_ALIGN_BUFFER(u32, tmpbuf, 1);
+
+ ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
+ USB_VENDOR_REQUEST_READ_REGISTER,
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0, index, tmpbuf, sizeof(*data), &len,
+ USB_CTRL_GET_TIMEOUT);
+
+ if (ret != VMM_OK || len != sizeof(*data)) {
+ debug("smsc95xx_read_reg failed: index=%d, len=%d\n",
+ index, len);
+ return -EIO;
+ }
+
+ *data = le32_to_cpu(tmpbuf[0]);
+ return 0;
+}
+
+/* Loop until the read is completed with timeout */
+static int smsc95xx_phy_wait_not_busy(struct usb_device *udev)
+{
+ struct vmm_timeval limit, now;
+ u32 val;
+
+ vmm_wallclock_get_local_time(&now);
+ limit = vmm_timeval_add(now, (struct vmm_timeval){1, 0});
+
+ do {
+ smsc95xx_read_reg(udev, MII_ADDR, &val);
+ if (!(val & MII_BUSY_))
+ return 0;
+
+ vmm_wallclock_get_local_time(&now);
+ } while (vmm_timeval_compare(&limit, &now) > 0);
+
+ return -ETIMEDOUT;
+}
+
+static int smsc95xx_mdio_read(struct usb_device *udev, int phy_id, int idx)
+{
+ u32 val, addr;
+
+ /* confirm MII not busy */
+ if (smsc95xx_phy_wait_not_busy(udev)) {
+ debug("MII is busy in smsc95xx_mdio_read\n");
+ return -ETIMEDOUT;
+ }
+
+ /* set the address, index & direction (read from PHY) */
+ addr = (phy_id << 11) | (idx << 6) | MII_READ_;
+ smsc95xx_write_reg(udev, MII_ADDR, addr);
+
+ if (smsc95xx_phy_wait_not_busy(udev)) {
+ debug("Timed out reading MII reg %02X\n", idx);
+ return -ETIMEDOUT;
+ }
+
+ smsc95xx_read_reg(udev, MII_DATA, &val);
+
+ return (u16)(val & 0xFFFF);
+}
+
+static void smsc95xx_mdio_write(struct usb_device *udev, int phy_id, int idx,
+ int regval)
+{
+ u32 val, addr;
+
+ /* confirm MII not busy */
+ if (smsc95xx_phy_wait_not_busy(udev)) {
+ debug("MII is busy in smsc95xx_mdio_write\n");
+ return;
+ }
+
+ val = regval;
+ smsc95xx_write_reg(udev, MII_DATA, val);
+
+ /* set the address, index & direction (write to PHY) */
+ addr = (phy_id << 11) | (idx << 6) | MII_WRITE_;
+ smsc95xx_write_reg(udev, MII_ADDR, addr);
+
+ if (smsc95xx_phy_wait_not_busy(udev))
+ debug("Timed out writing MII reg %02X\n", idx);
+}
+
+static int smsc95xx_eeprom_confirm_not_busy(struct usb_device *udev)
+{
+ struct vmm_timeval limit, now;
+ u32 val;
+
+ vmm_wallclock_get_local_time(&now);
+ limit = vmm_timeval_add(now, (struct vmm_timeval){10, 0});
+ do {
+ smsc95xx_read_reg(udev, E2P_CMD, &val);
+ if (!(val & E2P_CMD_BUSY_))
+ return 0;
+
+ vmm_usleep(40);
+ vmm_wallclock_get_local_time(&now);
+ } while (vmm_timeval_compare(&limit, &now) > 0);
+
+ debug("EEPROM is busy\n");
+ return -ETIMEDOUT;
+}
+
+static int smsc95xx_wait_eeprom(struct usb_device *udev)
+{
+ struct vmm_timeval limit, now;
+ u32 val;
+
+ vmm_wallclock_get_local_time(&now);
+ limit = vmm_timeval_add(now, (struct vmm_timeval){10, 0});
+ do {
+ smsc95xx_read_reg(udev, E2P_CMD, &val);
+ if (!(val & E2P_CMD_BUSY_) || (val & E2P_CMD_TIMEOUT_))
+ break;
+
+ vmm_usleep(40);
+ vmm_wallclock_get_local_time(&now);
+ } while (vmm_timeval_compare(&limit, &now) > 0);
+
+ if (val & (E2P_CMD_TIMEOUT_ | E2P_CMD_BUSY_)) {
+ debug("EEPROM read operation timeout\n");
+ return -ETIMEDOUT;
+ }
+ return 0;
+}
+
+static int smsc95xx_read_eeprom(struct usb_device *udev, u32 offset, u32 length,
+ u8 *data)
+{
+ u32 val;
+ int i, ret;
+
+ ret = smsc95xx_eeprom_confirm_not_busy(udev);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < length; i++) {
+ val = E2P_CMD_BUSY_ | E2P_CMD_READ_ | (offset & E2P_CMD_ADDR_);
+ smsc95xx_write_reg(udev, E2P_CMD, val);
+
+ ret = smsc95xx_wait_eeprom(udev);
+ if (ret < 0)
+ return ret;
+
+ ret = smsc95xx_read_reg(udev, E2P_DATA, &val);
+ if (ret < 0)
+ return ret;
+
+ data[i] = val & 0xFF;
+ offset++;
+ }
+ return 0;
+}
+
+/*
+ * mii_nway_restart - restart NWay (autonegotiation) for this interface
+ *
+ * Returns 0 on success, negative on error.
+ */
+static int smsc95xx_mii_nway_restart(struct usb_device *udev, struct ueth_data *dev)
+{
+ int bmcr;
+ int r = -1;
+
+ /* if autoneg is off, it's an error */
+ bmcr = smsc95xx_mdio_read(udev, dev->phy_id, MII_BMCR);
+
+ if (bmcr & BMCR_ANENABLE) {
+ bmcr |= BMCR_ANRESTART;
+ smsc95xx_mdio_write(udev, dev->phy_id, MII_BMCR, bmcr);
+ r = 0;
+ }
+ return r;
+}
+
+static int smsc95xx_phy_initialize(struct usb_device *udev,
+ struct ueth_data *dev)
+{
+ smsc95xx_mdio_write(udev, dev->phy_id, MII_BMCR, BMCR_RESET);
+ smsc95xx_mdio_write(udev, dev->phy_id, MII_ADVERTISE,
+ ADVERTISE_ALL | ADVERTISE_CSMA |
+ ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM);
+
+ /* read to clear */
+ smsc95xx_mdio_read(udev, dev->phy_id, PHY_INT_SRC);
+
+ smsc95xx_mdio_write(udev, dev->phy_id, PHY_INT_MASK,
+ PHY_INT_MASK_DEFAULT_);
+ smsc95xx_mii_nway_restart(udev, dev);
+
+ debug("phy initialised succesfully\n");
+ return 0;
+}
+
+static int smsc95xx_init_mac_address(unsigned char *enetaddr,
+ struct usb_device *udev)
+{
+ const uint8_t *mac_addr;
+
+ /* maybe the boot loader passed the MAC address in devicetree */
+ mac_addr = of_get_mac_address(udev->dev.of_node);
+ if (mac_addr) {
+ memcpy(enetaddr, mac_addr, ETH_ALEN);
+ debug("MAC address read from OF_NODE\n");
+ return VMM_OK;
+ }
+
+ /* try reading mac address from EEPROM */
+ if (smsc95xx_read_eeprom(udev, EEPROM_MAC_OFFSET, ETH_ALEN,
+ enetaddr) == 0) {
+ if (is_valid_ether_addr(enetaddr)) {
+ /* eeprom values are valid so use them */
+ debug("MAC address read from EEPROM\n");
+ return VMM_OK;
+ }
+ }
+
+ /* no useful static MAC address found. generate a random one */
+ random_ether_addr(enetaddr);
+ debug("MAC address generated randomly\n");
+ return VMM_OK;
+}
+
+static int smsc95xx_write_hwaddr_common(struct usb_device *udev,
+ struct smsc95xx_private *priv,
+ unsigned char *enetaddr)
+{
+ u32 addr_lo = get_unaligned_le32(&enetaddr[0]);
+ u32 addr_hi = get_unaligned_le16(&enetaddr[4]);
+ int ret;
+
+ /* set hardware address */
+ debug("** %s()\n", __func__);
+ ret = smsc95xx_write_reg(udev, ADDRL, addr_lo);
+ if (ret < 0)
+ return ret;
+
+ ret = smsc95xx_write_reg(udev, ADDRH, addr_hi);
+ if (ret < 0)
+ return ret;
+
+ debug("MAC %pM\n", enetaddr);
+ priv->have_hwaddr = 1;
+
+ return 0;
+}
+
+/* Enable or disable Tx & Rx checksum offload engines */
+static int smsc95xx_set_csums(struct usb_device *udev, int use_tx_csum,
+ int use_rx_csum)
+{
+ u32 read_buf;
+ int ret = smsc95xx_read_reg(udev, COE_CR, &read_buf);
+ if (ret < 0)
+ return ret;
+
+ if (use_tx_csum)
+ read_buf |= Tx_COE_EN_;
+ else
+ read_buf &= ~Tx_COE_EN_;
+
+ if (use_rx_csum)
+ read_buf |= Rx_COE_EN_;
+ else
+ read_buf &= ~Rx_COE_EN_;
+
+ ret = smsc95xx_write_reg(udev, COE_CR, read_buf);
+ if (ret < 0)
+ return ret;
+
+ debug("COE_CR = 0x%08x\n", read_buf);
+ return 0;
+}
+
+static void smsc95xx_set_multicast(struct smsc95xx_private *priv)
+{
+ /* Enable promiscuous mode for debugging */
+ priv->mac_cr |= (MAC_CR_PRMS_);
+ priv->mac_cr &= ~(MAC_CR_MCPAS_ | MAC_CR_HPFILT_);
+
+ /* No multicast in u-boot */
+ //priv->mac_cr &= ~(MAC_CR_PRMS_ | MAC_CR_MCPAS_ | MAC_CR_HPFILT_);
+}
+
+/* starts the TX path */
+static void smsc95xx_start_tx_path(struct usb_device *udev,
+ struct smsc95xx_private *priv)
+{
+ u32 reg_val;
+
+ /* Enable Tx at MAC */
+ priv->mac_cr |= MAC_CR_TXEN_;
+
+ smsc95xx_write_reg(udev, MAC_CR, priv->mac_cr);
+
+ /* Enable Tx at SCSRs */
+ reg_val = TX_CFG_ON_;
+ smsc95xx_write_reg(udev, TX_CFG, reg_val);
+}
+
+/* Starts the Receive path */
+static void smsc95xx_start_rx_path(struct usb_device *udev,
+ struct smsc95xx_private *priv)
+{
+ priv->mac_cr |= MAC_CR_RXEN_;
+ smsc95xx_write_reg(udev, MAC_CR, priv->mac_cr);
+}
+
+static int smsc95xx_init_common(struct usb_device *udev, struct ueth_data *dev,
+ struct smsc95xx_private *priv,
+ unsigned char *enetaddr)
+{
+ int ret;
+ u32 write_buf;
+ u32 read_buf;
+ u32 burst_cap;
+ int timeout;
+#define TIMEOUT_RESOLUTION 50 /* ms */
+ int link_detected;
+
+ debug("** %s()\n", __func__);
+ dev->phy_id = SMSC95XX_INTERNAL_PHY_ID; /* fixed phy id */
+
+ write_buf = HW_CFG_LRST_;
+ ret = smsc95xx_write_reg(udev, HW_CFG, write_buf);
+ if (ret < 0)
+ return ret;
+
+ timeout = 0;
+ do {
+ ret = smsc95xx_read_reg(udev, HW_CFG, &read_buf);
+ if (ret < 0)
+ return ret;
+ vmm_udelay(10 * 1000);
+ timeout++;
+ } while ((read_buf & HW_CFG_LRST_) && (timeout < 100));
+
+ if (timeout >= 100) {
+ debug("timeout waiting for completion of Lite Reset\n");
+ return -ETIMEDOUT;
+ }
+
+ write_buf = PM_CTL_PHY_RST_;
+ ret = smsc95xx_write_reg(udev, PM_CTRL, write_buf);
+ if (ret < 0)
+ return ret;
+
+ timeout = 0;
+ do {
+ ret = smsc95xx_read_reg(udev, PM_CTRL, &read_buf);
+ if (ret < 0)
+ return ret;
+ vmm_udelay(10 * 1000);
+ timeout++;
+ } while ((read_buf & PM_CTL_PHY_RST_) && (timeout < 100));
+
+ if (timeout >= 100) {
+ debug("timeout waiting for PHY Reset\n");
+ return -ETIMEDOUT;
+ }
+
+ if (!priv->have_hwaddr) {
+ ret = smsc95xx_init_mac_address(enetaddr, udev);
+ priv->have_hwaddr = !ret;
+ if (ret != 0)
+ {
+ debug("Error: SMSC95xx: No MAC address set - set usbethaddr\n");
+ return -ENODATA;
+ }
+ }
+
+ ret = smsc95xx_write_hwaddr_common(udev, priv, enetaddr);
+ if (ret < 0)
+ return ret;
+
+#ifdef TURBO_MODE
+ if (dev->pusb_dev->speed == USB_SPEED_HIGH) {
+ burst_cap = DEFAULT_HS_BURST_CAP_SIZE / HS_USB_PKT_SIZE;
+ priv->rx_urb_size = DEFAULT_HS_BURST_CAP_SIZE;
+ } else {
+ burst_cap = DEFAULT_FS_BURST_CAP_SIZE / FS_USB_PKT_SIZE;
+ priv->rx_urb_size = DEFAULT_FS_BURST_CAP_SIZE;
+ }
+#else
+ burst_cap = 0;
+ priv->rx_urb_size = MAX_SINGLE_PACKET_SIZE;
+#endif
+ debug("rx_urb_size=%ld\n", (ulong)priv->rx_urb_size);
+
+ ret = smsc95xx_write_reg(udev, BURST_CAP, burst_cap);
+ if (ret < 0)
+ return ret;
+
+ ret = smsc95xx_read_reg(udev, BURST_CAP, &read_buf);
+ if (ret < 0)
+ return ret;
+ debug("Read Value from BURST_CAP after writing: 0x%08x\n", read_buf);
+
+ read_buf = DEFAULT_BULK_IN_DELAY;
+ ret = smsc95xx_write_reg(udev, BULK_IN_DLY, read_buf);
+ if (ret < 0)
+ return ret;
+
+ ret = smsc95xx_read_reg(udev, BULK_IN_DLY, &read_buf);
+ if (ret < 0)
+ return ret;
+ debug("Read Value from BULK_IN_DLY after writing: "
+ "0x%08x\n", read_buf);
+
+ ret = smsc95xx_read_reg(udev, HW_CFG, &read_buf);
+ if (ret < 0)
+ return ret;
+ debug("Read Value from HW_CFG: 0x%08x\n", read_buf);
+
+#ifdef TURBO_MODE
+ read_buf |= (HW_CFG_MEF_ | HW_CFG_BCE_);
+#endif
+ read_buf &= ~HW_CFG_RXDOFF_;
+
+#ifdef NET_IP_ALIGN
+#undef NET_IP_ALIGN
+#endif
+#define NET_IP_ALIGN 0
+ read_buf |= NET_IP_ALIGN << 9;
+
+ ret = smsc95xx_write_reg(udev, HW_CFG, read_buf);
+ if (ret < 0)
+ return ret;
+
+ ret = smsc95xx_read_reg(udev, HW_CFG, &read_buf);
+ if (ret < 0)
+ return ret;
+ debug("Read Value from HW_CFG after writing: 0x%08x\n", read_buf);
+
+ write_buf = 0xFFFFFFFF;
+ ret = smsc95xx_write_reg(udev, INT_STS, write_buf);
+ if (ret < 0)
+ return ret;
+
+ ret = smsc95xx_read_reg(udev, ID_REV, &read_buf);
+ if (ret < 0)
+ return ret;
+ debug("ID_REV = 0x%08x\n", read_buf);
+
+ /* Configure GPIO pins as LED outputs */
+ write_buf = LED_GPIO_CFG_SPD_LED | LED_GPIO_CFG_LNK_LED |
+ LED_GPIO_CFG_FDX_LED;
+ ret = smsc95xx_write_reg(udev, LED_GPIO_CFG, write_buf);
+ if (ret < 0)
+ return ret;
+ debug("LED_GPIO_CFG set\n");
+
+ /* Init Tx */
+ write_buf = 0;
+ ret = smsc95xx_write_reg(udev, FLOW, write_buf);
+ if (ret < 0)
+ return ret;
+
+ read_buf = AFC_CFG_DEFAULT;
+ ret = smsc95xx_write_reg(udev, AFC_CFG, read_buf);
+ if (ret < 0)
+ return ret;
+
+ ret = smsc95xx_read_reg(udev, MAC_CR, &priv->mac_cr);
+ if (ret < 0)
+ return ret;
+
+ /* Init Rx. Set Vlan */
+ write_buf = (u32)ETH_P_8021Q;
+ ret = smsc95xx_write_reg(udev, VLAN1, write_buf);
+ if (ret < 0)
+ return ret;
+
+ /* Disable checksum offload engines */
+ ret = smsc95xx_set_csums(udev, 0, 0);
+ if (ret < 0) {
+ debug("Failed to set csum offload: %d\n", ret);
+ return ret;
+ }
+
+ smsc95xx_set_multicast(priv);
+
+ ret = smsc95xx_phy_initialize(udev, dev);
+ if (ret < 0)
+ return ret;
+ ret = smsc95xx_read_reg(udev, INT_EP_CTL, &read_buf);
+ if (ret < 0)
+ return ret;
+
+ /* enable PHY interrupts */
+ read_buf |= INT_EP_CTL_PHY_INT_;
+
+ ret = smsc95xx_write_reg(udev, INT_EP_CTL, read_buf);
+ if (ret < 0)
+ return ret;
+
+ smsc95xx_start_tx_path(udev, priv);
+ smsc95xx_start_rx_path(udev, priv);
+
+ timeout = 0;
+ do {
+ link_detected = smsc95xx_mdio_read(udev, dev->phy_id, MII_BMSR)
+ & BMSR_LSTATUS;
+ if (!link_detected) {
+ if (timeout == 0)
+ debug("Waiting for Ethernet connection... \n");
+ vmm_udelay(TIMEOUT_RESOLUTION * 1000);
+ timeout += TIMEOUT_RESOLUTION;
+ }
+ } while (!link_detected && timeout < PHY_CONNECT_TIMEOUT);
+ if (link_detected) {
+ if (timeout != 0)
+ debug("done.\n");
+ } else {
+ debug("unable to connect.\n");
+ return -EIO;
+ }
+ return 0;
+}
+
+static int smsc95xx_send_common(struct ueth_data *dev, void *packet, int length)
+{
+ int err;
+ int actual_len;
+ u32 tx_cmd_a;
+ u32 tx_cmd_b;
+ ALLOC_CACHE_ALIGN_BUFFER(unsigned char, msg,
+ ETH_FRAME_LEN + sizeof(tx_cmd_a) + sizeof(tx_cmd_b));
+
+ debug("** %s(), len %d, buf %#x\n", __func__, length,
+ (unsigned int)(ulong)msg);
+
+ if (length > ETH_FRAME_LEN)
+ return -ENOSPC;
+
+ tx_cmd_a = cpu_to_le32((u32)length | TX_CMD_A_FIRST_SEG_ | TX_CMD_A_LAST_SEG_);
+ tx_cmd_b = cpu_to_le32((u32)length);
+
+ /* prepend cmd_a and cmd_b */
+ memcpy(msg, &tx_cmd_a, sizeof(tx_cmd_a));
+ memcpy(msg + sizeof(tx_cmd_a), &tx_cmd_b, sizeof(tx_cmd_b));
+ memcpy(msg + sizeof(tx_cmd_a) + sizeof(tx_cmd_b), (void *)packet,
+ length);
+
+ err = usb_bulk_msg(dev->pusb_dev,
+ usb_sndbulkpipe(dev->pusb_dev, dev->ep_out),
+ (void *)msg,
+ length + sizeof(tx_cmd_a) + sizeof(tx_cmd_b),
+ &actual_len,
+ USB_BULK_SEND_TIMEOUT);
+
+ debug("Tx: len = %u, actual = %u, err = %d\n",
+ (unsigned int)(length + sizeof(tx_cmd_a) + sizeof(tx_cmd_b)),
+ (unsigned int)actual_len, err);
+
+ return err;
+}
+
+/*
+ * Smsc95xx callbacks
+ */
+static int smsc95xx_init(struct net_device *netdev)
+{
+ struct smsc95xx_private *priv = netdev_priv(netdev);
+ struct usb_device *udev = priv->ueth.pusb_dev;
+ unsigned char enetaddr[ETH_ALEN];
+
+ /* Driver-model Ethernet ensures we have this */
+ priv->have_hwaddr = 1;
+
+ return smsc95xx_init_common(udev, &priv->ueth, priv, enetaddr);
+}
+
+
+static int smsc95xx_send(struct sk_buff *skb, struct net_device *netdev)
+{
+ struct smsc95xx_private *priv = netdev_priv(netdev);
+ char* payload = skb_data(skb);
+ int paylen = skb_len(skb);
+ int ret = VMM_OK;
+
+ skb_dma_ensure(skb);
+
+ ret = smsc95xx_send_common(&priv->ueth, payload, paylen);
+ if (VMM_OK == ret)
+ {
+ netdev->stats.tx_bytes += paylen;
+ netdev->stats.tx_packets++;
+ return NETDEV_TX_OK;
+ }
+
+ return NETDEV_TX_BUSY;
+}
+
+static int smsc95xx_recv(struct net_device *netdev)
+{
+ struct smsc95xx_private *priv = netdev_priv(netdev);
+ struct ueth_data *dev = &priv->ueth;
+ struct sk_buff *skb;
+ uint32_t pad;
+ int totlen, err;
+ uint32_t msghdr;
+ uint16_t pktlen;
+
+ skb = dev_alloc_skb(RX_URB_SIZE);
+ if (unlikely(skb == NULL)) {
+ debug("%s: Low memory, packet dropped.\n", netdev->name);
+ netdev->stats.rx_dropped++;
+ return -ENOMEM;
+ }
+
+ // Align on the cache line size
+ pad = ALIGN((unsigned long)skb_data(skb), VMM_CACHE_LINE_SIZE) - (unsigned long)skb_data(skb);
+ skb_reserve(skb, pad);
+
+ err = usb_bulk_msg(dev->pusb_dev,
+ usb_rcvbulkpipe(dev->pusb_dev, dev->ep_in),
+ (void *)skb_data(skb), (RX_URB_SIZE - pad), &totlen,
+ USB_BULK_RECV_TIMEOUT);
+
+ if (VMM_OK != err) {
+ debug("Rx: failed to receive\n");
+ dev_kfree_skb_any(skb);
+ return -err;
+ }
+
+ debug("Rx: len = %u, actual = %u, err = %d\n", RX_URB_SIZE - pad,
+ totlen, err);
+
+ if (totlen > RX_URB_SIZE) {
+ debug("Rx: received too many bytes %d\n", totlen);
+ dev_kfree_skb_any(skb);
+ return -ENOSPC;
+ }
+
+ memcpy(&msghdr, skb_data(skb), sizeof(msghdr));
+ msghdr = le32_to_cpu(msghdr);
+ pktlen = ((msghdr & RX_STS_FL_) >> 16);
+
+ if (msghdr & RX_STS_ES_) {
+ debug("Rx: Error header=%#x", pktlen);
+ netdev->stats.rx_errors++;
+ dev_kfree_skb_any(skb);
+ return -EIO;
+ }
+
+ if (pktlen > (uint16_t)totlen - sizeof(msghdr)) {
+ debug("Rx: too large packet: %d\n", pktlen);
+ netdev->stats.rx_errors++;
+ dev_kfree_skb_any(skb);
+ return -EIO;
+ }
+
+ /*
+ * 1st 4 bytes contain the length of the actual data plus error
+ * info. Extract data length.
+ */
+ skb_reserve(skb, sizeof(msghdr));
+ skb_put(skb, pktlen);
+ //PRINT_PKT(skb_data(skb), pktlen);
+
+ if (msghdr & RX_STS_MF_) {
+ netdev->stats.multicast++;
+ }
+
+ netdev->stats.rx_packets++;
+ netdev->stats.rx_bytes += pktlen;
+
+ return netif_rx(skb, netdev);
+}
+
+static int smsc95xx_recv_worker(void *data)
+{
+ int rc = VMM_OK;
+ struct smsc95xx_private *priv = netdev_priv(data);
+ struct ueth_data *dev = &priv->ueth;
+ uint32_t rx_bytes = 0;
+
+ while (1) {
+ rc = smsc95xx_read_reg(dev->pusb_dev, RX_FIFO_INF, &rx_bytes);
+ if (rc != VMM_OK || (rx_bytes & 0xFFFF) == 0)
+ {
+ vmm_usleep(DEFAULT_BULK_IN_DELAY);
+ continue;
+ }
+
+ smsc95xx_recv(data);
+ }
+
+ return rc;
+}
+
+#if 0
+static int smsc95xx_write_hwaddr(struct eth_device *eth)
+{
+ struct ueth_data *dev = eth->priv;
+ struct usb_device *udev = dev->pusb_dev;
+ struct smsc95xx_private *priv = dev->dev_priv;
+
+ return smsc95xx_write_hwaddr_common(udev, priv, eth->enetaddr);
+}
+#endif
+
+struct smsc95xx_dongle {
+ unsigned short vendor;
+ unsigned short product;
+};
+
+static const struct smsc95xx_dongle smsc95xx_dongles[] = {
+ { 0x0424, 0xec00 }, /* LAN9512/LAN9514 Ethernet */
+ { 0x0424, 0x9500 }, /* LAN9500 Ethernet */
+ { 0x0424, 0x9730 }, /* LAN9730 Ethernet (HSIC) */
+ { 0x0424, 0x9900 }, /* SMSC9500 USB Ethernet Device (SAL10) */
+ { 0x0424, 0x9e00 }, /* LAN9500A Ethernet */
+ { 0x0000, 0x0000 } /* END - Do not remove */
+};
+
+static int smsc95xx_open(struct net_device *netdev)
+{
+ struct smsc95xx_private *priv = netdev_priv(netdev);
+
+ debug("** TRACE **");
+
+ priv->ueth.rx_thread = vmm_threads_create(netdev->name, smsc95xx_recv_worker,
+ netdev, VMM_THREAD_DEF_PRIORITY, VMM_THREAD_DEF_TIME_SLICE);
+
+ netif_start_queue(netdev);
+ vmm_threads_start(priv->ueth.rx_thread);
+ return 0;
+
+fail:
+ vmm_threads_stop(priv->ueth.rx_thread);
+ vmm_threads_destroy(priv->ueth.rx_thread);
+ return -EIO;
+}
+
+
+static int smsc95xx_stop(struct net_device *netdev)
+{
+ struct smsc95xx_private *priv = netdev_priv(netdev);
+
+ debug("** TRACE **");
+
+ vmm_threads_stop(priv->ueth.rx_thread);
+ vmm_threads_destroy(priv->ueth.rx_thread);
+ netif_stop_queue(netdev);
+ return 0;
+}
+
+static const struct net_device_ops smsc95xx_netdev_ops = {
+ .ndo_init = smsc95xx_init,
+ .ndo_open = smsc95xx_open,
+ .ndo_stop = smsc95xx_stop,
+ .ndo_start_xmit = smsc95xx_send,
+#if 0
+ .ndo_validate_addr = NULL,
+ .ndo_tx_timeout = NULL,
+ .ndo_do_ioctl = NULL,
+ .ndo_change_mtu = NULL,
+#endif
+};
+
+/* Probe to see if a new device is actually an SMSC device */
+static int __smsc95xx_eth_probe(struct usb_interface *iface,
+ const struct usb_device_id *id, struct ueth_data *ss)
+{
+ struct usb_device *dev;
+ struct usb_interface_descriptor *iface_desc;
+ int i;
+
+ if (NULL == iface || NULL == id || NULL == ss)
+ return 0;
+
+ /* let's examine the device now */
+ dev = interface_to_usbdev(iface);
+ iface_desc = &iface->desc;
+
+ for (i = 0; smsc95xx_dongles[i].vendor != 0; i++) {
+ if (id->idVendor == smsc95xx_dongles[i].vendor &&
+ id->idProduct == smsc95xx_dongles[i].product)
+ /* Found a supported dongle */
+ break;
+ }
+
+ if (smsc95xx_dongles[i].vendor == 0)
+ return 0;
+
+ /* At this point, we know we've got a live one */
+ debug("USB Ethernet device detected\n");
+ memset(ss, 0, sizeof(struct ueth_data));
+
+ /* Initialize the ueth_data structure with some useful info */
+ ss->ifnum = 0;
+ ss->pusb_dev = interface_to_usbdev(iface);
+ ss->subclass = iface_desc->bInterfaceSubClass;
+ ss->protocol = iface_desc->bInterfaceProtocol;
+
+ /*
+ * We are expecting a minimum of 3 endpoints - in, out (bulk), and int.
+ * We will ignore any others.
+ */
+ for (i = 0; i < iface_desc->bNumEndpoints; i++) {
+ /* is it an BULK endpoint? */
+ if ((iface->ep_desc[i].bmAttributes &
+ USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_BULK) {
+ if (iface->ep_desc[i].bEndpointAddress & USB_DIR_IN)
+ ss->ep_in =
+ iface->ep_desc[i].bEndpointAddress &
+ USB_ENDPOINT_NUMBER_MASK;
+ else
+ ss->ep_out =
+ iface->ep_desc[i].bEndpointAddress &
+ USB_ENDPOINT_NUMBER_MASK;
+ }
+
+ /* is it an interrupt endpoint? */
+ if ((iface->ep_desc[i].bmAttributes &
+ USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT) {
+ ss->ep_int = iface->ep_desc[i].bEndpointAddress &
+ USB_ENDPOINT_NUMBER_MASK;
+ ss->irqinterval = iface->ep_desc[i].bInterval;
+ }
+ }
+ debug("Endpoints In %d Out %d Int %d\n",
+ ss->ep_in, ss->ep_out, ss->ep_int);
+
+ /* Do some basic sanity checks, and bail if we find a problem */
+ if (usb_set_interface(dev, iface_desc->bInterfaceNumber, 0) ||
+ !ss->ep_in || !ss->ep_out || !ss->ep_int) {
+ debug("Problems with device\n");
+ return 0;
+ }
+
+ return 1;
+}
+
+static int smsc95xx_eth_probe(struct usb_interface *iface,
+ const struct usb_device_id *id)
+{
+ int rc = VMM_OK;
+ struct usb_device *usbdev;
+ struct net_device *netdev;
+ struct smsc95xx_private *priv;
+
+ usbdev = interface_to_usbdev(iface);
+ netdev = alloc_etherdev(sizeof(struct smsc95xx_private));
+ if (!netdev) {
+ vmm_printf("%s Failed to allocate netdev for %s\n",
+ __func__, usbdev->product);
+ rc = VMM_EFAIL;
+ goto done;
+ }
+
+ SET_NETDEV_DEV(netdev, &iface->dev);
+ vmm_devdrv_set_data(&iface->dev, netdev);
+ vmm_snprintf(netdev->name, MAX_NETDEV_NAME_LEN, SMSC95XX_BASE_NAME "%d", (usbdev->portnum - 1));
+
+ priv = netdev_priv(netdev);
+ if (!__smsc95xx_eth_probe(iface, id, &priv->ueth)) {
+ rc = VMM_EFAIL;
+ goto free_netdev;
+ }
+
+ /* Fill in the fields of the device structure with ethernet values. */
+ ether_setup(netdev);
+
+ netdev->netdev_ops = &smsc95xx_netdev_ops;
+ rc = register_netdev(netdev);
+ if (VMM_OK == rc)
+ goto done;
+
+free_netdev:
+ vmm_devdrv_set_data(&iface->dev, NULL);
+ vmm_free(netdev);
+
+done:
+ return rc;
+}
+
+static void smsc95xx_eth_remove(struct usb_interface *iface)
+{
+ struct usb_device *dev;
+ struct ueth_data *ss;
+
+ dev = interface_to_usbdev(iface);
+ if (!dev) {
+ return;
+ }
+
+ ss = interface_get_data(iface);
+ if (!ss) {
+ return;
+ }
+
+ /* Free the private */
+ vmm_free(ss->pusb_dev->dev.priv);
+ vmm_free(ss);
+
+ /* Free the interface ueth */
+ interface_set_data(iface, NULL);
+ return;
+}
+
+static const struct usb_device_id smsc95xx_eth_id_table[] = {
+ { USB_DEVICE(0x05ac, 0x1402) },
+ { USB_DEVICE(0x0424, 0xec00) }, /* LAN9512/LAN9514 Ethernet */
+ { USB_DEVICE(0x0424, 0x9500) }, /* LAN9500 Ethernet */
+ { USB_DEVICE(0x0424, 0x9730) }, /* LAN9730 Ethernet (HSIC) */
+ { USB_DEVICE(0x0424, 0x9900) }, /* SMSC9500 USB Ethernet (SAL10) */
+ { USB_DEVICE(0x0424, 0x9e00) }, /* LAN9500A Ethernet */
+ { } /* Terminating entry */
+};
+
+static struct usb_driver smsc95xx_eth = {
+ .name = "smsc95xx_eth",
+ .id_table = smsc95xx_eth_id_table,
+ .probe = smsc95xx_eth_probe,
+ .disconnect = smsc95xx_eth_remove
+};
+
+static int __init smsc95xx_eth_init(void)
+{
+ return usb_register(&smsc95xx_eth);
+}
+
+static void __exit smsc95xx_eth_exit(void)
+{
+ usb_deregister(&smsc95xx_eth);
+}
+
+VMM_DECLARE_MODULE(MODULE_DESC,
+ MODULE_AUTHOR,
+ MODULE_LICENSE,
+ MODULE_IPRIORITY,
+ MODULE_INIT,
+ MODULE_EXIT);
diff --git a/drivers/usb/devices/smsc95xx.h b/drivers/usb/devices/smsc95xx.h
new file mode 100644
index 0000000..cfc704f
--- /dev/null
+++ b/drivers/usb/devices/smsc95xx.h
@@ -0,0 +1,364 @@
+ /***************************************************************************
+ *
+ * Copyright (C) 2007-2008 SMSC
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ *****************************************************************************/
+
+#ifndef _SMSC95XX_H
+#define _SMSC95XX_H
+
+/* Tx command words */
+#define TX_CMD_A_DATA_OFFSET_ (0x001F0000) /* Data Start Offset */
+#define TX_CMD_A_FIRST_SEG_ (0x00002000) /* First Segment */
+#define TX_CMD_A_LAST_SEG_ (0x00001000) /* Last Segment */
+#define TX_CMD_A_BUF_SIZE_ (0x000007FF) /* Buffer Size */
+
+#define TX_CMD_B_CSUM_ENABLE (0x00004000) /* TX Checksum Enable */
+#define TX_CMD_B_ADD_CRC_DIS_ (0x00002000) /* Add CRC Disable */
+#define TX_CMD_B_DIS_PADDING_ (0x00001000) /* Disable Frame Padding */
+#define TX_CMD_B_FRAME_LENGTH_ (0x000007FF) /* Frame Length (bytes) */
+
+/* Rx status word */
+#define RX_STS_FF_ (0x40000000) /* Filter Fail */
+#define RX_STS_FL_ (0x3FFF0000) /* Frame Length */
+#define RX_STS_ES_ (0x00008000) /* Error Summary */
+#define RX_STS_BF_ (0x00002000) /* Broadcast Frame */
+#define RX_STS_LE_ (0x00001000) /* Length Error */
+#define RX_STS_RF_ (0x00000800) /* Runt Frame */
+#define RX_STS_MF_ (0x00000400) /* Multicast Frame */
+#define RX_STS_TL_ (0x00000080) /* Frame too long */
+#define RX_STS_CS_ (0x00000040) /* Collision Seen */
+#define RX_STS_FT_ (0x00000020) /* Frame Type */
+#define RX_STS_RW_ (0x00000010) /* Receive Watchdog */
+#define RX_STS_ME_ (0x00000008) /* MII Error */
+#define RX_STS_DB_ (0x00000004) /* Dribbling */
+#define RX_STS_CRC_ (0x00000002) /* CRC Error */
+
+/* SCSRs - System Control and Status Registers */
+/* Device ID and Revision Register */
+#define ID_REV (0x00)
+#define ID_REV_CHIP_ID_MASK_ (0xFFFF0000)
+#define ID_REV_CHIP_REV_MASK_ (0x0000FFFF)
+#define ID_REV_CHIP_ID_9500_ (0x9500)
+#define ID_REV_CHIP_ID_9500A_ (0x9E00)
+#define ID_REV_CHIP_ID_9512_ (0xEC00)
+#define ID_REV_CHIP_ID_9530_ (0x9530)
+#define ID_REV_CHIP_ID_89530_ (0x9E08)
+#define ID_REV_CHIP_ID_9730_ (0x9730)
+
+/* Interrupt Status Register */
+#define INT_STS (0x08)
+#define INT_STS_MAC_RTO_ (0x00040000) /* MAC Reset Time Out */
+#define INT_STS_TX_STOP_ (0x00020000) /* TX Stopped */
+#define INT_STS_RX_STOP_ (0x00010000) /* RX Stopped */
+#define INT_STS_PHY_INT_ (0x00008000) /* PHY Interrupt */
+#define INT_STS_TXE_ (0x00004000) /* Transmitter Error */
+#define INT_STS_TDFU_ (0x00002000) /* TX Data FIFO Underrun */
+#define INT_STS_TDFO_ (0x00001000) /* TX Data FIFO Overrun */
+#define INT_STS_RXDF_ (0x00000800) /* RX Dropped Frame */
+#define INT_STS_GPIOS_ (0x000007FF) /* GPIOs Interrupts */
+#define INT_STS_CLEAR_ALL_ (0xFFFFFFFF)
+
+/* Receive Configuration Register */
+#define RX_CFG (0x0C)
+#define RX_FIFO_FLUSH_ (0x00000001) /* Receive FIFO Flush */
+
+/* Transmit Configuration Register */
+#define TX_CFG (0x10)
+#define TX_CFG_ON_ (0x00000004) /* Transmitter Enable */
+#define TX_CFG_STOP_ (0x00000002) /* Stop Transmitter */
+#define TX_CFG_FIFO_FLUSH_ (0x00000001) /* Transmit FIFO Flush */
+
+/* Hardware Configuration Register */
+#define HW_CFG (0x14)
+#define HW_CFG_BIR_ (0x00001000) /* Bulk In Empty Response */
+#define HW_CFG_LEDB_ (0x00000800) /* Activity LED 80ms Bypass */
+#define HW_CFG_RXDOFF_ (0x00000600) /* RX Data Offset */
+#define HW_CFG_SBP_ (0x00000100) /* Stall Bulk Out Pipe Dis. */
+#define HW_CFG_IME_ (0x00000080) /* Internal MII Visi. Enable */
+#define HW_CFG_DRP_ (0x00000040) /* Discard Errored RX Frame */
+#define HW_CFG_MEF_ (0x00000020) /* Mult. ETH Frames/USB pkt */
+#define HW_CFG_ETC_ (0x00000010) /* EEPROM Timeout Control */
+#define HW_CFG_LRST_ (0x00000008) /* Soft Lite Reset */
+#define HW_CFG_PSEL_ (0x00000004) /* External PHY Select */
+#define HW_CFG_BCE_ (0x00000002) /* Burst Cap Enable */
+#define HW_CFG_SRST_ (0x00000001) /* Soft Reset */
+
+/* Receive FIFO Information Register */
+#define RX_FIFO_INF (0x18)
+#define RX_FIFO_INF_USED_ (0x0000FFFF) /* RX Data FIFO Used Space */
+
+/* Transmit FIFO Information Register */
+#define TX_FIFO_INF (0x1C)
+#define TX_FIFO_INF_FREE_ (0x0000FFFF) /* TX Data FIFO Free Space */
+
+/* Power Management Control Register */
+#define PM_CTRL (0x20)
+#define PM_CTL_RES_CLR_WKP_STS (0x00000200) /* Resume Clears Wakeup STS */
+#define PM_CTL_RES_CLR_WKP_EN (0x00000100) /* Resume Clears Wkp Enables */
+#define PM_CTL_DEV_RDY_ (0x00000080) /* Device Ready */
+#define PM_CTL_SUS_MODE_ (0x00000060) /* Suspend Mode */
+#define PM_CTL_SUS_MODE_0 (0x00000000)
+#define PM_CTL_SUS_MODE_1 (0x00000020)
+#define PM_CTL_SUS_MODE_2 (0x00000040)
+#define PM_CTL_SUS_MODE_3 (0x00000060)
+#define PM_CTL_PHY_RST_ (0x00000010) /* PHY Reset */
+#define PM_CTL_WOL_EN_ (0x00000008) /* Wake On Lan Enable */
+#define PM_CTL_ED_EN_ (0x00000004) /* Energy Detect Enable */
+#define PM_CTL_WUPS_ (0x00000003) /* Wake Up Status */
+#define PM_CTL_WUPS_NO_ (0x00000000) /* No Wake Up Event Detected */
+#define PM_CTL_WUPS_ED_ (0x00000001) /* Energy Detect */
+#define PM_CTL_WUPS_WOL_ (0x00000002) /* Wake On Lan */
+#define PM_CTL_WUPS_MULTI_ (0x00000003) /* Multiple Events Occurred */
+
+/* LED General Purpose IO Configuration Register */
+#define LED_GPIO_CFG (0x24)
+#define LED_GPIO_CFG_SPD_LED (0x01000000) /* GPIOz as Speed LED */
+#define LED_GPIO_CFG_LNK_LED (0x00100000) /* GPIOy as Link LED */
+#define LED_GPIO_CFG_FDX_LED (0x00010000) /* GPIOx as Full Duplex LED */
+
+/* General Purpose IO Configuration Register */
+#define GPIO_CFG (0x28)
+
+/* Automatic Flow Control Configuration Register */
+#define AFC_CFG (0x2C)
+#define AFC_CFG_HI_ (0x00FF0000) /* Auto Flow Ctrl High Level */
+#define AFC_CFG_LO_ (0x0000FF00) /* Auto Flow Ctrl Low Level */
+#define AFC_CFG_BACK_DUR_ (0x000000F0) /* Back Pressure Duration */
+#define AFC_CFG_FC_MULT_ (0x00000008) /* Flow Ctrl on Mcast Frame */
+#define AFC_CFG_FC_BRD_ (0x00000004) /* Flow Ctrl on Bcast Frame */
+#define AFC_CFG_FC_ADD_ (0x00000002) /* Flow Ctrl on Addr. Decode */
+#define AFC_CFG_FC_ANY_ (0x00000001) /* Flow Ctrl on Any Frame */
+/* Hi watermark = 15.5Kb (~10 mtu pkts) */
+/* low watermark = 3k (~2 mtu pkts) */
+/* backpressure duration = ~ 350us */
+/* Apply FC on any frame. */
+#define AFC_CFG_DEFAULT (0x00F830A1)
+
+/* EEPROM Command Register */
+#define E2P_CMD (0x30)
+#define E2P_CMD_BUSY_ (0x80000000) /* E2P Controller Busy */
+#define E2P_CMD_MASK_ (0x70000000) /* Command Mask (see below) */
+#define E2P_CMD_READ_ (0x00000000) /* Read Location */
+#define E2P_CMD_EWDS_ (0x10000000) /* Erase/Write Disable */
+#define E2P_CMD_EWEN_ (0x20000000) /* Erase/Write Enable */
+#define E2P_CMD_WRITE_ (0x30000000) /* Write Location */
+#define E2P_CMD_WRAL_ (0x40000000) /* Write All */
+#define E2P_CMD_ERASE_ (0x50000000) /* Erase Location */
+#define E2P_CMD_ERAL_ (0x60000000) /* Erase All */
+#define E2P_CMD_RELOAD_ (0x70000000) /* Data Reload */
+#define E2P_CMD_TIMEOUT_ (0x00000400) /* Set if no resp within 30ms */
+#define E2P_CMD_LOADED_ (0x00000200) /* Valid EEPROM found */
+#define E2P_CMD_ADDR_ (0x000001FF) /* Byte aligned address */
+
+#define MAX_EEPROM_SIZE (512)
+
+/* EEPROM Data Register */
+#define E2P_DATA (0x34)
+#define E2P_DATA_MASK_ (0x000000FF) /* EEPROM Data Mask */
+
+/* Burst Cap Register */
+#define BURST_CAP (0x38)
+#define BURST_CAP_MASK_ (0x000000FF) /* Max burst sent by the UTX */
+
+/* Configuration Straps Status Register */
+#define STRAP_STATUS (0x3C)
+#define STRAP_STATUS_PWR_SEL_ (0x00000020) /* Device self-powered */
+#define STRAP_STATUS_AMDIX_EN_ (0x00000010) /* Auto-MDIX Enabled */
+#define STRAP_STATUS_PORT_SWAP_ (0x00000008) /* USBD+/USBD- Swapped */
+#define STRAP_STATUS_EEP_SIZE_ (0x00000004) /* EEPROM Size */
+#define STRAP_STATUS_RMT_WKP_ (0x00000002) /* Remote Wkp supported */
+#define STRAP_STATUS_EEP_DISABLE_ (0x00000001) /* EEPROM Disabled */
+
+/* Data Port Select Register */
+#define DP_SEL (0x40)
+
+/* Data Port Command Register */
+#define DP_CMD (0x44)
+
+/* Data Port Address Register */
+#define DP_ADDR (0x48)
+
+/* Data Port Data 0 Register */
+#define DP_DATA0 (0x4C)
+
+/* Data Port Data 1 Register */
+#define DP_DATA1 (0x50)
+
+/* General Purpose IO Wake Enable and Polarity Register */
+#define GPIO_WAKE (0x64)
+
+/* Interrupt Endpoint Control Register */
+#define INT_EP_CTL (0x68)
+#define INT_EP_CTL_INTEP_ (0x80000000) /* Always TX Interrupt PKT */
+#define INT_EP_CTL_MAC_RTO_ (0x00080000) /* MAC Reset Time Out */
+#define INT_EP_CTL_RX_FIFO_ (0x00040000) /* RX FIFO Has Frame */
+#define INT_EP_CTL_TX_STOP_ (0x00020000) /* TX Stopped */
+#define INT_EP_CTL_RX_STOP_ (0x00010000) /* RX Stopped */
+#define INT_EP_CTL_PHY_INT_ (0x00008000) /* PHY Interrupt */
+#define INT_EP_CTL_TXE_ (0x00004000) /* TX Error */
+#define INT_EP_CTL_TDFU_ (0x00002000) /* TX Data FIFO Underrun */
+#define INT_EP_CTL_TDFO_ (0x00001000) /* TX Data FIFO Overrun */
+#define INT_EP_CTL_RXDF_ (0x00000800) /* RX Dropped Frame */
+#define INT_EP_CTL_GPIOS_ (0x000007FF) /* GPIOs Interrupt Enable */
+
+/* Bulk In Delay Register (units of 16.667ns, until ~1092µs) */
+#define BULK_IN_DLY (0x6C)
+
+/* MAC CSRs - MAC Control and Status Registers */
+/* MAC Control Register */
+#define MAC_CR (0x100)
+#define MAC_CR_RXALL_ (0x80000000) /* Receive All Mode */
+#define MAC_CR_RCVOWN_ (0x00800000) /* Disable Receive Own */
+#define MAC_CR_LOOPBK_ (0x00200000) /* Loopback Operation Mode */
+#define MAC_CR_FDPX_ (0x00100000) /* Full Duplex Mode */
+#define MAC_CR_MCPAS_ (0x00080000) /* Pass All Multicast */
+#define MAC_CR_PRMS_ (0x00040000) /* Promiscuous Mode */
+#define MAC_CR_INVFILT_ (0x00020000) /* Inverse Filtering */
+#define MAC_CR_PASSBAD_ (0x00010000) /* Pass Bad Frames */
+#define MAC_CR_HFILT_ (0x00008000) /* Hash Only Filtering Mode */
+#define MAC_CR_HPFILT_ (0x00002000) /* Hash/Perfect Filt. Mode */
+#define MAC_CR_LCOLL_ (0x00001000) /* Late Collision Control */
+#define MAC_CR_BCAST_ (0x00000800) /* Disable Broadcast Frames */
+#define MAC_CR_DISRTY_ (0x00000400) /* Disable Retry */
+#define MAC_CR_PADSTR_ (0x00000100) /* Automatic Pad Stripping */
+#define MAC_CR_BOLMT_MASK (0x000000C0) /* BackOff Limit */
+#define MAC_CR_DFCHK_ (0x00000020) /* Deferral Check */
+#define MAC_CR_TXEN_ (0x00000008) /* Transmitter Enable */
+#define MAC_CR_RXEN_ (0x00000004) /* Receiver Enable */
+
+/* MAC Address High Register */
+#define ADDRH (0x104)
+
+/* MAC Address Low Register */
+#define ADDRL (0x108)
+
+/* Multicast Hash Table High Register */
+#define HASHH (0x10C)
+
+/* Multicast Hash Table Low Register */
+#define HASHL (0x110)
+
+/* MII Access Register */
+#define MII_ADDR (0x114)
+#define MII_WRITE_ (0x02)
+#define MII_BUSY_ (0x01)
+#define MII_READ_ (0x00) /* ~of MII Write bit */
+
+/* MII Data Register */
+#define MII_DATA (0x118)
+
+/* Flow Control Register */
+#define FLOW (0x11C)
+#define FLOW_FCPT_ (0xFFFF0000) /* Pause Time */
+#define FLOW_FCPASS_ (0x00000004) /* Pass Control Frames */
+#define FLOW_FCEN_ (0x00000002) /* Flow Control Enable */
+#define FLOW_FCBSY_ (0x00000001) /* Flow Control Busy */
+
+/* VLAN1 Tag Register */
+#define VLAN1 (0x120)
+
+/* VLAN2 Tag Register */
+#define VLAN2 (0x124)
+
+/* Wake Up Frame Filter Register */
+#define WUFF (0x128)
+#define LAN9500_WUFF_NUM (4)
+#define LAN9500A_WUFF_NUM (8)
+
+/* Wake Up Control and Status Register */
+#define WUCSR (0x12C)
+#define WUCSR_WFF_PTR_RST_ (0x80000000) /* WFrame Filter Pointer Rst */
+#define WUCSR_GUE_ (0x00000200) /* Global Unicast Enable */
+#define WUCSR_WUFR_ (0x00000040) /* Wakeup Frame Received */
+#define WUCSR_MPR_ (0x00000020) /* Magic Packet Received */
+#define WUCSR_WAKE_EN_ (0x00000004) /* Wakeup Frame Enable */
+#define WUCSR_MPEN_ (0x00000002) /* Magic Packet Enable */
+
+/* Checksum Offload Engine Control Register */
+#define COE_CR (0x130)
+#define Tx_COE_EN_ (0x00010000) /* TX Csum Offload Enable */
+#define Rx_COE_MODE_ (0x00000002) /* RX Csum Offload Mode */
+#define Rx_COE_EN_ (0x00000001) /* RX Csum Offload Enable */
+
+/* Vendor-specific PHY Definitions (via MII access) */
+/* EDPD NLP / crossover time configuration (LAN9500A only) */
+#define PHY_EDPD_CONFIG (16)
+#define PHY_EDPD_CONFIG_TX_NLP_EN_ ((u16)0x8000)
+#define PHY_EDPD_CONFIG_TX_NLP_1000_ ((u16)0x0000)
+#define PHY_EDPD_CONFIG_TX_NLP_768_ ((u16)0x2000)
+#define PHY_EDPD_CONFIG_TX_NLP_512_ ((u16)0x4000)
+#define PHY_EDPD_CONFIG_TX_NLP_256_ ((u16)0x6000)
+#define PHY_EDPD_CONFIG_RX_1_NLP_ ((u16)0x1000)
+#define PHY_EDPD_CONFIG_RX_NLP_64_ ((u16)0x0000)
+#define PHY_EDPD_CONFIG_RX_NLP_256_ ((u16)0x0400)
+#define PHY_EDPD_CONFIG_RX_NLP_512_ ((u16)0x0800)
+#define PHY_EDPD_CONFIG_RX_NLP_1000_ ((u16)0x0C00)
+#define PHY_EDPD_CONFIG_EXT_CROSSOVER_ ((u16)0x0001)
+#define PHY_EDPD_CONFIG_DEFAULT (PHY_EDPD_CONFIG_TX_NLP_EN_ | \
+ PHY_EDPD_CONFIG_TX_NLP_768_ | \
+ PHY_EDPD_CONFIG_RX_1_NLP_)
+
+/* Mode Control/Status Register */
+#define PHY_MODE_CTRL_STS (17)
+#define MODE_CTRL_STS_EDPWRDOWN_ ((u16)0x2000)
+#define MODE_CTRL_STS_ENERGYON_ ((u16)0x0002)
+
+/* Control/Status Indication Register */
+#define SPECIAL_CTRL_STS (27)
+#define SPECIAL_CTRL_STS_OVRRD_AMDIX_ ((u16)0x8000)
+#define SPECIAL_CTRL_STS_AMDIX_ENABLE_ ((u16)0x4000)
+#define SPECIAL_CTRL_STS_AMDIX_STATE_ ((u16)0x2000)
+
+/* Interrupt Source Register */
+#define PHY_INT_SRC (29)
+#define PHY_INT_SRC_ENERGY_ON_ ((u16)0x0080)
+#define PHY_INT_SRC_ANEG_COMP_ ((u16)0x0040)
+#define PHY_INT_SRC_REMOTE_FAULT_ ((u16)0x0020)
+#define PHY_INT_SRC_LINK_DOWN_ ((u16)0x0010)
+
+/* Interrupt Mask Register */
+#define PHY_INT_MASK (30)
+#define PHY_INT_MASK_ENERGY_ON_ ((u16)0x0080)
+#define PHY_INT_MASK_ANEG_COMP_ ((u16)0x0040)
+#define PHY_INT_MASK_REMOTE_FAULT_ ((u16)0x0020)
+#define PHY_INT_MASK_LINK_DOWN_ ((u16)0x0010)
+#define PHY_INT_MASK_DEFAULT_ (PHY_INT_MASK_ANEG_COMP_ | \
+ PHY_INT_MASK_LINK_DOWN_)
+/* PHY Special Control/Status Register */
+#define PHY_SPECIAL (31)
+#define PHY_SPECIAL_SPD_ ((u16)0x001C)
+#define PHY_SPECIAL_SPD_10HALF_ ((u16)0x0004)
+#define PHY_SPECIAL_SPD_10FULL_ ((u16)0x0014)
+#define PHY_SPECIAL_SPD_100HALF_ ((u16)0x0008)
+#define PHY_SPECIAL_SPD_100FULL_ ((u16)0x0018)
+
+/* USB Vendor Requests */
+#define USB_VENDOR_REQUEST_WRITE_REGISTER 0xA0
+#define USB_VENDOR_REQUEST_READ_REGISTER 0xA1
+#define USB_VENDOR_REQUEST_GET_STATS 0xA2
+
+/* Interrupt Endpoint status word bitfields */
+#define INT_ENP_MAC_RTO_ ((u32)BIT(18)) /* MAC Reset Time Out */
+#define INT_ENP_TX_STOP_ ((u32)BIT(17)) /* TX Stopped */
+#define INT_ENP_RX_STOP_ ((u32)BIT(16)) /* RX Stopped */
+#define INT_ENP_PHY_INT_ ((u32)BIT(15)) /* PHY Interrupt */
+#define INT_ENP_TXE_ ((u32)BIT(14)) /* TX Error */
+#define INT_ENP_TDFU_ ((u32)BIT(13)) /* TX FIFO Underrun */
+#define INT_ENP_TDFO_ ((u32)BIT(12)) /* TX FIFO Overrun */
+#define INT_ENP_RXDF_ ((u32)BIT(11)) /* RX Dropped Frame */
+
+#endif /* _SMSC95XX_H */
diff --git a/drivers/usb/storage/objects.mk b/drivers/usb/storage/objects.mk
index f6fc310..35980bc 100644
--- a/drivers/usb/storage/objects.mk
+++ b/drivers/usb/storage/objects.mk
@@ -22,4 +22,4 @@
# */

drivers-objs-$(CONFIG_USB_STORAGE)+= usb/storage/usb_storage.o
-
+drivers-objs-$(CONFIG_USB_ETHER_SMSC95XX)+= usb/devices/smsc95xx.o
diff --git a/drivers/usb/storage/openconf.cfg b/drivers/usb/storage/openconf.cfg
index 9ba238e..47ff51e 100644
--- a/drivers/usb/storage/openconf.cfg
+++ b/drivers/usb/storage/openconf.cfg
@@ -33,3 +33,15 @@ config CONFIG_USB_STORAGE
help
USB mass storage device driver.

+config CONFIG_USB_HOST_ETHER
+ tristate "USB ethernet support"
+ depends on CONFIG_USB && CONFIG_NET
+ default n
+
+if CONFIG_USB_HOST_ETHER
+config CONFIG_USB_ETHER_SMSC95XX
+ tristate "SMSC95xx device driver"
+ depends on CONFIG_USB_HOST_ETHER
+ default n
+endif
+
diff --git a/emulators/net/virtio_net.c b/emulators/net/virtio_net.c
index ad88086..9f92461 100644
--- a/emulators/net/virtio_net.c
+++ b/emulators/net/virtio_net.c
@@ -406,6 +406,11 @@ static int virtio_net_connect(struct vmm_virtio_device *dev,
}
}

+ attr = of_get_mac_address(dev->edev->node);
+ if (attr) {
+ memcpy(vmm_netport_mac(ndev->port), attr, ETH_ALEN);
+ }
+
rc = vmm_netport_register(ndev->port);
if (rc) {
vmm_free(ndev->vqs);
--
2.1.4

Anup Patel

unread,
Jun 20, 2017, 12:19:03 PM6/20/17
to Xvisor Devel, Wayne Kim, Ashutosh Sharma
Hi Wayne,

First of all thanks for this patch.

Unfortunately, Ashutosh (CCed) here has also ported
smsc95xx driver. He has also got it working. He currently
get 80Mbps.

The issue is our DWC2 USB host driver is polling based
which needs to be moved to interrupt based. Once this
is done Ashutosh will provide fully optimized smsc95xxx
driver which provides 100Mbps with minimum CPU
utilization.

It was my mistake to ask Ashutosh to submit only
after smsc95xxx is fully optimized.

I request you to wait little more till we get
smsc95xxx driver from Ashutosh.

What is your take on this?

Regards,
Anup

Dojung Kim

unread,
Jun 20, 2017, 8:53:15 PM6/20/17
to Anup Patel, Xvisor Devel, Ashutosh Sharma
Hi, Anup.

Thank you for reviewing my commits.

It is good to hear from you that the driver is moved to interrupt based.
actually I was also struggling to make it work based on the interrupt reception.
I thought it was my fault to setting some registers on the device.

Anyway, I look forward to have a chance to see the code that Mr. Ashutosh is written.

Regards,
Wayne.

Anup Patel

unread,
Jul 1, 2017, 12:55:52 AM7/1/17
to Dojung Kim, Xvisor Devel, Ashutosh Sharma
Hi Wayne,

We have initial working implmentation of smsc95xx
merged in Xvisor-next.

You can play with it and possibly send improvment
patches.

Meanwhile, we are actively trying to make DWC2
interrupt based. After which we might have some
changes in smsc95xx driver.

Regards,
Anup
Reply all
Reply to author
Forward
0 new messages