netdev.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Apeksha Gupta <apeksha.gupta@nxp.com>
To: qiangqing.zhang@nxp.com, davem@davemloft.net, kuba@kernel.org
Cc: netdev@vger.kernel.org, linux-kernel@vger.kernel.org,
	linux-devel@linux.nxdi.nxp.com, LnxRevLi@nxp.com,
	sachin.saxena@nxp.com, hemant.agrawal@nxp.com,
	nipun.gupta@nxp.com, Apeksha Gupta <apeksha.gupta@nxp.com>
Subject: [PATCH 2/5] net: fec: fec-uio driver
Date: Wed, 10 Nov 2021 11:18:35 +0530	[thread overview]
Message-ID: <20211110054838.27907-3-apeksha.gupta@nxp.com> (raw)
In-Reply-To: <20211110054838.27907-1-apeksha.gupta@nxp.com>

i.mx: fec-uio driver

This patch adds the userspace support. In this basic
hardware initialization is performed in kernel via userspace
input/output, while the majority of code is written in the
userspace.

Module fec-uio.ko is generated.
imx8mm-evk-dpdk.dtb is required to support fec-uio driver.

Signed-off-by: Sachin Saxena <sachin.saxena@nxp.com>
Signed-off-by: Apeksha Gupta <apeksha.gupta@nxp.com>
---
 drivers/net/ethernet/freescale/Kconfig   |  10 +
 drivers/net/ethernet/freescale/Makefile  |   7 +-
 drivers/net/ethernet/freescale/fec_uio.c | 437 +++++++++++++++++++++++
 3 files changed, 451 insertions(+), 3 deletions(-)
 create mode 100644 drivers/net/ethernet/freescale/fec_uio.c

diff --git a/drivers/net/ethernet/freescale/Kconfig b/drivers/net/ethernet/freescale/Kconfig
index e04e1c5cb013..c41289e9ebfa 100644
--- a/drivers/net/ethernet/freescale/Kconfig
+++ b/drivers/net/ethernet/freescale/Kconfig
@@ -33,6 +33,16 @@ config FEC
 	  Say Y here if you want to use the built-in 10/100 Fast ethernet
 	  controller on some Motorola ColdFire and Freescale i.MX processors.
 
+config FEC_UIO
+	tristate "FEC_UIO ethernet controller (i.MX 8M Mini CPU)"
+	default n
+	select UIO
+	help
+	  Say Y here if you want to use the built-in 10/100/1000-Mbit/s Fast
+	  ethernet controller on Freescale i.MX 8M Mini processor. Basic
+	  hardware initialization is performed in kernel via UIO, fec-uio
+	  driver adds the userspace support.
+
 config FEC_MPC52xx
 	tristate "FEC MPC52xx driver"
 	depends on PPC_MPC52xx && PPC_BESTCOMM
diff --git a/drivers/net/ethernet/freescale/Makefile b/drivers/net/ethernet/freescale/Makefile
index 61d417694e0e..a32c807a626b 100644
--- a/drivers/net/ethernet/freescale/Makefile
+++ b/drivers/net/ethernet/freescale/Makefile
@@ -2,11 +2,12 @@
 #
 # Makefile for the Freescale network device drivers.
 #
-
-common-objs := fec_phy.o
+common-obj := fec_phy.o
 
 obj-$(CONFIG_FEC) += fec.o
-fec-objs :=fec_main.o fec_ptp.o $(common-objs)
+fec-objs :=fec_main.o fec_ptp.o $(common-obj)
+obj-$(CONFIG_FEC_UIO) += enetfec_uio.o
+enetfec_uio-objs := fec_uio.o $(common-obj)
 
 obj-$(CONFIG_FEC_MPC52xx) += fec_mpc52xx.o
 ifeq ($(CONFIG_FEC_MPC52xx_MDIO),y)
