diff options
author | Simon Glass | 2015-07-06 16:47:54 -0600 |
---|---|---|
committer | Simon Glass | 2015-07-21 17:39:41 -0600 |
commit | fbc4b8af469459425de72530dbded0ddbe157004 (patch) | |
tree | b7313ec7abd0974f8f6e507326bf4f5d2c987385 /drivers/usb/eth | |
parent | 2b53b078d786a3e0485dd9a85949d49501137a23 (diff) |
dm: usb: eth: Add driver-model support to the asix driver
This USB Ethernet driver is quite widely use. Allow it to work with
CONFIG_DM_ETH enabled. Most of the code remains common but there is a new
packet receive flow which is handled specially.
Signed-off-by: Simon Glass <sjg@chromium.org>
Diffstat (limited to 'drivers/usb/eth')
-rw-r--r-- | drivers/usb/eth/asix.c | 237 |
1 files changed, 216 insertions, 21 deletions
diff --git a/drivers/usb/eth/asix.c b/drivers/usb/eth/asix.c index c8697ae78db..72ec41ea896 100644 --- a/drivers/usb/eth/asix.c +++ b/drivers/usb/eth/asix.c @@ -5,11 +5,11 @@ */ #include <common.h> +#include <dm.h> #include <usb.h> +#include <malloc.h> #include <linux/mii.h> #include "usb_ether.h" -#include <malloc.h> - /* ASIX AX8817X based USB 2.0 Ethernet Devices */ @@ -92,14 +92,20 @@ #define FLAG_TYPE_AX88772B (1U << 2) #define FLAG_EEPROM_MAC (1U << 3) /* initial mac address in eeprom */ -/* local vars */ -static int curr_eth_dev; /* index for name of next device detected */ /* 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,13 +290,12 @@ static int asix_write_gpio(struct ueth_data *dev, u16 value, int sleep) return ret; } -static int asix_write_hwaddr(struct eth_device *eth) +static int asix_write_hwaddr_common(struct ueth_data *dev, uint8_t *enetaddr) { - struct ueth_data *dev = (struct ueth_data *)eth->priv; int ret; ALLOC_CACHE_ALIGN_BUFFER(unsigned char, buf, ETH_ALEN); - memcpy(buf, eth->enetaddr, 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) @@ -325,12 +330,11 @@ static int mii_nway_restart(struct ueth_data *dev) return r; } -static int asix_read_mac(struct eth_device *eth) +static int asix_read_mac_common(struct ueth_data *dev, + struct asix_private *priv, uint8_t *enetaddr) { - struct ueth_data *dev = (struct ueth_data *)eth->priv; - struct asix_private *priv = (struct asix_private *)dev->dev_priv; - int i; 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++) { @@ -339,7 +343,7 @@ static int asix_read_mac(struct eth_device *eth) debug("Failed to read SROM address 04h.\n"); return -1; } - memcpy((eth->enetaddr + i * 2), buf, 2); + memcpy(enetaddr + i * 2, buf, 2); } } else { if (asix_read_cmd(dev, AX_CMD_READ_NODE_ID, 0, 0, ETH_ALEN, buf) @@ -347,7 +351,7 @@ static int asix_read_mac(struct eth_device *eth) debug("Failed to read MAC address.\n"); return -1; } - memcpy(eth->enetaddr, buf, ETH_ALEN); + memcpy(enetaddr, buf, ETH_ALEN); } return 0; @@ -414,12 +418,8 @@ static int asix_basic_reset(struct ueth_data *dev) return 0; } -/* - * Asix callbacks - */ -static int asix_init(struct eth_device *eth, bd_t *bd) +static int asix_init_common(struct ueth_data *dev) { - struct ueth_data *dev = (struct ueth_data *)eth->priv; int timeout = 0; #define TIMEOUT_RESOLUTION 50 /* ms */ int link_detected; @@ -452,9 +452,8 @@ 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; @@ -481,6 +480,24 @@ static int asix_send(struct eth_device *eth, void *packet, int length) 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); +} + +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; @@ -552,6 +569,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 */ @@ -694,9 +718,180 @@ int asix_eth_get_info(struct usb_device *dev, struct ueth_data *ss, return 0; /* Get the MAC address */ - if (asix_read_mac(eth)) + 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 asix_private *priv = dev_get_priv(dev); + + return asix_init_common(&priv->ueth); +} + +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 |