net: asix: Fix ASIX 88772B with driver model
[pandora-u-boot.git] / drivers / usb / eth / asix.c
index 50cbbbd..1c6e967 100644 (file)
@@ -1,40 +1,31 @@
 /*
  * Copyright (c) 2011 The Chromium OS Authors.
- * See file CREDITS for list of people who contributed to this
- * project.
  *
- * 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.
+ * Patched for AX88772B by Antmicro Ltd <www.antmicro.com>
  *
- * 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, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
- * MA 02111-1307 USA
+ * SPDX-License-Identifier:    GPL-2.0+
  */
 
 #include <common.h>
+#include <dm.h>
 #include <usb.h>
+#include <malloc.h>
+#include <memalign.h>
 #include <linux/mii.h>
 #include "usb_ether.h"
 
-
 /* ASIX AX8817X based USB 2.0 Ethernet Devices */
 
 #define AX_CMD_SET_SW_MII              0x06
 #define AX_CMD_READ_MII_REG            0x07
 #define AX_CMD_WRITE_MII_REG           0x08
 #define AX_CMD_SET_HW_MII              0x0a
+#define AX_CMD_READ_EEPROM             0x0b
 #define AX_CMD_READ_RX_CTL             0x0f
 #define AX_CMD_WRITE_RX_CTL            0x10
 #define AX_CMD_WRITE_IPG0              0x12
 #define AX_CMD_READ_NODE_ID            0x13
+#define AX_CMD_WRITE_NODE_ID   0x14
 #define AX_CMD_READ_PHY_ID             0x19
 #define AX_CMD_WRITE_MEDIUM_MODE       0x1b
 #define AX_CMD_WRITE_GPIOS             0x1f
 #define AX_RX_URB_SIZE 2048
 #define PHY_CONNECT_TIMEOUT 5000
 
+/* asix_flags defines */
+#define FLAG_NONE                      0
+#define FLAG_TYPE_AX88172      (1U << 0)
+#define FLAG_TYPE_AX88772      (1U << 1)
+#define FLAG_TYPE_AX88772B     (1U << 2)
+#define FLAG_EEPROM_MAC                (1U << 3) /* initial mac address in eeprom */
+
+
+/* driver private */
+struct asix_private {
+       int flags;
+#ifdef CONFIG_DM_ETH
+       struct ueth_data ueth;
+#endif
+};
+
+#ifndef CONFIG_DM_ETH
 /* local vars */
 static int curr_eth_dev; /* index for name of next device detected */
+#endif
 
 /*
  * Asix infrastructure commands
@@ -284,6 +293,20 @@ static int asix_write_gpio(struct ueth_data *dev, u16 value, int sleep)
        return ret;
 }
 
+static int asix_write_hwaddr_common(struct ueth_data *dev, uint8_t *enetaddr)
+{
+       int ret;
+       ALLOC_CACHE_ALIGN_BUFFER(unsigned char, buf, ETH_ALEN);
+
+       memcpy(buf, enetaddr, ETH_ALEN);
+
+       ret = asix_write_cmd(dev, AX_CMD_WRITE_NODE_ID, 0, 0, ETH_ALEN, buf);
+       if (ret < 0)
+               debug("Failed to set MAC address: %02x\n", ret);
+
+       return ret;
+}
+
 /*
  * mii commands
  */
@@ -310,6 +333,33 @@ static int mii_nway_restart(struct ueth_data *dev)
        return r;
 }
 
