summaryrefslogtreecommitdiff
path: root/drivers/net
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net')
-rw-r--r--drivers/net/Kconfig5
-rw-r--r--drivers/net/Makefile1
-rw-r--r--drivers/net/dwc_eth_qos.c96
-rw-r--r--drivers/net/dwc_eth_qos.h5
-rw-r--r--drivers/net/liteeth.c214
-rw-r--r--drivers/net/macb.c51
-rw-r--r--drivers/net/phy/Kconfig5
-rw-r--r--drivers/net/phy/Makefile1
-rw-r--r--drivers/net/phy/aquantia.c8
-rw-r--r--drivers/net/phy/intel_xway.c48
-rw-r--r--drivers/net/phy/phy.c3
11 files changed, 388 insertions, 49 deletions
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index 8df3dce6dff..029bf3872aa 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -438,6 +438,11 @@ config KSZ9477
This driver implements a DSA switch driver for the KSZ9477 family
of GbE switches using the I2C interface.
+config LITEETH
+ bool "LiteX LiteEth Ethernet MAC"
+ help
+ Driver for the LiteEth Ethernet MAC from LiteX.
+
config MVGBE
bool "Marvell Orion5x/Kirkwood network interface support"
depends on ARCH_KIRKWOOD || ARCH_ORION5X
diff --git a/drivers/net/Makefile b/drivers/net/Makefile
index 96b7678e988..d3fc6b7d3ee 100644
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -47,6 +47,7 @@ obj-$(CONFIG_GMAC_ROCKCHIP) += gmac_rockchip.o
obj-$(CONFIG_HIGMACV300_ETH) += higmacv300.o
obj-$(CONFIG_KS8851_MLL) += ks8851_mll.o
obj-$(CONFIG_KSZ9477) += ksz9477.o
+obj-$(CONFIG_LITEETH) += liteeth.o
obj-$(CONFIG_MACB) += macb.o
obj-$(CONFIG_MCFFEC) += mcffec.o mcfmii.o
obj-$(CONFIG_MDIO_IPQ4019) += mdio-ipq4019.o
diff --git a/drivers/net/dwc_eth_qos.c b/drivers/net/dwc_eth_qos.c
index 001b028fa13..afc47b56ff5 100644
--- a/drivers/net/dwc_eth_qos.c
+++ b/drivers/net/dwc_eth_qos.c
@@ -75,10 +75,7 @@
*/
static void *eqos_alloc_descs(struct eqos_priv *eqos, unsigned int num)
{
- eqos->desc_size = ALIGN(sizeof(struct eqos_desc),
- (unsigned int)ARCH_DMA_MINALIGN);
-
- return memalign(eqos->desc_size, num * eqos->desc_size);
+ return memalign(ARCH_DMA_MINALIGN, num * eqos->desc_size);
}
static void eqos_free_descs(void *descs)
@@ -89,13 +86,13 @@ static void eqos_free_descs(void *descs)
static struct eqos_desc *eqos_get_desc(struct eqos_priv *eqos,
unsigned int num, bool rx)
{
- return eqos->descs +
- ((rx ? EQOS_DESCRIPTORS_TX : 0) + num) * eqos->desc_size;
+ return (rx ? eqos->rx_descs : eqos->tx_descs) +
+ (num * eqos->desc_size);
}
void eqos_inval_desc_generic(void *desc)
{
- unsigned long start = (unsigned long)desc;
+ unsigned long start = (unsigned long)desc & ~(ARCH_DMA_MINALIGN - 1);
unsigned long end = ALIGN(start + sizeof(struct eqos_desc),
ARCH_DMA_MINALIGN);
@@ -104,7 +101,7 @@ void eqos_inval_desc_generic(void *desc)
void eqos_flush_desc_generic(void *desc)
{
- unsigned long start = (unsigned long)desc;
+ unsigned long start = (unsigned long)desc & ~(ARCH_DMA_MINALIGN - 1);
unsigned long end = ALIGN(start + sizeof(struct eqos_desc),
ARCH_DMA_MINALIGN);
@@ -1001,7 +998,8 @@ static int eqos_start(struct udevice *dev)
/* Set up descriptors */
- memset(eqos->descs, 0, eqos->desc_size * EQOS_DESCRIPTORS_NUM);
+ memset(eqos->tx_descs, 0, eqos->desc_size * EQOS_DESCRIPTORS_TX);
+ memset(eqos->rx_descs, 0, eqos->desc_size * EQOS_DESCRIPTORS_RX);
for (i = 0; i < EQOS_DESCRIPTORS_TX; i++) {
struct eqos_desc *tx_desc = eqos_get_desc(eqos, i, false);
@@ -1187,6 +1185,7 @@ static int eqos_recv(struct udevice *dev, int flags, uchar **packetp)
static int eqos_free_pkt(struct udevice *dev, uchar *packet, int length)
{
struct eqos_priv *eqos = dev_get_priv(dev);
+ u32 idx, idx_mask = eqos->desc_per_cacheline - 1;
uchar *packet_expected;
struct eqos_desc *rx_desc;
@@ -1202,24 +1201,30 @@ static int eqos_free_pkt(struct udevice *dev, uchar *packet, int length)
eqos->config->ops->eqos_inval_buffer(packet, length);
- rx_desc = eqos_get_desc(eqos, eqos->rx_desc_idx, true);
-
- rx_desc->des0 = 0;
- mb();
- eqos->config->ops->eqos_flush_desc(rx_desc);
- eqos->config->ops->eqos_inval_buffer(packet, length);
- rx_desc->des0 = (u32)(ulong)packet;
- rx_desc->des1 = 0;
- rx_desc->des2 = 0;
- /*
- * Make sure that if HW sees the _OWN write below, it will see all the
- * writes to the rest of the descriptor too.
- */
- mb();
- rx_desc->des3 = EQOS_DESC3_OWN | EQOS_DESC3_BUF1V;
- eqos->config->ops->eqos_flush_desc(rx_desc);
-
- writel((ulong)rx_desc, &eqos->dma_regs->ch0_rxdesc_tail_pointer);
+ if ((eqos->rx_desc_idx & idx_mask) == idx_mask) {
+ for (idx = eqos->rx_desc_idx - idx_mask;
+ idx <= eqos->rx_desc_idx;
+ idx++) {
+ rx_desc = eqos_get_desc(eqos, idx, true);
+ rx_desc->des0 = 0;
+ mb();
+ eqos->config->ops->eqos_flush_desc(rx_desc);
+ eqos->config->ops->eqos_inval_buffer(packet, length);
+ rx_desc->des0 = (u32)(ulong)(eqos->rx_dma_buf +
+ (idx * EQOS_MAX_PACKET_SIZE));
+ rx_desc->des1 = 0;
+ rx_desc->des2 = 0;
+ /*
+ * Make sure that if HW sees the _OWN write below,
+ * it will see all the writes to the rest of the
+ * descriptor too.
+ */
+ mb();
+ rx_desc->des3 = EQOS_DESC3_OWN | EQOS_DESC3_BUF1V;
+ eqos->config->ops->eqos_flush_desc(rx_desc);
+ }
+ writel((ulong)rx_desc, &eqos->dma_regs->ch0_rxdesc_tail_pointer);
+ }
eqos->rx_desc_idx++;
eqos->rx_desc_idx %= EQOS_DESCRIPTORS_RX;
@@ -1230,17 +1235,41 @@ static int eqos_free_pkt(struct udevice *dev, uchar *packet, int length)
static int eqos_probe_resources_core(struct udevice *dev)
{
struct eqos_priv *eqos = dev_get_priv(dev);
+ unsigned int desc_step;
int ret;
debug("%s(dev=%p):\n", __func__, dev);
- eqos->descs = eqos_alloc_descs(eqos, EQOS_DESCRIPTORS_NUM);
- if (!eqos->descs) {
- debug("%s: eqos_alloc_descs() failed\n", __func__);
+ /* Maximum distance between neighboring descriptors, in Bytes. */
+ desc_step = sizeof(struct eqos_desc) +
+ EQOS_DMA_CH0_CONTROL_DSL_MASK * eqos->config->axi_bus_width;
+ if (desc_step < ARCH_DMA_MINALIGN) {
+ /*
+ * The EQoS hardware implementation cannot place one descriptor
+ * per cacheline, it is necessary to place multiple descriptors
+ * per cacheline in memory and do cache management carefully.
+ */
+ eqos->desc_size = BIT(fls(desc_step) - 1);
+ } else {
+ eqos->desc_size = ALIGN(sizeof(struct eqos_desc),
+ (unsigned int)ARCH_DMA_MINALIGN);
+ }
+ eqos->desc_per_cacheline = ARCH_DMA_MINALIGN / eqos->desc_size;
+
+ eqos->tx_descs = eqos_alloc_descs(eqos, EQOS_DESCRIPTORS_TX);
+ if (!eqos->tx_descs) {
+ debug("%s: eqos_alloc_descs(tx) failed\n", __func__);
ret = -ENOMEM;
goto err;
}
+ eqos->rx_descs = eqos_alloc_descs(eqos, EQOS_DESCRIPTORS_RX);
+ if (!eqos->rx_descs) {
+ debug("%s: eqos_alloc_descs(rx) failed\n", __func__);
+ ret = -ENOMEM;
+ goto err_free_tx_descs;
+ }
+
eqos->tx_dma_buf = memalign(EQOS_BUFFER_ALIGN, EQOS_MAX_PACKET_SIZE);
if (!eqos->tx_dma_buf) {
debug("%s: memalign(tx_dma_buf) failed\n", __func__);
@@ -1276,7 +1305,9 @@ err_free_rx_dma_buf:
err_free_tx_dma_buf:
free(eqos->tx_dma_buf);
err_free_descs:
- eqos_free_descs(eqos->descs);
+ eqos_free_descs(eqos->rx_descs);
+err_free_tx_descs:
+ eqos_free_descs(eqos->tx_descs);
err:
debug("%s: returns %d\n", __func__, ret);
@@ -1292,7 +1323,8 @@ static int eqos_remove_resources_core(struct udevice *dev)
free(eqos->rx_pkt);
free(eqos->rx_dma_buf);
free(eqos->tx_dma_buf);
- eqos_free_descs(eqos->descs);
+ eqos_free_descs(eqos->rx_descs);
+ eqos_free_descs(eqos->tx_descs);
debug("%s: OK\n", __func__);
return 0;
diff --git a/drivers/net/dwc_eth_qos.h b/drivers/net/dwc_eth_qos.h
index b35e7742634..8fccd6f0572 100644
--- a/drivers/net/dwc_eth_qos.h
+++ b/drivers/net/dwc_eth_qos.h
@@ -162,6 +162,7 @@ struct eqos_dma_regs {
#define EQOS_DMA_SYSBUS_MODE_BLEN4 BIT(1)
#define EQOS_DMA_CH0_CONTROL_DSL_SHIFT 18
+#define EQOS_DMA_CH0_CONTROL_DSL_MASK 0x7
#define EQOS_DMA_CH0_CONTROL_PBLX8 BIT(16)
#define EQOS_DMA_CH0_TX_CONTROL_TXPBL_SHIFT 16
@@ -264,9 +265,11 @@ struct eqos_priv {
struct phy_device *phy;
ofnode phy_of_node;
u32 max_speed;
- void *descs;
+ void *tx_descs;
+ void *rx_descs;
int tx_desc_idx, rx_desc_idx;
unsigned int desc_size;
+ unsigned int desc_per_cacheline;
void *tx_dma_buf;
void *rx_dma_buf;
void *rx_pkt;
diff --git a/drivers/net/liteeth.c b/drivers/net/liteeth.c
new file mode 100644
index 00000000000..84d3852723e
--- /dev/null
+++ b/drivers/net/liteeth.c
@@ -0,0 +1,214 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * LiteX Liteeth Ethernet
+ *
+ * Copyright 2021 Joel Stanley <joel@jms.id.au>, IBM Corp.
+ */
+
+#include <linux/litex.h>
+
+#include <dm.h>
+#include <dm/device_compat.h>
+#include <net.h>
+
+#define LITEETH_WRITER_SLOT 0x00
+#define LITEETH_WRITER_LENGTH 0x04
+#define LITEETH_WRITER_ERRORS 0x08
+#define LITEETH_WRITER_EV_STATUS 0x0C
+#define LITEETH_WRITER_EV_PENDING 0x10
+#define LITEETH_WRITER_EV_ENABLE 0x14
+#define LITEETH_READER_START 0x18
+#define LITEETH_READER_READY 0x1C
+#define LITEETH_READER_LEVEL 0x20
+#define LITEETH_READER_SLOT 0x24
+#define LITEETH_READER_LENGTH 0x28
+#define LITEETH_READER_EV_STATUS 0x2C
+#define LITEETH_READER_EV_PENDING 0x30
+#define LITEETH_READER_EV_ENABLE 0x34
+#define LITEETH_PREAMBLE_CRC 0x38
+#define LITEETH_PREAMBLE_ERRORS 0x3C
+#define LITEETH_CRC_ERRORS 0x40
+
+struct liteeth {
+ struct udevice *dev;
+
+ void __iomem *base;
+ u32 slot_size;
+
+ /* Tx */
+ u32 tx_slot;
+ u32 num_tx_slots;
+ void __iomem *tx_base;
+
+ /* Rx */
+ u32 rx_slot;
+ u32 num_rx_slots;
+ void __iomem *rx_base;
+};
+
+static int liteeth_recv(struct udevice *dev, int flags, uchar **packetp)
+{
+ struct liteeth *priv = dev_get_priv(dev);
+ u8 rx_slot;
+ int len;
+
+ if (!litex_read8(priv->base + LITEETH_WRITER_EV_PENDING)) {
+ debug("liteeth: No packet ready\n");
+ return -EAGAIN;
+ }
+
+ rx_slot = litex_read8(priv->base + LITEETH_WRITER_SLOT);
+ len = litex_read32(priv->base + LITEETH_WRITER_LENGTH);
+
+ debug("%s: slot %d len 0x%x\n", __func__, rx_slot, len);
+
+ *packetp = priv->rx_base + rx_slot * priv->slot_size;
+
+ return len;
+}
+
+static int liteeth_free_pkt(struct udevice *dev, uchar *packet, int length)
+{
+ struct liteeth *priv = dev_get_priv(dev);
+
+ litex_write8(priv->base + LITEETH_WRITER_EV_PENDING, 1);
+
+ return 0;
+}
+
+static int liteeth_start(struct udevice *dev)
+{
+ struct liteeth *priv = dev_get_priv(dev);
+
+ /* Clear pending events */
+ litex_write8(priv->base + LITEETH_WRITER_EV_PENDING, 1);
+ litex_write8(priv->base + LITEETH_READER_EV_PENDING, 1);
+
+ /* Enable events */
+ litex_write8(priv->base + LITEETH_WRITER_EV_ENABLE, 1);
+ litex_write8(priv->base + LITEETH_READER_EV_ENABLE, 1);
+
+ return 0;
+}
+
+static void liteeth_stop(struct udevice *dev)
+{
+ struct liteeth *priv = dev_get_priv(dev);
+
+ litex_write8(priv->base + LITEETH_WRITER_EV_ENABLE, 0);
+ litex_write8(priv->base + LITEETH_READER_EV_ENABLE, 0);
+}
+
+static int liteeth_send(struct udevice *dev, void *packet, int len)
+{
+ struct liteeth *priv = dev_get_priv(dev);
+ void __iomem *txbuffer;
+
+ if (!litex_read8(priv->base + LITEETH_READER_READY)) {
+ printf("liteeth: reader not ready\n");
+ return -EAGAIN;
+ }
+
+ /* Reject oversize packets */
+ if (unlikely(len > priv->slot_size))
+ return -EMSGSIZE;
+
+ txbuffer = priv->tx_base + priv->tx_slot * priv->slot_size;
+ memcpy_toio(txbuffer, packet, len);
+ litex_write8(priv->base + LITEETH_READER_SLOT, priv->tx_slot);
+ litex_write16(priv->base + LITEETH_READER_LENGTH, len);
+ litex_write8(priv->base + LITEETH_READER_START, 1);
+
+ priv->tx_slot = (priv->tx_slot + 1) % priv->num_tx_slots;
+
+ return 0;
+}
+
+static void liteeth_setup_slots(struct liteeth *priv)
+{
+ int err;
+
+ err = ofnode_read_u32(dev_ofnode(priv->dev), "litex,rx-slots", &priv->num_rx_slots);
+ if (err) {
+ dev_dbg(priv->dev, "unable to get litex,rx-slots, using 2\n");
+ priv->num_rx_slots = 2;
+ }
+
+ err = ofnode_read_u32(dev_ofnode(priv->dev), "litex,tx-slots", &priv->num_tx_slots);
+ if (err) {
+ dev_dbg(priv->dev, "unable to get litex,tx-slots, using 2\n");
+ priv->num_tx_slots = 2;
+ }
+
+ err = ofnode_read_u32(dev_ofnode(priv->dev), "litex,slot-size", &priv->slot_size);
+ if (err) {
+ dev_dbg(priv->dev, "unable to get litex,slot-size, using 0x800\n");
+ priv->slot_size = 0x800;
+ }
+}
+
+static int liteeth_remove(struct udevice *dev)
+{
+ liteeth_stop(dev);
+
+ return 0;
+}
+
+static const struct eth_ops liteeth_ops = {
+ .start = liteeth_start,
+ .stop = liteeth_stop,
+ .send = liteeth_send,
+ .recv = liteeth_recv,
+ .free_pkt = liteeth_free_pkt,
+};
+
+static int liteeth_of_to_plat(struct udevice *dev)
+{
+ struct eth_pdata *pdata = dev_get_plat(dev);
+ struct liteeth *priv = dev_get_priv(dev);
+ void __iomem *buf_base;
+
+ pdata->iobase = dev_read_addr(dev);
+
+ priv->dev = dev;
+
+ priv->base = dev_remap_addr_name(dev, "mac");
+ if (!priv->base) {
+ dev_err(dev, "failed to map registers\n");
+ return -EINVAL;
+ }
+
+ buf_base = dev_remap_addr_name(dev, "buffer");
+ if (!buf_base) {
+ dev_err(dev, "failed to map buffer\n");
+ return -EINVAL;
+ }
+
+ liteeth_setup_slots(priv);
+
+ /* Rx slots */
+ priv->rx_base = buf_base;
+ priv->rx_slot = 0;
+
+ /* Tx slots come after Rx slots */
+ priv->tx_base = buf_base + priv->num_rx_slots * priv->slot_size;
+ priv->tx_slot = 0;
+
+ return 0;
+}
+
+static const struct udevice_id liteeth_ids[] = {
+ { .compatible = "litex,liteeth" },
+ {}
+};
+
+U_BOOT_DRIVER(liteeth) = {
+ .name = "liteeth",
+ .id = UCLASS_ETH,
+ .of_match = liteeth_ids,
+ .of_to_plat = liteeth_of_to_plat,
+ .plat_auto = sizeof(struct eth_pdata),
+ .remove = liteeth_remove,
+ .ops = &liteeth_ops,
+ .priv_auto = sizeof(struct liteeth),
+};
diff --git a/drivers/net/macb.c b/drivers/net/macb.c
index e02a57b4114..65ec1f24ad2 100644
--- a/drivers/net/macb.c
+++ b/drivers/net/macb.c
@@ -98,6 +98,9 @@ struct macb_dma_desc_64 {
#define MACB_RX_DMA_DESC_SIZE (DMA_DESC_BYTES(MACB_RX_RING_SIZE))
#define MACB_TX_DUMMY_DMA_DESC_SIZE (DMA_DESC_BYTES(1))
+#define DESC_PER_CACHELINE_32 (ARCH_DMA_MINALIGN/sizeof(struct macb_dma_desc))
+#define DESC_PER_CACHELINE_64 (ARCH_DMA_MINALIGN/DMA_DESC_SIZE)
+
#define RXBUF_FRMLEN_MASK 0x00000fff
#define TXBUF_FRMLEN_MASK 0x000007ff
@@ -401,32 +404,56 @@ static int _macb_send(struct macb_device *macb, const char *name, void *packet,
return 0;
}
+static void reclaim_rx_buffer(struct macb_device *macb,
+ unsigned int idx)
+{
+ unsigned int mask;
+ unsigned int shift;
+ unsigned int i;
+
+ /*
+ * There may be multiple descriptors per CPU cacheline,
+ * so a cache flush would flush the whole line, meaning the content of other descriptors
+ * in the cacheline would also flush. If one of the other descriptors had been
+ * written to by the controller, the flush would cause those changes to be lost.
+ *
+ * To circumvent this issue, we do the actual freeing only when we need to free
+ * the last descriptor in the current cacheline. When the current descriptor is the
+ * last in the cacheline, we free all the descriptors that belong to that cacheline.
+ */
+ if (macb->config->hw_dma_cap & HW_DMA_CAP_64B) {
+ mask = DESC_PER_CACHELINE_64 - 1;
+ shift = 1;
+ } else {
+ mask = DESC_PER_CACHELINE_32 - 1;
+ shift = 0;
+ }
+
+ /* we exit without freeing if idx is not the last descriptor in the cacheline */
+ if ((idx & mask) != mask)
+ return;
+
+ for (i = idx & (~mask); i <= idx; i++)
+ macb->rx_ring[i << shift].addr &= ~MACB_BIT(RX_USED);
+}
+
static void reclaim_rx_buffers(struct macb_device *macb,
unsigned int new_tail)
{
unsigned int i;
- unsigned int count;
i = macb->rx_tail;
macb_invalidate_ring_desc(macb, RX);
while (i > new_tail) {
- if (macb->config->hw_dma_cap & HW_DMA_CAP_64B)
- count = i * 2;
- else
- count = i;
- macb->rx_ring[count].addr &= ~MACB_BIT(RX_USED);
+ reclaim_rx_buffer(macb, i);
i++;
- if (i > MACB_RX_RING_SIZE)
+ if (i >= MACB_RX_RING_SIZE)
i = 0;
}
while (i < new_tail) {
- if (macb->config->hw_dma_cap & HW_DMA_CAP_64B)
- count = i * 2;
- else
- count = i;
- macb->rx_ring[count].addr &= ~MACB_BIT(RX_USED);
+ reclaim_rx_buffer(macb, i);
i++;
}
diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig
index 52ce08b3b38..86e698190f8 100644
--- a/drivers/net/phy/Kconfig
+++ b/drivers/net/phy/Kconfig
@@ -321,6 +321,11 @@ config PHY_XILINX_GMII2RGMII
as bridge between MAC connected over GMII and external phy that
is connected over RGMII interface.
+config PHY_XWAY
+ bool "Intel XWAY PHY support"
+ help
+ This adds support for the Intel XWAY (formerly Lantiq) Gbe PHYs.
+
config PHY_ETHERNET_ID
bool "Read ethernet PHY id"
depends on DM_GPIO
diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile
index 9d87eb212c7..d38e99e7171 100644
--- a/drivers/net/phy/Makefile
+++ b/drivers/net/phy/Makefile
@@ -34,6 +34,7 @@ obj-$(CONFIG_PHY_TI_DP83867) += dp83867.o
obj-$(CONFIG_PHY_TI_DP83869) += dp83869.o
obj-$(CONFIG_PHY_XILINX) += xilinx_phy.o
obj-$(CONFIG_PHY_XILINX_GMII2RGMII) += xilinx_gmii2rgmii.o
+obj-$(CONFIG_PHY_XWAY) += intel_xway.o
obj-$(CONFIG_PHY_ETHERNET_ID) += ethernet_id.o
obj-$(CONFIG_PHY_VITESSE) += vitesse.o
obj-$(CONFIG_PHY_MSCC) += mscc.o
diff --git a/drivers/net/phy/aquantia.c b/drivers/net/phy/aquantia.c
index 7e950fe0c2f..79fbc115368 100644
--- a/drivers/net/phy/aquantia.c
+++ b/drivers/net/phy/aquantia.c
@@ -136,7 +136,7 @@ static int aquantia_read_fw(u8 **fw_addr, size_t *fw_length)
*fw_addr = NULL;
*fw_length = 0;
- debug("Loading Acquantia microcode from %s %s\n",
+ debug("Loading Aquantia microcode from %s %s\n",
CONFIG_PHY_AQUANTIA_FW_PART, CONFIG_PHY_AQUANTIA_FW_NAME);
ret = fs_set_blk_dev("mmc", CONFIG_PHY_AQUANTIA_FW_PART, FS_TYPE_ANY);
if (ret < 0)
@@ -163,7 +163,7 @@ static int aquantia_read_fw(u8 **fw_addr, size_t *fw_length)
*fw_addr = addr;
*fw_length = length;
- debug("Found Acquantia microcode.\n");
+ debug("Found Aquantia microcode.\n");
cleanup:
if (ret < 0) {
@@ -257,7 +257,7 @@ static int aquantia_upload_firmware(struct phy_device *phydev)
strlcpy(version, (char *)&addr[dram_offset + VERSION_STRING_OFFSET],
VERSION_STRING_SIZE);
- printf("%s loading firmare version '%s'\n", phydev->dev->name, version);
+ printf("%s loading firmware version '%s'\n", phydev->dev->name, version);
/* stall the microcprocessor */
phy_write(phydev, MDIO_MMD_VEND1, UP_CONTROL,
@@ -288,7 +288,7 @@ static int aquantia_upload_firmware(struct phy_device *phydev)
phy_write(phydev, MDIO_MMD_VEND1, UP_CONTROL, UP_RUN_STALL_OVERRIDE);
- printf("%s firmare loading done.\n", phydev->dev->name);
+ printf("%s firmware loading done.\n", phydev->dev->name);
done:
free(addr);
return ret;
diff --git a/drivers/net/phy/intel_xway.c b/drivers/net/phy/intel_xway.c
new file mode 100644
index 00000000000..dfce3f8332e
--- /dev/null
+++ b/drivers/net/phy/intel_xway.c
@@ -0,0 +1,48 @@
+// SPDX-License-Identifier: GPL-2.0+
+#include <common.h>
+#include <phy.h>
+#include <linux/bitfield.h>
+
+#define XWAY_MDIO_MIICTRL 0x17 /* mii control */
+
+#define XWAY_MDIO_MIICTRL_RXSKEW_MASK GENMASK(14, 12)
+#define XWAY_MDIO_MIICTRL_TXSKEW_MASK GENMASK(10, 8)
+
+static int xway_config(struct phy_device *phydev)
+{
+ ofnode node = phy_get_ofnode(phydev);
+ u32 val = 0;
+
+ if (ofnode_valid(node)) {
+ u32 rx_delay, tx_delay;
+
+ rx_delay = ofnode_read_u32_default(node, "rx-internal-delay-ps", 2000);
+ tx_delay = ofnode_read_u32_default(node, "tx-internal-delay-ps", 2000);
+ val |= FIELD_PREP(XWAY_MDIO_MIICTRL_TXSKEW_MASK, rx_delay / 500);
+ val |= FIELD_PREP(XWAY_MDIO_MIICTRL_RXSKEW_MASK, tx_delay / 500);
+ phy_modify(phydev, MDIO_DEVAD_NONE, XWAY_MDIO_MIICTRL,
+ XWAY_MDIO_MIICTRL_TXSKEW_MASK |
+ XWAY_MDIO_MIICTRL_RXSKEW_MASK, val);
+ }
+
+ genphy_config_aneg(phydev);
+
+ return 0;
+}
+
+static struct phy_driver XWAY_driver = {
+ .name = "XWAY",
+ .uid = 0xD565A400,
+ .mask = 0xffffff00,
+ .features = PHY_GBIT_FEATURES,
+ .config = xway_config,
+ .startup = genphy_startup,
+ .shutdown = genphy_shutdown,
+};
+
+int phy_xway_init(void)
+{
+ phy_register(&XWAY_driver);
+
+ return 0;
+}
diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c
index 90876630533..92143cf2369 100644
--- a/drivers/net/phy/phy.c
+++ b/drivers/net/phy/phy.c
@@ -556,6 +556,9 @@ int phy_init(void)
#ifdef CONFIG_PHY_XILINX
phy_xilinx_init();
#endif
+#ifdef CONFIG_PHY_XWAY
+ phy_xway_init();
+#endif
#ifdef CONFIG_PHY_MSCC
phy_mscc_init();
#endif