diff --git a/drivers/net/ethernet/freescale/fec_uio.c b/drivers/net/ethernet/freescale/fec_uio.c
new file mode 100644
index 000000000000..5aa90e1f5c55
--- /dev/null
+++ b/drivers/net/ethernet/freescale/fec_uio.c
@@ -0,0 +1,437 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2021 NXP
+ */
+
+#include <linux/kernel.h>
+#include <linux/of_platform.h>
+#include <linux/uio_driver.h>
+#include <linux/pm_runtime.h>
+#include <linux/errno.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/regulator/consumer.h>
+#include <linux/clk.h>
+#include <linux/of_gpio.h>
+#include <linux/of_net.h>
+
+#include "fec.h"
+#include "fec_phy.h"
+
+struct fec_dev *fec_dev;
+static const char fec_uio_version[] = "FEC UIO driver v1.0";
+dma_addr_t bd_dma;
+int bd_size;
+struct bufdesc *cbd_base;
+
+#define NAME_LENGTH		10
+#define DRIVER_NAME		"fec-uio"
+#define FEC_PRIV_SIZE		200
+#define FEC_MAX_Q		3
+#define ENABLE_ENET		BIT(8)
+#define ETHER_EN		0x2
+
+static const char uio_device_name[] = "imx-fec-uio";
+struct fec_uio_info {
+	atomic_t ref; /* exclusive, only one open() at a time */
+	struct uio_info uio_info;
+	char name[NAME_LENGTH];
+};
+
+struct fec_dev {
+	u32 index;
+	struct device *dev;
+	struct resource *res;
+	struct fec_uio_info info;
+};
+
+struct fec_uio_devinfo {
+	u32 quirks;
+};
+
+static const struct fec_uio_devinfo fec_imx8mm_info = {
+	.quirks = FEC_QUIRK_ENET_MAC,
+};
+
+static struct platform_device_id fec_enet_uio_devtype[] = {
+	{
+		.name = DRIVER_NAME,
+		.driver_data = (kernel_ulong_t)&fec_imx8mm_info,
+	}, {
+		.name = "imx8mm-fec",
+		.driver_data = (kernel_ulong_t)&fec_imx8mm_info,
+	}, {
+		/* sentinel */
+	}
+};
+MODULE_DEVICE_TABLE(platform, fec_enet_uio_devtype);
+
+static const struct of_device_id fec_enet_uio_ids[] = {
+	{ .compatible = "fsl,imx8mm-fec-uio", .data = &fec_enet_uio_devtype },
+	{ /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, fec_enet_uio_ids);
+
+static unsigned char macaddr[ETH_ALEN];
+module_param_array(macaddr, byte, NULL, 0);
+MODULE_PARM_DESC(macaddr, "FEC Ethernet MAC address");
+
+static int fec_uio_open(struct uio_info *info, struct inode *inode)
+{
+	return 0;
+}
+
+static int fec_uio_release(struct uio_info *info, struct inode *inode)
+{
+	return 0;
+}
+
+static int fec_uio_mmap(struct uio_info *info, struct vm_area_struct *vma)
+{
+	u32 ret;
+	u32 pfn;
+
+	pfn = (info->mem[vma->vm_pgoff].addr) >> PAGE_SHIFT;
+
+	if (vma->vm_pgoff)
+		vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+	else
+		vma->vm_page_prot = pgprot_device(vma->vm_page_prot);
+
+	ret = remap_pfn_range(vma, vma->vm_start, pfn,
+			      vma->vm_end - vma->vm_start, vma->vm_page_prot);
+	if (ret) {
+		/* Error Handle */
+		pr_info("remap_pfn_range failed");
+	}
+	return ret;
+}
+
+static int __init fec_uio_init(struct fec_dev *fec_dev)
+{
+	struct fec_uio_info *fec_uio_info;
+	int ret;
+
+	fec_uio_info = &fec_dev->info;
+	atomic_set(&fec_uio_info->ref, 0);
+	fec_uio_info->uio_info.version = fec_uio_version;
+	fec_uio_info->uio_info.name = fec_dev->info.name;
+
+	fec_uio_info->uio_info.mem[0].name = "FEC_REG_SPACE";
+	fec_uio_info->uio_info.mem[0].addr = fec_dev->res->start;
+	fec_uio_info->uio_info.mem[0].size = 0x1000;
+	fec_uio_info->uio_info.mem[0].internal_addr = 0;
+	fec_uio_info->uio_info.mem[0].memtype = UIO_MEM_PHYS;
+
+	fec_uio_info->uio_info.mem[1].name = "FEC_BD_SPACE";
+	fec_uio_info->uio_info.mem[1].addr = bd_dma;
+	fec_uio_info->uio_info.mem[1].size = bd_size;
+	fec_uio_info->uio_info.mem[1].memtype = UIO_MEM_PHYS;
+
+	fec_uio_info->uio_info.open = fec_uio_open;
+	fec_uio_info->uio_info.release = fec_uio_release;
+	/* Custom mmap function. */
+	fec_uio_info->uio_info.mmap = fec_uio_mmap;
+	fec_uio_info->uio_info.priv = fec_dev;
+
+	ret = uio_register_device(fec_dev->dev, &fec_uio_info->uio_info);
+	/* return if driver requests probe retry */
+	if (ret == -EPROBE_DEFER)
+		return ret;
+	if (ret) {
+		dev_err(fec_dev->dev, "fec_uio: UIO registration failed\n");
+		return ret;
+	}
+	return 0;
+}
+
+/* Calculate the Rx,Tx ring size to get the total buffer discriptor(BD)
+ * size and allocate memory for BD ring.
+ */
+static int fec_enet_uio_init(struct net_device *ndev)
+{
+	unsigned int total_tx_ring_size = 0, total_rx_ring_size = 0;
+	struct fec_enet_private *fep = netdev_priv(ndev);
+	unsigned int dsize = sizeof(struct bufdesc);
+	unsigned short tx_ring_size, rx_ring_size;
+	int ret, i;
+
+	/* Check mask of the streaming and coherent API */
+	ret = dma_set_mask_and_coherent(&fep->pdev->dev, DMA_BIT_MASK(32));
+	if (ret < 0) {
+		dev_warn(&fep->pdev->dev, "No suitable DMA available\n");
+		return ret;
+	}
+
+	tx_ring_size = TX_RING_SIZE;
+	rx_ring_size = RX_RING_SIZE;
+
+	for (i = 0; i <	FEC_ENET_MAX_TX_QS; i++)
+		total_tx_ring_size += tx_ring_size;
+	for (i = 0; i <	FEC_ENET_MAX_RX_QS; i++)
+		total_rx_ring_size += rx_ring_size;
+
+	bd_size = (total_tx_ring_size + total_rx_ring_size) * dsize;
+
+	/* Allocate memory for buffer descriptors. */
+	cbd_base = dma_alloc_coherent(&fep->pdev->dev, bd_size, &bd_dma,
+				      GFP_KERNEL);
+	if (!cbd_base) {
+		ret = -ENOMEM;
+		goto free_mem;
+	}
+
+	return 0;
+free_mem:
+	dma_free_coherent(&fep->pdev->dev, bd_size, cbd_base, bd_dma);
+	return ret;
+}
+
+static int
+fec_enet_uio_probe(struct platform_device *pdev)
+{
+	struct fec_uio_devinfo *dev_info;
+	const struct of_device_id *of_id;
+	struct fec_enet_private *fep;
+	struct net_device *ndev;
+	u32 ecntl = ETHER_EN;
+	static int dev_id;
+	bool reset_again;
+	int ret = 0;
+
+	/* Init network device */
+	ndev = alloc_etherdev_mq(sizeof(struct fec_enet_private) +
+				FEC_PRIV_SIZE, FEC_MAX_Q);
+	if (!ndev)
+		return -ENOMEM;
+
+	SET_NETDEV_DEV(ndev, &pdev->dev);
+
+	/* setup board info structure */
+	fep = netdev_priv(ndev);
+
+	of_id = of_match_device(fec_enet_uio_ids, &pdev->dev);
+	if (of_id)
+		pdev->id_entry = of_id->data;
+
+	dev_info = (struct fec_uio_devinfo *)pdev->id_entry->driver_data;
+	if (dev_info)
+		fep->quirks = dev_info->quirks;
+
+	/* Select default pin state */
+	pinctrl_pm_select_default_state(&pdev->dev);
+
+	/* allocate memory for uio structure */
+	fec_dev = kzalloc(sizeof(*fec_dev), GFP_KERNEL);
+	if (!fec_dev)
+		return -ENOMEM;
+
+	snprintf(fec_dev->info.name, sizeof(fec_dev->info.name) - 1,
+		 "%s", uio_device_name);
+
+	fec_dev->dev = &pdev->dev;
+
+	fec_dev->res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	fep->hwp = ioremap(fec_dev->res->start, 0x1000);
+	if (IS_ERR(fep->hwp)) {
+		ret = PTR_ERR(fep->hwp);
+		goto failed_ioremap;
+	}
+	fep->pdev = pdev;
+	fep->dev_id = dev_id++;
+
+	platform_set_drvdata(pdev, ndev);
+
+	fep->clk_ipg = devm_clk_get(&pdev->dev, "ipg");
+	if (IS_ERR(fep->clk_ipg)) {
+		ret = PTR_ERR(fep->clk_ipg);
+		goto failed_clk;
+	}
+
+	fep->clk_ahb = devm_clk_get(&pdev->dev, "ahb");
+	if (IS_ERR(fep->clk_ahb)) {
+		ret = PTR_ERR(fep->clk_ahb);
+		goto failed_clk;
+	}
+
+	/* enet_out is optional, depends on board */
+	fep->clk_enet_out = devm_clk_get(&pdev->dev, "enet_out");
+	if (IS_ERR(fep->clk_enet_out))
+		fep->clk_enet_out = NULL;
+
+	/* clk_ref is optional, depends on board */
+	fep->clk_ref = devm_clk_get(&pdev->dev, "enet_clk_ref");
+	if (IS_ERR(fep->clk_ref))
+		fep->clk_ref = NULL;
+
+	ret = clk_prepare_enable(fep->clk_enet_out);
+	if (ret)
+		return ret;
+
+	ret = clk_prepare_enable(fep->clk_ref);
+	if (ret)
+		goto failed_clk_ref;
+
+	fec_enet_phy_reset_after_clk_enable(ndev);
+
+	ret = clk_prepare_enable(fep->clk_ipg);
+	if (ret)
+		goto failed_clk_ipg;
+
+	ret = clk_prepare_enable(fep->clk_ahb);
+	if (ret)
+		goto failed_clk_ahb;
+
+	fep->reg_phy = devm_regulator_get_optional(&pdev->dev, "phy");
+	if (!IS_ERR(fep->reg_phy)) {
+		ret = regulator_enable(fep->reg_phy);
+		if (ret) {
+			dev_err(&pdev->dev,
+				"Failed to enable phy regulator: %d\n", ret);
+			goto failed_regulator;
+		}
+	} else {
+		if (PTR_ERR(fep->reg_phy) == -EPROBE_DEFER) {
+			ret = -EPROBE_DEFER;
+			goto failed_regulator;
+		}
+		fep->reg_phy = NULL;
+	}
+
+	pm_runtime_enable(&pdev->dev);
+	ret = fec_reset_phy(pdev);
+	if (ret)
+		goto failed_reset;
+
+	ret = fec_enet_uio_init(ndev);
+	if (ret)
+		goto failed_init;
+
+	/* Register UIO */
+	ret = fec_uio_init(fec_dev);
+	if (ret) {
+		/* return if driver requests probe retry */
+		if (ret == -EPROBE_DEFER) {
+			dev_info(&pdev->dev,
+				 "Driver request probe retry: %s\n", __func__);
+			goto out_unmap;
+		} else {
+			dev_err(&pdev->dev, "UIO init Failed\n");
+			goto abort;
+		}
+	}
+	dev_info(fec_dev->dev, "UIO device full name %s initialized\n",
+		 fec_dev->info.name);
+
+	if (fep->quirks & FEC_QUIRK_ENET_MAC) {
+		/* enable ENET endian swap */
+		ecntl |= ENABLE_ENET;
+		/* enable ENET store and forward mode */
+		writel(ENABLE_ENET, fep->hwp + FEC_X_WMRK);
+	}
+
+	/* And last, enable the transmit and receive processing */
+	writel(ecntl, fep->hwp + FEC_ECNTRL);
+
+	ret = fec_enet_mii_init(pdev);
+	if (ret)
+		goto failed_mii_init;
+
+	if (ndev->phydev && ndev->phydev->drv)
+		reset_again = false;
+	else
+		reset_again = true;
+
+	return 0;
+
+failed_mii_init:
+failed_init:
+failed_reset:
+	pm_runtime_disable(&pdev->dev);
+	if (fep->reg_phy)
+		regulator_disable(fep->reg_phy);
+failed_clk_ref:
+	clk_disable_unprepare(fep->clk_enet_out);
+failed_regulator:
+	clk_disable_unprepare(fep->clk_ahb);
+failed_clk_ahb:
+	clk_disable_unprepare(fep->clk_ipg);
+failed_clk_ipg:
+	clk_disable_unprepare(fep->clk_enet_out);
+	clk_disable_unprepare(fep->clk_ref);
+failed_clk:
+	dev_id--;
+failed_ioremap:
+	free_netdev(ndev);
+
+	return ret;
+out_unmap:
+	dev_id--;
+	kfree(fec_dev);
+	iounmap(fep->hwp);
+	dma_free_coherent(&fep->pdev->dev, bd_size, cbd_base, bd_dma);
+	free_netdev(ndev);
+	clk_disable_unprepare(fep->clk_ahb);
+	clk_disable_unprepare(fep->clk_ipg);
+	clk_disable_unprepare(fep->clk_enet_out);
+	clk_disable_unprepare(fep->clk_ref);
+	pm_runtime_disable(&pdev->dev);
+
+	return -EPROBE_DEFER;
+abort:
+	return ret;
+}
+
+static int
+fec_enet_uio_remove(struct platform_device *pdev)
+{
+	struct net_device *ndev = platform_get_drvdata(pdev);
+	struct fec_enet_private *fep = netdev_priv(ndev);
+
+	kfree(fec_dev);
+	iounmap(fep->hwp);
+	dma_free_coherent(&fep->pdev->dev, bd_size, cbd_base, bd_dma);
+
+	uio_unregister_device(&fec_dev->info.uio_info);
+
+	fec_enet_mii_remove(fep);
+	if (fep->reg_phy)
+		regulator_disable(fep->reg_phy);
+
+	free_netdev(ndev);
+
+	clk_disable_unprepare(fep->clk_ahb);
+	clk_disable_unprepare(fep->clk_ipg);
+	pm_runtime_disable(&pdev->dev);
+
+	return 0;
+}
+
+static struct platform_driver fec_enet_uio_driver = {
+	.driver = {
+		.name = DRIVER_NAME,
+		.of_match_table = fec_enet_uio_ids,
+		.suppress_bind_attrs = true,
+	},
+	.id_table = fec_enet_uio_devtype,
+	.prevent_deferred_probe = false,
+	.probe = fec_enet_uio_probe,
+	.remove = fec_enet_uio_remove,
+};
+
+static int __init fec_enet_uio_load(void)
+{
+	return platform_driver_register(&fec_enet_uio_driver);
+}
+
+static void __exit fec_enet_uio_unload(void)
+{
+	platform_driver_unregister(&fec_enet_uio_driver);
+}
+
+module_init(fec_enet_uio_load);
+module_exit(fec_enet_uio_unload);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("NXP");
+MODULE_DESCRIPTION("i.MX FEC UIO Driver");
-- 
2.17.1


  parent reply	other threads:[~2021-11-10  5:48 UTC|newest]

Thread overview: 9+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-11-10  5:48 [PATCH 0/5] drivers/net: add NXP FEC-UIO driver Apeksha Gupta
2021-11-10  5:48 ` [PATCH 1/5] dt-bindings: add binding for fec-uio Apeksha Gupta
2021-11-10  5:48 ` Apeksha Gupta [this message]
2021-11-10 22:06   ` [PATCH 2/5] net: fec: fec-uio driver Andrew Lunn
2021-11-10  5:48 ` [PATCH 3/5] ARM64: defconfig: Add config for fec-uio Apeksha Gupta
2021-11-10  5:48 ` [PATCH 4/5] MAINTAINERS: add new file Apeksha Gupta
2021-11-10  5:48 ` [PATCH 5/5] arm64: dts: imx8mm-evk-dpdk: dts for fec-uio driver Apeksha Gupta
2021-11-10 22:11   ` Andrew Lunn
2021-11-11 11:06 ` [PATCH 0/5] drivers/net: add NXP FEC-UIO driver Michael Walle

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20211110054838.27907-3-apeksha.gupta@nxp.com \
    --to=apeksha.gupta@nxp.com \
    --cc=LnxRevLi@nxp.com \
    --cc=davem@davemloft.net \
    --cc=hemant.agrawal@nxp.com \
    --cc=kuba@kernel.org \
    --cc=linux-devel@linux.nxdi.nxp.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=netdev@vger.kernel.org \
    --cc=nipun.gupta@nxp.com \
    --cc=qiangqing.zhang@nxp.com \
    --cc=sachin.saxena@nxp.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).