+static int asix_read_mac_common(struct ueth_data *dev,
+                               struct asix_private *priv, uint8_t *enetaddr)
+{
+       ALLOC_CACHE_ALIGN_BUFFER(unsigned char, buf, ETH_ALEN);
+       int i;
+
+       if (priv->flags & FLAG_EEPROM_MAC) {
+               for (i = 0; i < (ETH_ALEN >> 1); i++) {
+                       if (asix_read_cmd(dev, AX_CMD_READ_EEPROM,
+                                         0x04 + i, 0, 2, buf) < 0) {
+                               debug("Failed to read SROM address 04h.\n");
+                               return -1;
+                       }
+                       memcpy(enetaddr + i * 2, buf, 2);
+               }
+       } else {
+               if (asix_read_cmd(dev, AX_CMD_READ_NODE_ID, 0, 0, ETH_ALEN, buf)
+                    < 0) {
+                       debug("Failed to read MAC address.\n");
+                       return -1;
+               }
+               memcpy(enetaddr, buf, ETH_ALEN);
+       }
+
+       return 0;
+}
+
 static int asix_basic_reset(struct ueth_data *dev)
 {
        int embd_phy;
@@ -349,61 +399,42 @@ static int asix_basic_reset(struct ueth_data *dev)
        rx_ctl = asix_read_rx_ctl(dev);
        debug("RX_CTL is 0x%04x setting to 0x0000\n", rx_ctl);
 
-       return 0;
-}
-
-/*
- * Asix callbacks
- */
-static int asix_init(struct eth_device *eth, bd_t *bd)
-{
-       struct ueth_data        *dev = (struct ueth_data *)eth->priv;
-       int timeout = 0;
-#define TIMEOUT_RESOLUTION 50  /* ms */
-       int link_detected;
-
-       debug("** %s()\n", __func__);
-
-       /* Get the MAC address */
-       if (asix_read_cmd(dev, AX_CMD_READ_NODE_ID,
-                               0, 0, ETH_ALEN, buf) < 0) {
-               debug("Failed to read MAC address.\n");
-               goto out_err;
-       }
-       memcpy(eth->enetaddr, buf, ETH_ALEN);
-       debug("MAC %02x:%02x:%02x:%02x:%02x:%02x\n",
-               eth->enetaddr[0], eth->enetaddr[1],
-               eth->enetaddr[2], eth->enetaddr[3],
-               eth->enetaddr[4], eth->enetaddr[5]);
-
        dev->phy_id = asix_get_phy_addr(dev);
        if (dev->phy_id < 0)
                debug("Failed to read phy id\n");
 
-       if (asix_sw_reset(dev, AX_SWRESET_PRL) < 0)
-               goto out_err;
-
-       if (asix_sw_reset(dev, AX_SWRESET_IPRL | AX_SWRESET_PRL) < 0)
-               goto out_err;
-
        asix_mdio_write(dev, dev->phy_id, MII_BMCR, BMCR_RESET);
        asix_mdio_write(dev, dev->phy_id, MII_ADVERTISE,
                        ADVERTISE_ALL | ADVERTISE_CSMA);
        mii_nway_restart(dev);
 
        if (asix_write_medium_mode(dev, AX88772_MEDIUM_DEFAULT) < 0)
-               goto out_err;
+               return -1;
 
        if (asix_write_cmd(dev, AX_CMD_WRITE_IPG0,
                                AX88772_IPG0_DEFAULT | AX88772_IPG1_DEFAULT,
                                AX88772_IPG2_DEFAULT, 0, NULL) < 0) {
                debug("Write IPG,IPG1,IPG2 failed\n");
-               goto out_err;
+               return -1;
        }
 
+       return 0;
+}
+
+static int asix_init_common(struct ueth_data *dev, uint8_t *enetaddr)
+{
+       int timeout = 0;
+#define TIMEOUT_RESOLUTION 50  /* ms */
+       int link_detected;
+
+       debug("** %s()\n", __func__);
+
        if (asix_write_rx_ctl(dev, AX_DEFAULT_RX_CTL) < 0)
                goto out_err;
 
+       if (asix_write_hwaddr_common(dev, enetaddr) < 0)
+               goto out_err;
+
        do {
                link_detected = asix_mdio_read(dev, dev->phy_id, MII_BMSR) &
                        BMSR_LSTATUS;
@@ -422,14 +453,19 @@ static int asix_init(struct eth_device *eth, bd_t *bd)
                goto out_err;
        }
 
+       /*
+        * Wait some more to avoid timeout on first transfer
+        * (e.g. EHCI timed out on TD - token=0x8008d80)
+        */
+       mdelay(25);
+
        return 0;
 out_err:
        return -1;
 }
 
