[PATCH 6/9] net: add LiteEth driver

Roland Hieber rhi at pengutronix.de
Thu May 6 03:10:38 PDT 2021


On Wed, May 05, 2021 at 01:08:57PM +0300, 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>
> ---
>  drivers/net/Kconfig   |   8 +
>  drivers/net/Makefile  |   1 +
>  drivers/net/liteeth.c | 376 ++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 385 insertions(+)
> 
> Cc: Marek Czerski <m.czerski at ap-tech.pl>
> 
> diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
> index 0d55ea7a3b..4818f52603 100644
> --- a/drivers/net/Kconfig
> +++ b/drivers/net/Kconfig
> @@ -271,6 +271,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 656d45a868..bb751943f6 100644
> --- a/drivers/net/Makefile
> +++ b/drivers/net/Makefile
> @@ -36,3 +36,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 @@
> +/*

Could you please add an SPDX-License-Identifier header [1] here? (Maybe
also for other newly introduced files in this series, haven't checked.)

[1]: https://www.kernel.org/doc/html/latest/process/license-rules.html

 - Roland

> + * 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");
> -- 
> 2.31.1
> 
> 
> _______________________________________________
> barebox mailing list
> barebox at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/barebox
> 

-- 
Roland Hieber, Pengutronix e.K.          | r.hieber at pengutronix.de     |
Steuerwalder Str. 21                     | https://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