diff options
Diffstat (limited to 'drivers/net')
-rw-r--r-- | drivers/net/Kconfig | 5 | ||||
-rw-r--r-- | drivers/net/Makefile | 1 | ||||
-rw-r--r-- | drivers/net/dwc_eth_qos.c | 96 | ||||
-rw-r--r-- | drivers/net/dwc_eth_qos.h | 5 | ||||
-rw-r--r-- | drivers/net/liteeth.c | 214 | ||||
-rw-r--r-- | drivers/net/macb.c | 51 | ||||
-rw-r--r-- | drivers/net/phy/Kconfig | 5 | ||||
-rw-r--r-- | drivers/net/phy/Makefile | 1 | ||||
-rw-r--r-- | drivers/net/phy/aquantia.c | 8 | ||||
-rw-r--r-- | drivers/net/phy/intel_xway.c | 48 | ||||
-rw-r--r-- | drivers/net/phy/phy.c | 3 |
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 |