diff options
Diffstat (limited to 'net/eth.c')
-rw-r--r-- | net/eth.c | 294 |
1 files changed, 218 insertions, 76 deletions
@@ -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, ðaddr_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); |