Merge a patch series to improve dc2114x support
This patch series by Hanyuan Zhao <hanyuan-z@qq.com> provides a number of improvements to the dc2114x driver. Link: https://lore.kernel.org/r/tencent_BD4B002FC63A5F77969D9BD1FFF125371C08@qq.com
This commit is contained in:
@@ -762,6 +762,38 @@ config TULIP
|
||||
help
|
||||
This driver supports DEC DC2114x Fast ethernet chips.
|
||||
|
||||
config TULIP_SUPPORT_NON_PCI
|
||||
bool "No PCI controller"
|
||||
depends on TULIP
|
||||
default n
|
||||
help
|
||||
Say Y to this and you can run this driver on platforms that do not
|
||||
have PCI controllers.
|
||||
|
||||
config TULIP_IGNORE_TX_NO_CARRIER
|
||||
bool "Ignore tx no carrier error"
|
||||
depends on TULIP
|
||||
default n
|
||||
help
|
||||
Some IP cores of dc2114x or its variants do not comply so well with
|
||||
the behaviors described by the official document. A packet could be
|
||||
sent successfully but reported with No Carrier error. Latest drivers
|
||||
of this IP core do not detect this error anymore. Say Y to this could
|
||||
disable handling of this error.
|
||||
|
||||
config TULIP_MULTIPLE_TX_DESC
|
||||
bool "Use multiple tx descriptors"
|
||||
depends on TULIP
|
||||
default n
|
||||
help
|
||||
Some IP cores of dc2114x or its variants do not comply so well with
|
||||
the behaviors described by the official document. Originally this
|
||||
driver uses only one tx descriptor and organizes it as a ring buffer,
|
||||
which would lead to a problem that one packet would be sent twice.
|
||||
Say Y to this could prevent this bug if you are using IP cores with
|
||||
this issue, by using multiple tx descriptors and organizing them as
|
||||
a real well-defined ring buffer.
|
||||
|
||||
config XILINX_AXIEMAC
|
||||
select PHYLIB
|
||||
select MII
|
||||
|
@@ -1,6 +1,7 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
|
||||
#include <asm/io.h>
|
||||
#include <cpu_func.h>
|
||||
#include <dm.h>
|
||||
#include <malloc.h>
|
||||
#include <net.h>
|
||||
@@ -72,10 +73,20 @@
|
||||
|
||||
#define POLL_DEMAND 1
|
||||
|
||||
#if CONFIG_IS_ENABLED(TULIP_SUPPORT_NON_PCI)
|
||||
#define phys_to_bus(dev, a) virt_to_phys((volatile const void *)(a))
|
||||
#else
|
||||
#define phys_to_bus(dev, a) dm_pci_phys_to_mem((dev), (a))
|
||||
#endif
|
||||
|
||||
/* Number of TX descriptors */
|
||||
#if CONFIG_IS_ENABLED(TULIP_MULTIPLE_TX_DESC)
|
||||
#define NUM_TX_DESC 4
|
||||
#else
|
||||
#define NUM_TX_DESC 1
|
||||
#endif
|
||||
|
||||
#define NUM_RX_DESC PKTBUFSRX
|
||||
#define NUM_TX_DESC 1 /* Number of TX descriptors */
|
||||
#define RX_BUFF_SZ PKTSIZE_ALIGN
|
||||
|
||||
#define TOUT_LOOP 1000000
|
||||
@@ -89,9 +100,17 @@ struct de4x5_desc {
|
||||
u32 next;
|
||||
};
|
||||
|
||||
/* Assigned for network card's ring buffer:
|
||||
* Some CPU might treat these memories as cached, and changes to these memories
|
||||
* won't immediately be visible to each other. It is necessary to ensure that
|
||||
* these memories between the CPU and the network card are marked as uncached.
|
||||
*/
|
||||
static struct de4x5_desc rx_ring[NUM_RX_DESC] __aligned(32);
|
||||
static struct de4x5_desc tx_ring[NUM_TX_DESC] __aligned(32);
|
||||
|
||||
struct dc2114x_priv {
|
||||
struct de4x5_desc rx_ring[NUM_RX_DESC] __aligned(32);
|
||||
struct de4x5_desc tx_ring[NUM_TX_DESC] __aligned(32);
|
||||
struct de4x5_desc *rx_ring; /* Must be uncached to CPU */
|
||||
struct de4x5_desc *tx_ring; /* Must be uncached to CPU */
|
||||
int rx_new; /* RX descriptor ring pointer */
|
||||
int tx_new; /* TX descriptor ring pointer */
|
||||
char rx_ring_size;
|
||||
@@ -271,7 +290,12 @@ static int read_srom(struct dc2114x_priv *priv, u_long ioaddr, int index)
|
||||
|
||||
static void send_setup_frame(struct dc2114x_priv *priv)
|
||||
{
|
||||
char setup_frame[SETUP_FRAME_LEN];
|
||||
/* We are writing setup frame and these changes should be visible to the
|
||||
* network card immediately. So let's directly read/write through the
|
||||
* uncached window.
|
||||
*/
|
||||
char __setup_frame[SETUP_FRAME_LEN] __aligned(32);
|
||||
char *setup_frame = (char *)map_physmem((phys_addr_t)virt_to_phys(__setup_frame), 0, MAP_NOCACHE);
|
||||
char *pa = &setup_frame[0];
|
||||
int i;
|
||||
|
||||
@@ -292,8 +316,13 @@ static void send_setup_frame(struct dc2114x_priv *priv)
|
||||
}
|
||||
|
||||
priv->tx_ring[priv->tx_new].buf = cpu_to_le32(phys_to_bus(priv->devno,
|
||||
(u32)&setup_frame[0]));
|
||||
(phys_addr_t)&setup_frame[0]));
|
||||
#if CONFIG_IS_ENABLED(TULIP_MULTIPLE_TX_DESC)
|
||||
priv->tx_ring[priv->tx_new].des1 = cpu_to_le32(TD_SET | SETUP_FRAME_LEN);
|
||||
priv->tx_ring[priv->tx_ring_size - 1].des1 |= cpu_to_le32(TD_TER);
|
||||
#else
|
||||
priv->tx_ring[priv->tx_new].des1 = cpu_to_le32(TD_TER | TD_SET | SETUP_FRAME_LEN);
|
||||
#endif
|
||||
priv->tx_ring[priv->tx_new].status = cpu_to_le32(T_OWN);
|
||||
|
||||
dc2114x_outl(priv, POLL_DEMAND, DE4X5_TPD);
|
||||
@@ -307,7 +336,7 @@ static void send_setup_frame(struct dc2114x_priv *priv)
|
||||
}
|
||||
|
||||
if (le32_to_cpu(priv->tx_ring[priv->tx_new].status) != 0x7FFFFFFF) {
|
||||
printf("TX error status2 = 0x%08X\n",
|
||||
debug("TX error status2 = 0x%08X\n",
|
||||
le32_to_cpu(priv->tx_ring[priv->tx_new].status));
|
||||
}
|
||||
|
||||
@@ -332,9 +361,17 @@ static int dc21x4x_send_common(struct dc2114x_priv *priv, void *packet, int leng
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Packet should be visible to the network card */
|
||||
flush_dcache_range((phys_addr_t)packet, (phys_addr_t)(packet + RX_BUFF_SZ));
|
||||
|
||||
priv->tx_ring[priv->tx_new].buf = cpu_to_le32(phys_to_bus(priv->devno,
|
||||
(u32)packet));
|
||||
(phys_addr_t)packet));
|
||||
#if CONFIG_IS_ENABLED(TULIP_MULTIPLE_TX_DESC)
|
||||
priv->tx_ring[priv->tx_new].des1 = cpu_to_le32(TD_LS | TD_FS | length);
|
||||
priv->tx_ring[priv->tx_ring_size - 1].des1 |= cpu_to_le32(TD_TER);
|
||||
#else
|
||||
priv->tx_ring[priv->tx_new].des1 = cpu_to_le32(TD_TER | TD_LS | TD_FS | length);
|
||||
#endif
|
||||
priv->tx_ring[priv->tx_new].status = cpu_to_le32(T_OWN);
|
||||
|
||||
dc2114x_outl(priv, POLL_DEMAND, DE4X5_TPD);
|
||||
@@ -349,7 +386,9 @@ static int dc21x4x_send_common(struct dc2114x_priv *priv, void *packet, int leng
|
||||
|
||||
if (le32_to_cpu(priv->tx_ring[priv->tx_new].status) & TD_ES) {
|
||||
priv->tx_ring[priv->tx_new].status = 0x0;
|
||||
#if !CONFIG_IS_ENABLED(TULIP_IGNORE_TX_NO_CARRIER)
|
||||
goto done;
|
||||
#endif
|
||||
}
|
||||
|
||||
status = length;
|
||||
@@ -398,13 +437,22 @@ static int dc21x4x_init_common(struct dc2114x_priv *priv)
|
||||
return -1;
|
||||
}
|
||||
|
||||
dc2114x_outl(priv, OMR_SDP | OMR_PS | OMR_PM, DE4X5_OMR);
|
||||
/* 2024-07:
|
||||
* Remove the OMR_PM flag and choose 16 perfect filtering mode since in
|
||||
* modern networks there're plenty of multicasts and set ORM_PM flag will
|
||||
* increase the dc2114x's workload and ask the U-Boot to handle packets
|
||||
* not related to itself. And most of the time, U-Boot does not need this
|
||||
* feature.
|
||||
*
|
||||
* A better way: let user to decide whether to have this flag.
|
||||
*/
|
||||
dc2114x_outl(priv, OMR_SDP | OMR_PS, DE4X5_OMR);
|
||||
|
||||
for (i = 0; i < NUM_RX_DESC; i++) {
|
||||
priv->rx_ring[i].status = cpu_to_le32(R_OWN);
|
||||
priv->rx_ring[i].des1 = cpu_to_le32(RX_BUFF_SZ);
|
||||
priv->rx_ring[i].buf = cpu_to_le32(phys_to_bus(priv->devno,
|
||||
(u32)net_rx_packets[i]));
|
||||
(phys_addr_t)net_rx_packets[i]));
|
||||
priv->rx_ring[i].next = 0;
|
||||
}
|
||||
|
||||
@@ -423,9 +471,9 @@ static int dc21x4x_init_common(struct dc2114x_priv *priv)
|
||||
priv->tx_ring[priv->tx_ring_size - 1].des1 |= cpu_to_le32(TD_TER);
|
||||
|
||||
/* Tell the adapter where the TX/RX rings are located. */
|
||||
dc2114x_outl(priv, phys_to_bus(priv->devno, (u32)&priv->rx_ring),
|
||||
dc2114x_outl(priv, phys_to_bus(priv->devno, (phys_addr_t)priv->rx_ring),
|
||||
DE4X5_RRBA);
|
||||
dc2114x_outl(priv, phys_to_bus(priv->devno, (u32)&priv->tx_ring),
|
||||
dc2114x_outl(priv, phys_to_bus(priv->devno, (phys_addr_t)priv->tx_ring),
|
||||
DE4X5_TRBA);
|
||||
|
||||
start_de4x5(priv);
|
||||
@@ -461,21 +509,32 @@ static void read_hw_addr(struct dc2114x_priv *priv)
|
||||
}
|
||||
}
|
||||
|
||||
#if !CONFIG_IS_ENABLED(TULIP_SUPPORT_NON_PCI)
|
||||
static struct pci_device_id supported[] = {
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_TULIP_FAST) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_21142) },
|
||||
{ }
|
||||
};
|
||||
#endif
|
||||
|
||||
static int dc2114x_start(struct udevice *dev)
|
||||
{
|
||||
struct eth_pdata *plat = dev_get_plat(dev);
|
||||
struct dc2114x_priv *priv = dev_get_priv(dev);
|
||||
int rval;
|
||||
|
||||
memcpy(priv->enetaddr, plat->enetaddr, sizeof(plat->enetaddr));
|
||||
if (!priv->enetaddr) {
|
||||
rval = eth_env_get_enetaddr("ethaddr", priv->enetaddr);
|
||||
|
||||
if (!rval) {
|
||||
printf("dc2114x: Err: please set a valid MAC address\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
#if !CONFIG_IS_ENABLED(TULIP_SUPPORT_NON_PCI)
|
||||
/* Ensure we're not sleeping. */
|
||||
dm_pci_write_config8(dev, PCI_CFDA_PSM, WAKEUP);
|
||||
#endif
|
||||
|
||||
return dc21x4x_init_common(priv);
|
||||
}
|
||||
@@ -485,8 +544,9 @@ static void dc2114x_stop(struct udevice *dev)
|
||||
struct dc2114x_priv *priv = dev_get_priv(dev);
|
||||
|
||||
dc21x4x_halt_common(priv);
|
||||
|
||||
#if !CONFIG_IS_ENABLED(TULIP_SUPPORT_NON_PCI)
|
||||
dm_pci_write_config8(dev, PCI_CFDA_PSM, SLEEP);
|
||||
#endif
|
||||
}
|
||||
|
||||
static int dc2114x_send(struct udevice *dev, void *packet, int length)
|
||||
@@ -515,7 +575,8 @@ static int dc2114x_recv(struct udevice *dev, int flags, uchar **packetp)
|
||||
if (!ret)
|
||||
return 0;
|
||||
|
||||
*packetp = net_rx_packets[priv->rx_new];
|
||||
invalidate_dcache_range((phys_addr_t)net_rx_packets[priv->rx_new], (phys_addr_t)(net_rx_packets[priv->rx_new] + RX_BUFF_SZ));
|
||||
*packetp = (uchar *)net_rx_packets[priv->rx_new];
|
||||
|
||||
return ret - 4;
|
||||
}
|
||||
@@ -543,7 +604,7 @@ static int dc2114x_read_rom_hwaddr(struct udevice *dev)
|
||||
|
||||
static int dc2114x_bind(struct udevice *dev)
|
||||
{
|
||||
static int card_number;
|
||||
static int card_number = 0;
|
||||
char name[16];
|
||||
|
||||
sprintf(name, "dc2114x#%u", card_number++);
|
||||
@@ -555,6 +616,8 @@ static int dc2114x_probe(struct udevice *dev)
|
||||
{
|
||||
struct eth_pdata *plat = dev_get_plat(dev);
|
||||
struct dc2114x_priv *priv = dev_get_priv(dev);
|
||||
|
||||
#if !CONFIG_IS_ENABLED(TULIP_SUPPORT_NON_PCI)
|
||||
u16 command, status;
|
||||
u32 iobase;
|
||||
|
||||
@@ -562,9 +625,6 @@ static int dc2114x_probe(struct udevice *dev)
|
||||
iobase &= ~0xf;
|
||||
|
||||
debug("dc2114x: DEC 2114x PCI Device @0x%x\n", iobase);
|
||||
|
||||
priv->devno = dev;
|
||||
priv->enetaddr = plat->enetaddr;
|
||||
priv->iobase = (void __iomem *)dm_pci_mem_to_phys(dev, iobase);
|
||||
|
||||
command = PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER;
|
||||
@@ -576,10 +636,29 @@ static int dc2114x_probe(struct udevice *dev)
|
||||
}
|
||||
|
||||
dm_pci_write_config8(dev, PCI_LATENCY_TIMER, 0x60);
|
||||
#endif
|
||||
|
||||
priv->devno = dev;
|
||||
priv->enetaddr = plat->enetaddr;
|
||||
priv->rx_ring = (struct de4x5_desc *)map_physmem((phys_addr_t)virt_to_phys(rx_ring), 0, MAP_NOCACHE);
|
||||
priv->tx_ring = (struct de4x5_desc *)map_physmem((phys_addr_t)virt_to_phys(tx_ring), 0, MAP_NOCACHE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if CONFIG_IS_ENABLED(TULIP_SUPPORT_NON_PCI)
|
||||
static int dc2114x_of_to_plat(struct udevice *dev)
|
||||
{
|
||||
struct eth_pdata *plat = dev_get_plat(dev);
|
||||
struct dc2114x_priv *priv = dev_get_priv(dev);
|
||||
|
||||
plat->iobase = (phys_addr_t)map_physmem((phys_addr_t)devfdt_get_addr(dev), 0, MAP_NOCACHE);
|
||||
priv->iobase = (void *)plat->iobase;
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static const struct eth_ops dc2114x_ops = {
|
||||
.start = dc2114x_start,
|
||||
.send = dc2114x_send,
|
||||
@@ -589,9 +668,23 @@ static const struct eth_ops dc2114x_ops = {
|
||||
.read_rom_hwaddr = dc2114x_read_rom_hwaddr,
|
||||
};
|
||||
|
||||
#if CONFIG_IS_ENABLED(TULIP_SUPPORT_NON_PCI)
|
||||
static const struct udevice_id dc2114x_eth_ids[] = {
|
||||
{ .compatible = "dec,dmfe" },
|
||||
{ .compatible = "tulip,dmfe" },
|
||||
{ .compatible = "dec,dc2114x" },
|
||||
{ .compatible = "tulip,dc2114x" },
|
||||
{ }
|
||||
};
|
||||
#endif
|
||||
|
||||
U_BOOT_DRIVER(eth_dc2114x) = {
|
||||
.name = "eth_dc2114x",
|
||||
.id = UCLASS_ETH,
|
||||
#if CONFIG_IS_ENABLED(TULIP_SUPPORT_NON_PCI)
|
||||
.of_match = dc2114x_eth_ids,
|
||||
.of_to_plat = dc2114x_of_to_plat,
|
||||
#endif
|
||||
.bind = dc2114x_bind,
|
||||
.probe = dc2114x_probe,
|
||||
.ops = &dc2114x_ops,
|
||||
@@ -599,4 +692,6 @@ U_BOOT_DRIVER(eth_dc2114x) = {
|
||||
.plat_auto = sizeof(struct eth_pdata),
|
||||
};
|
||||
|
||||
#if !CONFIG_IS_ENABLED(TULIP_SUPPORT_NON_PCI)
|
||||
U_BOOT_PCI_DEVICE(eth_dc2114x, supported);
|
||||
#endif
|
||||
|
Reference in New Issue
Block a user