[PATCH RESEND v4 6/8] net: add LiteEth driver
Ahmad Fatoum
a.fatoum at pengutronix.de
Fri Oct 1 03:14:14 PDT 2021
On 17.08.21 12:11, Antony Pavlov wrote:
> LiteEth provides a small footprint and configurable Ethernet core.
>
> LiteEth is part of LiteX libraries whose aims are to lower entry level of
> complex FPGA cores by providing simple, elegant and efficient implementations
> of components used in today's SoC such as Ethernet, SATA, PCIe, SDRAM Controller...
>
> Using Migen to describe the HDL allows the core to be highly and easily configurable.
>
> LiteEth can be used as LiteX library or can be integrated with your standard
> design flow by generating the verilog rtl that you will use as a standard core.
>
> See https://github.com/enjoy-digital/liteeth for details.
>
> Signed-off-by: Antony Pavlov <antonynpavlov at gmail.com>
> Signed-off-by: Marek Czerski <m.czerski at ap-tech.pl>
Acked-by: Ahmad Fatoum <a.fatoum at pengutronix.de>
> ---
> drivers/net/Kconfig | 8 +
> drivers/net/Makefile | 1 +
> drivers/net/liteeth.c | 376 ++++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 385 insertions(+)
>
> diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
> index 802169a86e..781c7e69db 100644
> --- a/drivers/net/Kconfig
> +++ b/drivers/net/Kconfig
> @@ -285,6 +285,14 @@ config TSE_USE_DEDICATED_DESC_MEM
> reserved with a malloc but directly mapped to the memory
> address (defined in config.h)
>
> +config DRIVER_NET_LITEETH
> + bool "LiteX ethernet driver"
> + select PHYLIB
> + select MDIO_BITBANG
> + help
> + This option enables support for the LiteX LiteEth
> + ethernet IP core.
> +
> source "drivers/net/phy/Kconfig"
> source "drivers/net/usb/Kconfig"
>
> diff --git a/drivers/net/Makefile b/drivers/net/Makefile
> index fb3e3bdee4..466c910b3e 100644
> --- a/drivers/net/Makefile
> +++ b/drivers/net/Makefile
> @@ -38,3 +38,4 @@ obj-$(CONFIG_DRIVER_NET_TAP) += tap.o
> obj-$(CONFIG_DRIVER_NET_TSE) += altera_tse.o
> obj-$(CONFIG_DRIVER_NET_EFI_SNP) += efi-snp.o
> obj-$(CONFIG_DRIVER_NET_AG71XX) += ag71xx.o
> +obj-$(CONFIG_DRIVER_NET_LITEETH) += liteeth.o
> diff --git a/drivers/net/liteeth.c b/drivers/net/liteeth.c
> new file mode 100644
> index 0000000000..137cb854c5
> --- /dev/null
> +++ b/drivers/net/liteeth.c
> @@ -0,0 +1,376 @@
> +/*
> + * LiteX Liteeth Ethernet
> + *
> + * Copyright 2017 Joel Stanley <joel at jms.id.au>
> + *
> + * Ported to barebox from linux kernel
> + * Copyright (C) 2019-2021 Antony Pavlov <antonynpavlov at gmail.com>
> + * Copyright (C) 2021 Marek Czerski <m.czerski at ap-tech.pl>
> + *
> + */
> +
> +#include <common.h>
> +#include <io.h>
> +#include <linux/iopoll.h>
> +#include <malloc.h>
> +#include <net.h>
> +#include <init.h>
> +#include <of_net.h>
> +#include <linux/phy.h>
> +#include <linux/mdio-bitbang.h>
> +
> +#define DRV_NAME "liteeth"
> +
> +#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
> +
> +#define LITEETH_PHY_CRG_RESET 0x00
> +#define LITEETH_MDIO_W 0x04
> +#define MDIO_W_CLK BIT(0)
> +#define MDIO_W_OE BIT(1)
> +#define MDIO_W_DO BIT(2)
> +
> +#define LITEETH_MDIO_R 0x08
> +#define MDIO_R_DI BIT(0)
> +
> +#define LITEETH_BUFFER_SIZE 0x800
> +#define MAX_PKT_SIZE LITEETH_BUFFER_SIZE
> +
> +struct liteeth {
> + struct device_d *dev;
> + struct eth_device edev;
> + void __iomem *base;
> + void __iomem *mdio_base;
> + struct mii_bus *mii_bus;
> + struct mdiobb_ctrl mdiobb;
> +
> + /* Link management */
> + int cur_duplex;
> + int cur_speed;
> +
> + /* Tx */
> + int tx_slot;
> + int num_tx_slots;
> + void __iomem *tx_base;
> +
> + /* Rx */
> + int rx_slot;
> + int num_rx_slots;
> + void __iomem *rx_base;
> +};
> +
> +static inline void litex_write8(void __iomem *addr, u8 val)
> +{
> + writeb(val, addr);
> +}
> +
> +static inline void litex_write16(void __iomem *addr, u16 val)
> +{
> + writew(val, addr);
> +}
> +
> +static inline u8 litex_read8(void __iomem *addr)
> +{
> + return readb(addr);
> +}
> +
> +static inline u32 litex_read32(void __iomem *addr)
> +{
> + return readl(addr);
> +}
> +
> +static void liteeth_mdio_w_modify(struct liteeth *priv, u8 clear, u8 set)
> +{
> + void __iomem *mdio_w = priv->mdio_base + LITEETH_MDIO_W;
> +
> + litex_write8(mdio_w, (litex_read8(mdio_w) & ~clear) | set);
> +}
> +
> +static void liteeth_mdio_ctrl(struct mdiobb_ctrl *ctrl, u8 mask, int set)
> +{
> + struct liteeth *priv = container_of(ctrl, struct liteeth, mdiobb);
> +
> + liteeth_mdio_w_modify(priv, mask, set ? mask : 0);
> +}
> +
> +/* MDC pin control */
> +static void liteeth_set_mdc(struct mdiobb_ctrl *ctrl, int level)
> +{
> + liteeth_mdio_ctrl(ctrl, MDIO_W_CLK, level);
> +}
> +
> +/* Data I/O pin control */
> +static void liteeth_set_mdio_dir(struct mdiobb_ctrl *ctrl, int output)
> +{
> + liteeth_mdio_ctrl(ctrl, MDIO_W_OE, output);
> +}
> +
> +/* Set data bit */
> +static void liteeth_set_mdio_data(struct mdiobb_ctrl *ctrl, int value)
> +{
> + liteeth_mdio_ctrl(ctrl, MDIO_W_DO, value);
> +}
> +
> +/* Get data bit */
> +static int liteeth_get_mdio_data(struct mdiobb_ctrl *ctrl)
> +{
> + struct liteeth *priv = container_of(ctrl, struct liteeth, mdiobb);
> +
> + return (litex_read8(priv->mdio_base + LITEETH_MDIO_R) & MDIO_R_DI) != 0;
> +}
> +
> +/* MDIO bus control struct */
> +static struct mdiobb_ops bb_ops = {
> + .set_mdc = liteeth_set_mdc,
> + .set_mdio_dir = liteeth_set_mdio_dir,
> + .set_mdio_data = liteeth_set_mdio_data,
> + .get_mdio_data = liteeth_get_mdio_data,
> +};
> +
> +static int liteeth_init_dev(struct eth_device *edev)
> +{
> + return 0;
> +}
> +
> +static int liteeth_eth_open(struct eth_device *edev)
> +{
> + struct liteeth *priv = edev->priv;
> + int ret;
> +
> + /* Disable events */
> + litex_write8(priv->base + LITEETH_WRITER_EV_ENABLE, 0);
> + litex_write8(priv->base + LITEETH_READER_EV_ENABLE, 0);
> +
> + /* Clear pending events? */
> + litex_write8(priv->base + LITEETH_WRITER_EV_PENDING, 1);
> + litex_write8(priv->base + LITEETH_READER_EV_PENDING, 1);
> +
> + ret = phy_device_connect(edev, priv->mii_bus, -1, NULL, 0, -1);
> + if (ret)
> + return ret;
> +
> + return 0;
> +}
> +
> +static int liteeth_eth_send(struct eth_device *edev, void *packet,
> + int packet_length)
> +{
> + struct liteeth *priv = edev->priv;
> + void *txbuffer;
> + int ret;
> + u8 val;
> + u8 reg;
> +
> + reg = litex_read8(priv->base + LITEETH_READER_EV_PENDING);
> + if (reg) {
> + litex_write8(priv->base + LITEETH_READER_EV_PENDING, reg);
> + }
> +
> + /* Reject oversize packets */
> + if (unlikely(packet_length > MAX_PKT_SIZE)) {
> + dev_err(priv->dev, "tx packet too big\n");
> + goto drop;
> + }
> +
> + txbuffer = priv->tx_base + priv->tx_slot * LITEETH_BUFFER_SIZE;
> + memcpy(txbuffer, packet, packet_length);
> + litex_write8(priv->base + LITEETH_READER_SLOT, priv->tx_slot);
> + litex_write16(priv->base + LITEETH_READER_LENGTH, packet_length);
> +
> + ret = readb_poll_timeout(priv->base + LITEETH_READER_READY,
> + val, val, 1000);
> + if (ret == -ETIMEDOUT) {
> + dev_err(priv->dev, "LITEETH_READER_READY timed out\n");
> + goto drop;
> + }
> +
> + litex_write8(priv->base + LITEETH_READER_START, 1);
> +
> + priv->tx_slot = (priv->tx_slot + 1) % priv->num_tx_slots;
> +
> +drop:
> + return 0;
> +}
> +
> +static int liteeth_eth_rx(struct eth_device *edev)
> +{
> + struct liteeth *priv = edev->priv;
> + u8 rx_slot;
> + int len = 0;
> + u8 reg;
> +
> + reg = litex_read8(priv->base + LITEETH_WRITER_EV_PENDING);
> + if (!reg) {
> + goto done;
> + }
> +
> + len = litex_read32(priv->base + LITEETH_WRITER_LENGTH);
> + if (len == 0 || len > 2048) {
> + len = 0;
> + dev_err(priv->dev, "%s: invalid len %d\n", __func__, len);
> + litex_write8(priv->base + LITEETH_WRITER_EV_PENDING, reg);
> + goto done;
> + }
> +
> + rx_slot = litex_read8(priv->base + LITEETH_WRITER_SLOT);
> +
> + memcpy(NetRxPackets[0], priv->rx_base + rx_slot * LITEETH_BUFFER_SIZE, len);
> +
> + net_receive(edev, NetRxPackets[0], len);
> +
> + litex_write8(priv->base + LITEETH_WRITER_EV_PENDING, reg);
> +
> +done:
> + return len;
> +}
> +
> +static void liteeth_eth_halt(struct eth_device *edev)
> +{
> + struct liteeth *priv = edev->priv;
> +
> + litex_write8(priv->base + LITEETH_WRITER_EV_ENABLE, 0);
> + litex_write8(priv->base + LITEETH_READER_EV_ENABLE, 0);
> +}
> +
> +static void liteeth_reset_hw(struct liteeth *priv)
> +{
> + /* Reset, twice */
> + litex_write8(priv->base + LITEETH_PHY_CRG_RESET, 0);
> + udelay(10);
> + litex_write8(priv->base + LITEETH_PHY_CRG_RESET, 1);
> + udelay(10);
> + litex_write8(priv->base + LITEETH_PHY_CRG_RESET, 0);
> + udelay(10);
> +}
> +
> +static int liteeth_get_ethaddr(struct eth_device *edev, unsigned char *m)
> +{
> + return 0;
> +}
> +
> +static int liteeth_set_ethaddr(struct eth_device *edev,
> + const unsigned char *mac_addr)
> +{
> + return 0;
> +}
> +
> +static int liteeth_probe(struct device_d *dev)
> +{
> + struct device_node *np = dev->device_node;
> + struct eth_device *edev;
> + void __iomem *buf_base;
> + struct liteeth *priv;
> + int err;
> +
> + priv = xzalloc(sizeof(struct liteeth));
> + edev = &priv->edev;
> + edev->priv = priv;
> + priv->dev = dev;
> +
> + priv->base = dev_request_mem_region(dev, 0);
> + if (IS_ERR(priv->base)) {
> + err = PTR_ERR(priv->base);
> + goto err;
> + }
> +
> + priv->mdio_base = dev_request_mem_region(dev, 1);
> + if (IS_ERR(priv->mdio_base)) {
> + err = PTR_ERR(priv->mdio_base);
> + goto err;
> + }
> +
> + buf_base = dev_request_mem_region(dev, 2);
> + if (IS_ERR(buf_base)) {
> + err = PTR_ERR(buf_base);
> + goto err;
> + }
> +
> + err = of_property_read_u32(np, "rx-fifo-depth",
> + &priv->num_rx_slots);
> + if (err) {
> + dev_err(dev, "unable to get rx-fifo-depth\n");
> + goto err;
> + }
> +
> + err = of_property_read_u32(np, "tx-fifo-depth",
> + &priv->num_tx_slots);
> + if (err) {
> + dev_err(dev, "unable to get tx-fifo-depth\n");
> + goto err;
> + }
> +
> + /* 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 * LITEETH_BUFFER_SIZE;
> + priv->tx_slot = 0;
> +
> + edev->init = liteeth_init_dev;
> + edev->open = liteeth_eth_open;
> + edev->send = liteeth_eth_send;
> + edev->recv = liteeth_eth_rx;
> + edev->get_ethaddr = liteeth_get_ethaddr;
> + edev->set_ethaddr = liteeth_set_ethaddr;
> + edev->halt = liteeth_eth_halt;
> + edev->parent = dev;
> +
> + priv->mdiobb.ops = &bb_ops;
> +
> + priv->mii_bus = alloc_mdio_bitbang(&priv->mdiobb);
> + priv->mii_bus->parent = dev;
> +
> + liteeth_reset_hw(priv);
> +
> + err = eth_register(edev);
> + if (err) {
> + dev_err(dev, "failed to register edev\n");
> + goto err;
> + }
> +
> + err = mdiobus_register(priv->mii_bus);
> + if (err) {
> + dev_err(dev, "failed to register mii_bus\n");
> + goto err;
> + }
> +
> + dev_info(dev, DRV_NAME " driver registered\n");
> +
> + return 0;
> +
> +err:
> + return err;
> +}
> +
> +static const struct of_device_id liteeth_dt_ids[] = {
> + {
> + .compatible = "litex,liteeth"
> + }, {
> + }
> +};
> +
> +static struct driver_d liteeth_driver = {
> + .name = DRV_NAME,
> + .probe = liteeth_probe,
> + .of_compatible = DRV_OF_COMPAT(liteeth_dt_ids),
> +};
> +device_platform_driver(liteeth_driver);
> +
> +MODULE_AUTHOR("Joel Stanley <joel at jms.id.au>");
> +MODULE_LICENSE("GPL");
>
--
Pengutronix e.K. | |
Steuerwalder Str. 21 | http://www.pengutronix.de/ |
31137 Hildesheim, Germany | Phone: +49-5121-206917-0 |
Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 |
More information about the barebox
mailing list