-static int asix_send(struct eth_device *eth, void *packet, int length)
+static int asix_send_common(struct ueth_data *dev, void *packet, int length)
 {
-       struct ueth_data *dev = (struct ueth_data *)eth->priv;
        int err;
        u32 packet_len;
        int actual_len;
@@ -443,8 +479,6 @@ static int asix_send(struct eth_device *eth, void *packet, int length)
 
        memcpy(msg, &packet_len, sizeof(packet_len));
        memcpy(msg + sizeof(packet_len), (void *)packet, length);
-       if (length & 1)
-               length++;
 
        err = usb_bulk_msg(dev->pusb_dev,
                                usb_sndbulkpipe(dev->pusb_dev, dev->ep_out),
@@ -452,12 +486,30 @@ static int asix_send(struct eth_device *eth, void *packet, int length)
                                length + sizeof(packet_len),
                                &actual_len,
                                USB_BULK_SEND_TIMEOUT);
-       debug("Tx: len = %u, actual = %u, err = %d\n",
+       debug("Tx: len = %zu, actual = %u, err = %d\n",
                        length + sizeof(packet_len), actual_len, err);
 
        return err;
 }
 
+#ifndef CONFIG_DM_ETH
+/*
+ * Asix callbacks
+ */
+static int asix_init(struct eth_device *eth, bd_t *bd)
+{
+       struct ueth_data *dev = (struct ueth_data *)eth->priv;
+
+       return asix_init_common(dev, eth->enetaddr);
+}
+
+static int asix_send(struct eth_device *eth, void *packet, int length)
+{
+       struct ueth_data *dev = (struct ueth_data *)eth->priv;
+
+       return asix_send_common(dev, packet, length);
+}
+
 static int asix_recv(struct eth_device *eth)
 {
        struct ueth_data *dev = (struct ueth_data *)eth->priv;
@@ -498,20 +550,21 @@ static int asix_recv(struct eth_device *eth)
                }
                memcpy(&packet_len, buf_ptr, sizeof(packet_len));
                le32_to_cpus(&packet_len);
-               if (((packet_len >> 16) ^ 0xffff) != (packet_len & 0xffff)) {
+               if (((~packet_len >> 16) & 0x7ff) != (packet_len & 0x7ff)) {
                        debug("Rx: malformed packet length: %#x (%#x:%#x)\n",
-                             packet_len, (packet_len >> 16) ^ 0xffff,
-                             packet_len & 0xffff);
+                             packet_len, (~packet_len >> 16) & 0x7ff,
+                             packet_len & 0x7ff);
                        return -1;
                }
-               packet_len = packet_len & 0xffff;
+               packet_len = packet_len & 0x7ff;
                if (packet_len > actual_len - sizeof(packet_len)) {
                        debug("Rx: too large packet: %d\n", packet_len);
                        return -1;
                }
 
                /* Notify net stack */
-               NetReceive(buf_ptr + sizeof(packet_len), packet_len);
+               net_process_received_packet(buf_ptr + sizeof(packet_len),
+                                           packet_len);
 
                /* Adjust for next iteration. Packets are padded to 16-bits */
                if (packet_len & 1)
@@ -528,6 +581,13 @@ static void asix_halt(struct eth_device *eth)
        debug("** %s()\n", __func__);
 }
 
+static int asix_write_hwaddr(struct eth_device *eth)
+{
+       struct ueth_data *dev = (struct ueth_data *)eth->priv;
+
+       return asix_write_hwaddr_common(dev, eth->enetaddr);
+}
+
 /*
  * Asix probing functions
  */
@@ -539,19 +599,26 @@ void asix_eth_before_probe(void)
 struct asix_dongle {
        unsigned short vendor;
        unsigned short product;
+       int flags;
 };
 
