* Joe Hershberger, National Instruments
*/
+#define LOG_CATEGORY UCLASS_ETH
+
#include <common.h>
+#include <bootdev.h>
#include <bootstage.h>
#include <dm.h>
#include <env.h>
#include <log.h>
#include <net.h>
+#include <nvmem.h>
+#include <asm/global_data.h>
#include <dm/device-internal.h>
#include <dm/uclass-internal.h>
#include <net/pcap.h>
*/
struct eth_device_priv {
enum eth_state_t state;
+ bool running;
};
/**
* struct eth_uclass_priv - The structure attached to the uclass itself
*
* @current: The Ethernet device that the network functions are using
+ * @no_bootdevs: true to skip binding Ethernet bootdevs (this is a negative flag
+ * so that the default value enables it)
*/
struct eth_uclass_priv {
struct udevice *current;
+ bool no_bootdevs;
};
/* eth_errno - This stores the most recent failure code from DM functions */
static int eth_errno;
+/* board-specific Ethernet Interface initializations. */
+__weak int board_interface_eth_init(struct udevice *dev,
+ phy_interface_t interface_type)
+{
+ return 0;
+}
+
static struct eth_uclass_priv *eth_get_uclass_priv(void)
{
struct uclass *uc;
return NULL;
assert(uc);
- return uc->priv;
+ return uclass_get_priv(uc);
+}
+
+void eth_set_enable_bootdevs(bool enable)
+{
+ struct eth_uclass_priv *priv = eth_get_uclass_priv();
+
+ if (priv)
+ priv->no_bootdevs = !enable;
}
void eth_set_current_to_next(void)
/*
* Typically this will simply return the active device.
* In the case where the most recent active device was unset, this will attempt
- * to return the first device. If that device doesn't exist or fails to probe,
- * this function will return NULL.
+ * to return the device with sequence id 0 (which can be configured by the
+ * device tree). If this fails, fall back to just getting the first device.
+ * The latter is non-deterministic and depends on the order of the probing.
+ * If that device doesn't exist or fails to probe, this function will return
+ * NULL.
*/
struct udevice *eth_get_dev(void)
{
if (!uc_priv)
return NULL;
- if (!uc_priv->current)
- eth_errno = uclass_first_device(UCLASS_ETH,
- &uc_priv->current);
+ if (!uc_priv->current) {
+ eth_errno = uclass_get_device_by_seq(UCLASS_ETH, 0,
+ &uc_priv->current);
+ if (eth_errno)
+ eth_errno = uclass_first_device_err(UCLASS_ETH,
+ &uc_priv->current);
+ if (eth_errno)
+ uc_priv->current = NULL;
+ }
return uc_priv->current;
}
/* Must be longer than 3 to be an alias */
if (!strncmp(devname, "eth", len) && strlen(devname) > len) {
startp = devname + len;
- seq = simple_strtoul(startp, &endp, 10);
+ seq = dectoul(startp, &endp);
}
ret = uclass_get(UCLASS_ETH, &uc);
uclass_foreach_dev(it, uc) {
/*
- * We need the seq to be valid, so try to probe it.
- * If the probe fails, the seq will not match since it will be
- * -1 instead of what we are looking for.
* We don't care about errors from probe here. Either they won't
* match an alias or it will match a literal name and we'll pick
* up the error when we try to probe again in eth_set_dev().
continue;
/* Check for the name or the sequence number to match */
if (strcmp(it->name, devname) == 0 ||
- (endp > startp && it->seq == seq))
+ (endp > startp && dev_seq(it) == seq))
return it;
}
struct eth_pdata *pdata;
if (eth_get_dev()) {
- pdata = eth_get_dev()->platdata;
+ pdata = dev_get_plat(eth_get_dev());
return pdata->enetaddr;
}
if (!current || !device_active(current))
return -EINVAL;
- priv = current->uclass_priv;
+ priv = dev_get_uclass_priv(current);
priv->state = ETH_STATE_ACTIVE;
return 0;
if (!current || !device_active(current))
return;
- priv = current->uclass_priv;
+ priv = dev_get_uclass_priv(current);
priv->state = ETH_STATE_PASSIVE;
}
int eth_get_dev_index(void)
{
if (eth_get_dev())
- return eth_get_dev()->seq;
+ return dev_seq(eth_get_dev());
return -1;
}
return -EINVAL;
/* seq is valid since the device is active */
- if (eth_get_ops(dev)->write_hwaddr && !eth_mac_skip(dev->seq)) {
- pdata = dev->platdata;
+ if (eth_get_ops(dev)->write_hwaddr && !eth_mac_skip(dev_seq(dev))) {
+ pdata = dev_get_plat(dev);
if (!is_valid_ethaddr(pdata->enetaddr)) {
printf("\nError: %s address %pM illegal value\n",
dev->name, pdata->enetaddr);
struct udevice *dev;
/* look for an index after "eth" */
- index = simple_strtoul(name + 3, NULL, 10);
+ index = dectoul(name + 3, NULL);
- retval = uclass_find_device_by_seq(UCLASS_ETH, index, false, &dev);
+ retval = uclass_find_device_by_seq(UCLASS_ETH, index, &dev);
if (!retval) {
- struct eth_pdata *pdata = dev->platdata;
+ struct eth_pdata *pdata = dev_get_plat(dev);
switch (op) {
case env_op_create:
case env_op_overwrite:
ret = eth_get_ops(current)->start(current);
if (ret >= 0) {
struct eth_device_priv *priv =
- current->uclass_priv;
+ dev_get_uclass_priv(current);
priv->state = ETH_STATE_ACTIVE;
+ priv->running = true;
return 0;
}
} else {
struct eth_device_priv *priv;
current = eth_get_dev();
- if (!current || !eth_is_active(current))
+ if (!current)
+ return;
+
+ priv = dev_get_uclass_priv(current);
+ if (!priv || !priv->running)
return;
eth_get_ops(current)->stop(current);
- priv = current->uclass_priv;
- if (priv)
- priv->state = ETH_STATE_PASSIVE;
+ priv->state = ETH_STATE_PASSIVE;
+ priv->running = false;
}
int eth_is_active(struct udevice *dev)
bootstage_mark(BOOTSTAGE_ID_NET_ETH_INIT);
do {
- if (dev->seq != -1) {
+ if (device_active(dev)) {
if (num_devices)
printf(", ");
- printf("eth%d: %s", dev->seq, dev->name);
+ printf("eth%d: %s", dev_seq(dev), dev->name);
if (ethprime && dev == prime_dev)
printf(" [PRIME]");
eth_write_hwaddr(dev);
- if (dev->seq != -1)
+ if (device_active(dev))
num_devices++;
uclass_next_device_check(&dev);
} while (dev);
static int eth_post_bind(struct udevice *dev)
{
+ struct eth_uclass_priv *priv = uclass_get_priv(dev->uclass);
+ int ret;
+
if (strchr(dev->name, ' ')) {
printf("\nError: eth device name \"%s\" has a space!\n",
dev->name);
#ifdef CONFIG_DM_ETH_PHY
eth_phy_binds_nodes(dev);
#endif
+ if (CONFIG_IS_ENABLED(BOOTDEV_ETH) && !priv->no_bootdevs) {
+ ret = bootdev_setup_for_dev(dev, "eth_bootdev");
+ if (ret)
+ return log_msg_ret("bootdev", ret);
+ }
return 0;
}
static bool eth_dev_get_mac_address(struct udevice *dev, u8 mac[ARP_HLEN])
{
-#if IS_ENABLED(CONFIG_OF_CONTROL)
+#if CONFIG_IS_ENABLED(OF_CONTROL)
const uint8_t *p;
+ struct nvmem_cell mac_cell;
p = dev_read_u8_array_ptr(dev, "mac-address", ARP_HLEN);
if (!p)
p = dev_read_u8_array_ptr(dev, "local-mac-address", ARP_HLEN);
- if (!p)
- return false;
+ if (p) {
+ memcpy(mac, p, ARP_HLEN);
+ return true;
+ }
- memcpy(mac, p, ARP_HLEN);
+ if (nvmem_cell_get_by_name(dev, "mac-address", &mac_cell))
+ return false;
- return true;
+ return !nvmem_cell_read(&mac_cell, mac, ARP_HLEN);
#else
return false;
#endif
static int eth_post_probe(struct udevice *dev)
{
- struct eth_device_priv *priv = dev->uclass_priv;
- struct eth_pdata *pdata = dev->platdata;
+ struct eth_device_priv *priv = dev_get_uclass_priv(dev);
+ struct eth_pdata *pdata = dev_get_plat(dev);
unsigned char env_enetaddr[ARP_HLEN];
char *source = "DT";
#endif
priv->state = ETH_STATE_INIT;
+ priv->running = false;
/* Check if the device has a valid MAC address in device tree */
if (!eth_dev_get_mac_address(dev, pdata->enetaddr) ||
eth_get_ops(dev)->read_rom_hwaddr(dev);
}
- eth_env_get_enetaddr_by_index("eth", dev->seq, env_enetaddr);
+ eth_env_get_enetaddr_by_index("eth", dev_seq(dev), env_enetaddr);
if (!is_zero_ethaddr(env_enetaddr)) {
if (!is_zero_ethaddr(pdata->enetaddr) &&
memcmp(pdata->enetaddr, env_enetaddr, ARP_HLEN)) {
/* Override the ROM MAC address */
memcpy(pdata->enetaddr, env_enetaddr, ARP_HLEN);
} else if (is_valid_ethaddr(pdata->enetaddr)) {
- eth_env_set_enetaddr_by_index("eth", dev->seq, pdata->enetaddr);
+ eth_env_set_enetaddr_by_index("eth", dev_seq(dev),
+ pdata->enetaddr);
} else if (is_zero_ethaddr(pdata->enetaddr) ||
!is_valid_ethaddr(pdata->enetaddr)) {
#ifdef CONFIG_NET_RANDOM_ETHADDR
net_random_ethaddr(pdata->enetaddr);
printf("\nWarning: %s (eth%d) using random MAC address - %pM\n",
- dev->name, dev->seq, pdata->enetaddr);
+ dev->name, dev_seq(dev), pdata->enetaddr);
+ eth_env_set_enetaddr_by_index("eth", dev_seq(dev),
+ pdata->enetaddr);
#else
printf("\nError: %s address not set.\n",
dev->name);
static int eth_pre_remove(struct udevice *dev)
{
- struct eth_pdata *pdata = dev->platdata;
+ struct eth_pdata *pdata = dev_get_plat(dev);
eth_get_ops(dev)->stop(dev);
return 0;
}
-UCLASS_DRIVER(eth) = {
- .name = "eth",
+UCLASS_DRIVER(ethernet) = {
+ .name = "ethernet",
.id = UCLASS_ETH,
.post_bind = eth_post_bind,
.pre_unbind = eth_pre_unbind,
.post_probe = eth_post_probe,
.pre_remove = eth_pre_remove,
- .priv_auto_alloc_size = sizeof(struct eth_uclass_priv),
- .per_device_auto_alloc_size = sizeof(struct eth_device_priv),
+ .priv_auto = sizeof(struct eth_uclass_priv),
+ .per_device_auto = sizeof(struct eth_device_priv),
.flags = DM_UC_FLAG_SEQ_ALIAS,
};