summaryrefslogtreecommitdiffstats
path: root/net/eth.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/eth.c')
-rw-r--r--net/eth.c294
1 files changed, 218 insertions, 76 deletions
diff --git a/net/eth.c b/net/eth.c
index 53d24baa16..28961e868b 100644
--- a/net/eth.c
+++ b/net/eth.c
@@ -1,21 +1,5 @@
-/*
- * (C) Copyright 2001-2004
- * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
- *
- * 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.
- *
- * 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.
- *
- */
+// SPDX-License-Identifier: GPL-2.0-or-later
+// SPDX-FileCopyrightText: 2001-2004 Wolfgang Denk <wd@denx.de>, DENX Software Engineering
#include <common.h>
#include <command.h>
@@ -25,7 +9,10 @@
#include <init.h>
#include <dhcp.h>
#include <net.h>
+#include <dma.h>
+#include <machine_id.h>
#include <of.h>
+#include <of_net.h>
#include <linux/phy.h>
#include <errno.h>
#include <malloc.h>
@@ -34,8 +21,6 @@
#include <linux/ctype.h>
#include <linux/stat.h>
-static uint64_t last_link_check;
-
LIST_HEAD(netdev_list);
struct eth_ethaddr {
@@ -47,6 +32,14 @@ struct eth_ethaddr {
static LIST_HEAD(ethaddr_list);
+int eth_set_promisc(struct eth_device *edev, bool enable)
+{
+ if (!edev->set_promisc)
+ return -EOPNOTSUPP;
+
+ return edev->set_promisc(edev, enable);
+}
+
int eth_set_ethaddr(struct eth_device *edev, const char *ethaddr)
{
int ret;
@@ -62,11 +55,8 @@ int eth_set_ethaddr(struct eth_device *edev, const char *ethaddr)
static void register_preset_mac_address(struct eth_device *edev, const char *ethaddr)
{
- unsigned char ethaddr_str[sizeof("xx:xx:xx:xx:xx:xx")];
-
if (is_valid_ether_addr(ethaddr)) {
- ethaddr_to_string(ethaddr, ethaddr_str);
- dev_info(&edev->dev, "got preset MAC address: %s\n", ethaddr_str);
+ dev_info(&edev->dev, "got preset MAC address: %pM\n", ethaddr);
eth_set_ethaddr(edev, ethaddr);
}
}
@@ -77,7 +67,7 @@ static int eth_get_registered_ethaddr(struct eth_device *edev, void *buf)
struct device_node *node = NULL;
if (edev->parent)
- node = edev->parent->device_node;
+ node = edev->parent->of_node;
list_for_each_entry(addr, &ethaddr_list, list) {
if ((node && node == addr->node) ||
@@ -129,9 +119,9 @@ static struct eth_device *eth_get_by_node(struct device_node *node)
for_each_netdev(edev) {
if (!edev->parent)
continue;
- if (!edev->parent->device_node)
+ if (!edev->parent->of_node)
continue;
- if (edev->parent->device_node == node)
+ if (edev->parent->of_node == node)
return edev;
}
return NULL;
@@ -184,10 +174,7 @@ int eth_complete(struct string_list *sl, char *instr)
}
#endif
-/*
- * Check for link if we haven't done so for longer.
- */
-static int eth_carrier_check(struct eth_device *edev, int force)
+int eth_carrier_poll_once(struct eth_device *edev)
{
int ret;
@@ -197,70 +184,119 @@ static int eth_carrier_check(struct eth_device *edev, int force)
if (!edev->phydev)
return 0;
- if (force)
- phy_wait_aneg_done(edev->phydev);
-
- if (force || is_timeout(last_link_check, 5 * SECOND) ||
- !edev->phydev->link) {
- ret = phy_update_status(edev->phydev);
- if (ret)
- return ret;
- last_link_check = get_time_ns();
- }
+ ret = phy_update_status(edev->phydev);
+ if (ret)
+ return ret;
+ edev->last_link_check = get_time_ns();
return edev->phydev->link ? 0 : -ENETDOWN;
}
/*
- * Check if we have a current ethernet device and
- * eventually open it if we have to.
+ * Check for link if we haven't done so for longer.
*/
-static int eth_check_open(struct eth_device *edev)
+static int eth_carrier_check(struct eth_device *edev, bool may_wait)
{
- int ret;
+ if (!IS_ENABLED(CONFIG_PHYLIB))
+ return 0;
- if (edev->active)
+ if (!edev->phydev)
return 0;
- ret = edev->open(edev);
- if (ret)
- return ret;
+ if (!edev->last_link_check ||
+ is_timeout(edev->last_link_check, 5 * SECOND))
+ eth_carrier_poll_once(edev);
+
+ if (may_wait && !edev->phydev->link) {
+ phy_wait_aneg_done(edev->phydev);
+ edev->last_link_check = get_time_ns();
+ }
+
+ return edev->phydev->link ? 0 : -ENETDOWN;
+}
+
+struct eth_q {
+ struct eth_device *edev;
+ int length;
+ struct list_head list;
+ void *data;
+};
+
+static int eth_queue(struct eth_device *edev, void *packet, int length)
+{
+ struct eth_q *q;
- edev->active = 1;
+ q = xzalloc(sizeof(*q));
+ if (!q)
+ return -ENOMEM;
- return eth_carrier_check(edev, 1);
+ q->data = dma_alloc(length);
+ if (!q->data) {
+ free(q);
+ return -ENOMEM;
+ }
+
+ q->length = length;
+ q->edev = edev;
+
+ memcpy(q->data, packet, length);
+ list_add_tail(&q->list, &edev->send_queue);
+
+ return 0;
}
int eth_send(struct eth_device *edev, void *packet, int length)
{
int ret;
- ret = eth_check_open(edev);
- if (ret)
- return ret;
+ if (!edev->active)
+ return -ENETDOWN;
- ret = eth_carrier_check(edev, 0);
+ if (slice_acquired(eth_device_slice(edev)))
+ return eth_queue(edev, packet, length);
+
+ ret = eth_carrier_check(edev, true);
if (ret)
return ret;
+ slice_acquire(eth_device_slice(edev));
+
led_trigger_network(LED_TRIGGER_NET_TX);
- return edev->send(edev, packet, length);
+ ret = eth_send_raw(edev, packet, length);
+
+ slice_release(eth_device_slice(edev));
+
+ return ret;
}
-static int __eth_rx(struct eth_device *edev)
+static void eth_do_work(struct eth_device *edev)
{
+ struct eth_q *q, *tmp;
int ret;
- ret = eth_check_open(edev);
- if (ret)
- return ret;
+ if (!phy_acquired(edev->phydev)) {
+ ret = eth_carrier_check(edev, false);
+ if (ret)
+ return;
+ }
- ret = eth_carrier_check(edev, 0);
- if (ret)
- return ret;
+ if (slice_acquired(eth_device_slice(edev)))
+ return;
- return edev->recv(edev);
+ slice_acquire(eth_device_slice(edev));
+
+ edev->recv(edev);
+
+ list_for_each_entry_safe(q, tmp, &edev->send_queue, list) {
+ led_trigger_network(LED_TRIGGER_NET_TX);
+ eth_send_raw(edev, q->data, q->length);
+ list_del(&q->list);
+ free(q->data);
+ free(q);
+ }
+
+ slice_release(eth_device_slice(edev));
}
int eth_rx(void)
@@ -269,7 +305,7 @@ int eth_rx(void)
for_each_netdev(edev) {
if (edev->active)
- __eth_rx(edev);
+ eth_do_work(edev);
}
return 0;
@@ -287,7 +323,8 @@ static void eth_of_fixup_node(struct device_node *root,
const char *node_path, int ethid,
const u8 ethaddr[ETH_ALEN])
{
- struct device_node *node;
+ struct device_node *bb_node, *fixup_node;
+ char *name;
int ret;
if (!is_valid_ether_addr(ethaddr)) {
@@ -297,22 +334,25 @@ static void eth_of_fixup_node(struct device_node *root,
}
if (node_path) {
- node = of_find_node_by_path_from(root, node_path);
+ bb_node = of_find_node_by_path_from(0, node_path);
+ name = of_get_reproducible_name(bb_node);
+ fixup_node = of_find_node_by_reproducible_name(root, name);
+ free(name);
} else {
char eth[12];
sprintf(eth, "ethernet%d", ethid);
- node = of_find_node_by_alias(root, eth);
+ fixup_node = of_find_node_by_alias(root, eth);
}
- if (!node) {
+ if (!fixup_node) {
pr_debug("%s: no node to fixup\n", __func__);
return;
}
- ret = of_set_property(node, "mac-address", ethaddr, ETH_ALEN, 1);
+ ret = of_set_property(fixup_node, "mac-address", ethaddr, ETH_ALEN, 1);
if (ret)
pr_err("Setting mac-address property of %s failed with: %s\n",
- node->full_name, strerror(-ret));
+ fixup_node->full_name, strerror(-ret));
}
static int eth_of_fixup(struct device_node *root, void *unused)
@@ -355,7 +395,7 @@ static const char * const eth_mode_names[] = {
int eth_register(struct eth_device *edev)
{
- struct device_d *dev = &edev->dev;
+ struct device *dev = &edev->dev;
unsigned char ethaddr[ETH_ALEN];
int ret, found = 0;
@@ -369,18 +409,23 @@ int eth_register(struct eth_device *edev)
if (edev->parent)
edev->dev.parent = edev->parent;
- if (edev->dev.parent && edev->dev.parent->device_node) {
- edev->dev.id = of_alias_get_id(edev->dev.parent->device_node, "ethernet");
+ if (edev->dev.parent && edev->dev.parent->of_node) {
+ edev->dev.id = of_alias_get_id(edev->dev.parent->of_node,
+ "ethernet");
if (edev->dev.id < 0)
edev->dev.id = DEVICE_ID_DYNAMIC;
} else {
edev->dev.id = DEVICE_ID_DYNAMIC;
}
+ INIT_LIST_HEAD(&edev->send_queue);
+
ret = register_device(&edev->dev);
if (ret)
return ret;
+ slice_init(&edev->slice, dev_name(dev));
+
edev->devname = xstrdup(dev_name(&edev->dev));
dev_add_param_ip(dev, "ipaddr", NULL, NULL, &edev->ipaddr, edev);
@@ -416,23 +461,61 @@ int eth_register(struct eth_device *edev)
register_preset_mac_address(edev, ethaddr);
if (IS_ENABLED(CONFIG_OFDEVICE) && edev->parent &&
- edev->parent->device_node)
- edev->nodepath = xstrdup(edev->parent->device_node->full_name);
+ edev->parent->of_node)
+ edev->nodepath = xstrdup(edev->parent->of_node->full_name);
return 0;
}
+int eth_open(struct eth_device *edev)
+{
+ int ret;
+
+ if (edev->active)
+ return 0;
+
+ edev->last_link_check = 0;
+
+ ret = edev->open(edev);
+ if (!ret)
+ edev->active = 1;
+
+ return ret;
+}
+
+void eth_close(struct eth_device *edev)
+{
+ if (!edev->active)
+ return;
+
+ edev->halt(edev);
+
+ edev->active = 0;
+}
+
void eth_unregister(struct eth_device *edev)
{
+ struct eth_q *q, *tmp;
+
if (edev->active)
edev->halt(edev);
+ list_for_each_entry_safe(q, tmp, &edev->send_queue, list) {
+ if (q->edev != edev)
+ continue;
+
+ list_del(&q->list);
+ free(q->data);
+ free(q);
+ }
+
if (IS_ENABLED(CONFIG_OFDEVICE))
free(edev->nodepath);
free(edev->devname);
unregister_device(&edev->dev);
+ slice_exit(&edev->slice);
list_del(&edev->list);
}
@@ -441,3 +524,62 @@ void led_trigger_network(enum led_trigger trigger)
led_trigger(trigger, TRIGGER_FLASH);
led_trigger(LED_TRIGGER_NET_TXRX, TRIGGER_FLASH);
}
+
+struct eth_device *of_find_eth_device_by_node(struct device_node *np)
+{
+ struct eth_device *edev;
+ int ret;
+
+ ret = of_device_ensure_probed(np);
+ if (ret)
+ return NULL;
+
+ list_for_each_entry(edev, &netdev_list, list)
+ if (edev->parent->of_node == np)
+ return edev;
+ return NULL;
+}
+EXPORT_SYMBOL(of_find_eth_device_by_node);
+
+void eth_open_all(void)
+{
+ struct eth_device *edev;
+
+ list_for_each_entry(edev, &netdev_list, list) {
+ if (edev->global_mode == ETH_MODE_DISABLED)
+ continue;
+ eth_open(edev);
+ }
+}
+
+static int populate_ethaddr(void)
+{
+ struct eth_device *edev;
+ bool generated = false;
+ int ret;
+
+ list_for_each_entry(edev, &netdev_list, list) {
+ if (!edev->parent || is_valid_ether_addr(edev->ethaddr))
+ continue;
+
+ ret = of_get_mac_addr_nvmem(edev->parent->of_node,
+ edev->ethaddr);
+ if (IS_ENABLED(CONFIG_NET_ETHADDR_FROM_MACHINE_ID) && ret) {
+ ret = generate_ether_addr(edev->ethaddr, edev->dev.id);
+ generated = true;
+ }
+ if (ret)
+ continue;
+
+ if (generated)
+ dev_notice(&edev->dev, "Generated MAC address from unique id: %pM\n",
+ edev->ethaddr);
+ else
+ dev_info(&edev->dev, "Got preset MAC address from NVMEM: %pM\n",
+ edev->ethaddr);
+ eth_set_ethaddr(edev, edev->ethaddr);
+ }
+
+ return 0;
+}
+postenvironment_initcall(populate_ethaddr);