-static struct asix_dongle asix_dongles[] = {
-       { 0x05ac, 0x1402 },     /* Apple USB Ethernet Adapter */
-       { 0x07d1, 0x3c05 },     /* D-Link DUB-E100 H/W Ver B1 */
-       { 0x0b95, 0x772a },     /* Cables-to-Go USB Ethernet Adapter */
-       { 0x0b95, 0x7720 },     /* Trendnet TU2-ET100 V3.0R */
-       { 0x0b95, 0x1720 },     /* SMC */
-       { 0x0db0, 0xa877 },     /* MSI - ASIX 88772a */
-       { 0x13b1, 0x0018 },     /* Linksys 200M v2.1 */
-       { 0x1557, 0x7720 },     /* 0Q0 cable ethernet */
-       { 0x2001, 0x3c05 },     /* DLink DUB-E100 H/W Ver B1 Alternate */
-       { 0x0000, 0x0000 }      /* END - Do not remove */
+static const struct asix_dongle asix_dongles[] = {
+       { 0x05ac, 0x1402, FLAG_TYPE_AX88772 },  /* Apple USB Ethernet Adapter */
+       { 0x07d1, 0x3c05, FLAG_TYPE_AX88772 },  /* D-Link DUB-E100 H/W Ver B1 */
+       { 0x2001, 0x1a02, FLAG_TYPE_AX88772 },  /* D-Link DUB-E100 H/W Ver C1 */
+       /* Cables-to-Go USB Ethernet Adapter */
+       { 0x0b95, 0x772a, FLAG_TYPE_AX88772 },
+       { 0x0b95, 0x7720, FLAG_TYPE_AX88772 },  /* Trendnet TU2-ET100 V3.0R */
+       { 0x0b95, 0x1720, FLAG_TYPE_AX88172 },  /* SMC */
+       { 0x0db0, 0xa877, FLAG_TYPE_AX88772 },  /* MSI - ASIX 88772a */
+       { 0x13b1, 0x0018, FLAG_TYPE_AX88172 },  /* Linksys 200M v2.1 */
+       { 0x1557, 0x7720, FLAG_TYPE_AX88772 },  /* 0Q0 cable ethernet */
+       /* DLink DUB-E100 H/W Ver B1 Alternate */
+       { 0x2001, 0x3c05, FLAG_TYPE_AX88772 },
+       /* ASIX 88772B */
+       { 0x0b95, 0x772b, FLAG_TYPE_AX88772B | FLAG_EEPROM_MAC },
+       { 0x0b95, 0x7e2b, FLAG_TYPE_AX88772B },
+       { 0x0000, 0x0000, FLAG_NONE }   /* END - Do not remove */
 };
 
 /* Probe to see if a new device is actually an asix device */
@@ -560,6 +627,7 @@ int asix_eth_probe(struct usb_device *dev, unsigned int ifnum,
 {
        struct usb_interface *iface;
        struct usb_interface_descriptor *iface_desc;
+       int ep_in_found = 0, ep_out_found = 0;
        int i;
 
        /* let's examine the device now */
@@ -588,6 +656,13 @@ int asix_eth_probe(struct usb_device *dev, unsigned int ifnum,
        ss->subclass = iface_desc->bInterfaceSubClass;
        ss->protocol = iface_desc->bInterfaceProtocol;
 
+       /* alloc driver private */
+       ss->dev_priv = calloc(1, sizeof(struct asix_private));
+       if (!ss->dev_priv)
+               return 0;
+
+       ((struct asix_private *)ss->dev_priv)->flags = asix_dongles[i].flags;
+
        /*
         * We are expecting a minimum of 3 endpoints - in, out (bulk), and
         * int. We will ignore any others.
@@ -596,13 +671,20 @@ int asix_eth_probe(struct usb_device *dev, unsigned int ifnum,
                /* 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;
+                       u8 ep_addr = iface->ep_desc[i].bEndpointAddress;
+                       if (ep_addr & USB_DIR_IN) {
+                               if (!ep_in_found) {
+                                       ss->ep_in = ep_addr &
+                                               USB_ENDPOINT_NUMBER_MASK;
+                                       ep_in_found = 1;
+                               }
+                       } else {
+                               if (!ep_out_found) {
+                                       ss->ep_out = ep_addr &
+                                               USB_ENDPOINT_NUMBER_MASK;
+                                       ep_out_found = 1;
+                               }
+                       }
                }
 
                /* is it an interrupt endpoint? */
@@ -629,6 +711,8 @@ int asix_eth_probe(struct usb_device *dev, unsigned int ifnum,
 int asix_eth_get_info(struct usb_device *dev, struct ueth_data *ss,
                                struct eth_device *eth)
 {
+       struct asix_private *priv = (struct asix_private *)ss->dev_priv;
+
        if (!eth) {
                debug("%s: missing parameter.\n", __func__);
                return 0;
@@ -638,10 +722,189 @@ int asix_eth_get_info(struct usb_device *dev, struct ueth_data *ss,
        eth->send = asix_send;
        eth->recv = asix_recv;
        eth->halt = asix_halt;
+       if (!(priv->flags & FLAG_TYPE_AX88172))
+               eth->write_hwaddr = asix_write_hwaddr;
        eth->priv = ss;
 
        if (asix_basic_reset(ss))
                return 0;
 
+       /* Get the MAC address */
+       if (asix_read_mac_common(ss, priv, eth->enetaddr))
+               return 0;
+       debug("MAC %pM\n", eth->enetaddr);
+
        return 1;
 }
+#endif
+
+#ifdef CONFIG_DM_ETH
+static int asix_eth_start(struct udevice *dev)
+{
+       struct eth_pdata *pdata = dev_get_platdata(dev);
+       struct asix_private *priv = dev_get_priv(dev);
+
+       return asix_init_common(&priv->ueth, pdata->enetaddr);
+}
+
+void asix_eth_stop(struct udevice *dev)
+{
+       debug("** %s()\n", __func__);
+}
+
+int asix_eth_send(struct udevice *dev, void *packet, int length)
+{
+       struct asix_private *priv = dev_get_priv(dev);
+
+       return asix_send_common(&priv->ueth, packet, length);
+}
+
+int asix_eth_recv(struct udevice *dev, int flags, uchar **packetp)
+{
+       struct asix_private *priv = dev_get_priv(dev);
+       struct ueth_data *ueth = &priv->ueth;
+       uint8_t *ptr;
+       int ret, len;
+       u32 packet_len;
+
+       len = usb_ether_get_rx_bytes(ueth, &ptr);
+       debug("%s: first try, len=%d\n", __func__, len);
+       if (!len) {
+               if (!(flags & ETH_RECV_CHECK_DEVICE))
+                       return -EAGAIN;
+               ret = usb_ether_receive(ueth, AX_RX_URB_SIZE);
+               if (ret == -EAGAIN)
+                       return ret;
+
+               len = usb_ether_get_rx_bytes(ueth, &ptr);
+               debug("%s: second try, len=%d\n", __func__, len);
+       }
+
+       /*
+        * 1st 4 bytes contain the length of the actual data as two
+        * complementary 16-bit words. Extract the length of the data.
+        */
+       if (len < sizeof(packet_len)) {
+               debug("Rx: incomplete packet length\n");
+               goto err;
+       }
+       memcpy(&packet_len, ptr, sizeof(packet_len));
+       le32_to_cpus(&packet_len);
+       if (((~packet_len >> 16) & 0x7ff) != (packet_len & 0x7ff)) {
+               debug("Rx: malformed packet length: %#x (%#x:%#x)\n",
+                     packet_len, (~packet_len >> 16) & 0x7ff,
+                     packet_len & 0x7ff);
+               goto err;
+       }
+       packet_len = packet_len & 0x7ff;
+       if (packet_len > len - sizeof(packet_len)) {
+               debug("Rx: too large packet: %d\n", packet_len);
+               goto err;
+       }
+
+       *packetp = ptr + sizeof(packet_len);
+       return packet_len;
+
+err:
+       usb_ether_advance_rxbuf(ueth, -1);
+       return -EINVAL;
+}
+
+static int asix_free_pkt(struct udevice *dev, uchar *packet, int packet_len)
+{
+       struct asix_private *priv = dev_get_priv(dev);
+
+       if (packet_len & 1)
+               packet_len++;
+       usb_ether_advance_rxbuf(&priv->ueth, sizeof(u32) + packet_len);
+
+       return 0;
+}
+
+int asix_write_hwaddr(struct udevice *dev)
+{
+       struct eth_pdata *pdata = dev_get_platdata(dev);
+       struct asix_private *priv = dev_get_priv(dev);
+
+       if (priv->flags & FLAG_TYPE_AX88172)
+               return -ENOSYS;
+
+       return asix_write_hwaddr_common(&priv->ueth, pdata->enetaddr);
+}
+
+static int asix_eth_probe(struct udevice *dev)
+{
+       struct eth_pdata *pdata = dev_get_platdata(dev);
+       struct asix_private *priv = dev_get_priv(dev);
+       struct ueth_data *ss = &priv->ueth;
+       int ret;
+
+       priv->flags = dev->driver_data;
+       ret = usb_ether_register(dev, ss, AX_RX_URB_SIZE);
+       if (ret)
+               return ret;
+
+       ret = asix_basic_reset(ss);
+       if (ret)
+               goto err;
+
+       /* Get the MAC address */
+       ret = asix_read_mac_common(ss, priv, pdata->enetaddr);
+       if (ret)
+               goto err;
+       debug("MAC %pM\n", pdata->enetaddr);
+
+       return 0;
+
+err:
+       return usb_ether_deregister(ss);
+}
+
+static const struct eth_ops asix_eth_ops = {
+       .start  = asix_eth_start,
+       .send   = asix_eth_send,
+       .recv   = asix_eth_recv,
+       .free_pkt = asix_free_pkt,
+       .stop   = asix_eth_stop,
+       .write_hwaddr = asix_write_hwaddr,
+};
+
+U_BOOT_DRIVER(asix_eth) = {
+       .name   = "asix_eth",
+       .id     = UCLASS_ETH,
+       .probe = asix_eth_probe,
+       .ops    = &asix_eth_ops,
+       .priv_auto_alloc_size = sizeof(struct asix_private),
+       .platdata_auto_alloc_size = sizeof(struct eth_pdata),
+};
+
+static const struct usb_device_id asix_eth_id_table[] = {
+       /* Apple USB Ethernet Adapter */
+       { USB_DEVICE(0x05ac, 0x1402), .driver_info = FLAG_TYPE_AX88772 },
+       /* D-Link DUB-E100 H/W Ver B1 */
+       { USB_DEVICE(0x07d1, 0x3c05), .driver_info = FLAG_TYPE_AX88772 },
+       /* D-Link DUB-E100 H/W Ver C1 */
+       { USB_DEVICE(0x2001, 0x1a02), .driver_info = FLAG_TYPE_AX88772 },
+       /* Cables-to-Go USB Ethernet Adapter */
+       { USB_DEVICE(0x0b95, 0x772a), .driver_info = FLAG_TYPE_AX88772 },
+       /* Trendnet TU2-ET100 V3.0R */
+       { USB_DEVICE(0x0b95, 0x7720), .driver_info = FLAG_TYPE_AX88772 },
+       /* SMC */
+       { USB_DEVICE(0x0b95, 0x1720), .driver_info = FLAG_TYPE_AX88172 },
+       /* MSI - ASIX 88772a */
+       { USB_DEVICE(0x0db0, 0xa877), .driver_info = FLAG_TYPE_AX88772 },
+       /* Linksys 200M v2.1 */
+       { USB_DEVICE(0x13b1, 0x0018), .driver_info = FLAG_TYPE_AX88172 },
+       /* 0Q0 cable ethernet */
+       { USB_DEVICE(0x1557, 0x7720), .driver_info = FLAG_TYPE_AX88772 },
+       /* DLink DUB-E100 H/W Ver B1 Alternate */
+       { USB_DEVICE(0x2001, 0x3c05), .driver_info = FLAG_TYPE_AX88772 },
+       /* ASIX 88772B */
+       { USB_DEVICE(0x0b95, 0x772b),
+               .driver_info = FLAG_TYPE_AX88772B | FLAG_EEPROM_MAC },
+       { USB_DEVICE(0x0b95, 0x7e2b), .driver_info = FLAG_TYPE_AX88772B },
+       { }             /* Terminating entry */
+};
+
+U_BOOT_USB_DEVICE(asix_eth, asix_eth_id_table);
+#endif