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>
This commit is contained in:
Simon Glass 2015-07-06 16:47:54 -06:00
parent 2b53b078d7
commit fbc4b8af46

View file

@ -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