lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [thread-next>] [day] [month] [year] [list]
Message-ID: <20250611155402.1260634-1-khalid.mughal@intel.com>
Date: Wed, 11 Jun 2025 08:54:02 -0700
From: Khalid Mughal <khalid.mughal@...el.com>
To: intel-wired-lan@...ts.osuosl.org
Cc: netdev@...r.kernel.org,
	Khalid Mughal <khalid.mughal@...el.com>,
	Sridhar Samudrala <sridhar.samudrala@...el.com>
Subject: [PATCH] net: Add new iccnet driver

Intel(R) IPU ICCNET (Inter-Complex Communication Network) Driver:

The iccnet (Inter-Core Communication Network) driver enables sideband
channel communication between the Management-Complex and the
Compute-Complex, both powered by ARMv8 CPUs, on the Intel IPU
(Infrastructure Processing Unit). The driver establishes descriptor
rings for transmission and reception using a shared memory region
accessible to both CPU complexes. The TX ring of one CPU maps
directly to the RX ring of the other CPU.

== Initial and Evolving Use Cases ==

The initial use case was limited to simple utilities like scp, ssh,
etc. However, iccnet evolved into the primary communication channel
for iSCSI. In this scenario, Compute-Complex requires iSCSI to boot
its OS, with the Management-Complex acting as an iSCSI server
utilizing its SSD/NVMe storage. As a result, iccnet is now the
default communication interface between Management-Complex and
Compute-Complex for iSCSI. Since iSCSI relies on TCP/IP, a proper
netdev driver is required.

== Driver Design ==

The iccnet is implemented as a generic netdev driver, enabling
seamless integration with the Linux TCP/IP stack, without requiring
custom socket APIs. The driver uses ARPHRD_RAWIP, forming a
point-to-point link between Management-Complex and Compute-Complex.

It uses a reserved 2MB section of shared memory (outside the OS
domains of Management-Complex and Compute-Complex), within a larger
shared memory region. The driver follows a simple descriptor ring
model. Each descriptor includes a status word with an ownership bit
and a buffer for packet/frame data.
On transmit:
  Data is copied via memcpy() (no DMA available),
  The ownership bit is set, and
  An interrupt is triggered to notify the peer.
On receive:
  The interrupt handler processes the packet,
  Copies the data via memcpy(), and
  Resets the ownership bit.

Since the iccnet driver does not include an Ethernet header and lacks
ARP support, a static route must be added after module insertion, e.g.
    On Compute-Complex: ip route add 10.0.0.1 dev iccnet
    On Management-Complex: ip route add 10.0.0.2 dev iccnet

== Alternative Solutions Considered ==

Before developing iccnet, several existing solutions were evaluated,
but none met the requirements:
1. virtio-net: Requires a backend device model between CPU complexes.
2. veth (Virtual Ethernet): Only works within the same Linux network
   namespace and does not support shared memory communication.
3. PRMsg (Remote Processor Messaging): Cannot expose a netdev
   interface, which is required for iSCSI.
4. Mailbox Framework: Similar to RPMsg; lacks netdev support,
   making it unsuitable for iSCSI.

Signed-off-by: Khalid Mughal <khalid.mughal@...el.com>
Reviewed-by: Sridhar Samudrala <sridhar.samudrala@...el.com>
---
v2:
- Fixed issues highlighted by Marcin Szycik
v3:
- Fixed internal-kbuild-all build warning
v4:
- Changed iccnet header padding
---
 .../devicetree/bindings/net/intel,iccnet.yaml |  71 +++
 drivers/net/Kconfig                           |  10 +
 drivers/net/Makefile                          |   1 +
 drivers/net/iccnet.c                          | 541 ++++++++++++++++++
 4 files changed, 623 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/net/intel,iccnet.yaml
 create mode 100644 drivers/net/iccnet.c

diff --git a/Documentation/devicetree/bindings/net/intel,iccnet.yaml b/Documentation/devicetree/bindings/net/intel,iccnet.yaml
new file mode 100644
index 000000000000..bdd42c64e6a7
--- /dev/null
+++ b/Documentation/devicetree/bindings/net/intel,iccnet.yaml
@@ -0,0 +1,71 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/net/intel,iccnet.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Intel IPU ICCNET interface
+
+maintainers:
+  - Khalid Mughal <khalid.mughal@...el.com>
+
+properties:
+  compatible:
+    items:
+      - const: intel,iccnet
+
+  reg:
+    description:
+      physical address and sizes of shared memory region
+    maxItems: 1
+
+  rxring-offset:
+    description:
+      offset of start of receive descriptor ring
+    maxItems: 1
+
+  txring-offset:
+    description:
+      offset of start of transmit descriptor ring
+    maxItems: 1
+
+  ipc-reg:
+    description:
+      interrupt/status register address
+    maxItems: 1
+
+  interrupts:
+    description:
+      interrupt specifier specific to interrupt controller
+    maxItems: 1
+
+required:
+  - compatible
+  - reg
+  - rxring-offset
+  - txring-offset
+  - ipc-reg
+  - interrupts
+
+
+allOf:
+  - $ref: ethernet-controller.yaml#
+
+unevaluatedProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/interrupt-controller/arm-gic.h>
+    soc {
+        #address-cells = <2>;
+        #size-cells = <2>;
+
+        ethernet@...8000000 {
+            compatible = "intel,iccnet";
+            reg = <0x41 0x98000000 0x0 0x200000>;
+            rxring-offset = <0x000000>;
+            txring-offset = <0x100000>;
+            ipc-reg = <0x20 0x53A10000>;
+            interrupts = <GIC_SPI 121 IRQ_TYPE_LEVEL_HIGH>;
+        };
+    };
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index 1fd5acdc73c6..27d5ceeb6e54 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -669,4 +669,14 @@ config NETDEV_LEGACY_INIT
 	  Drivers that call netdev_boot_setup_check() should select this
 	  symbol, everything else no longer needs it.
 
+config ICCNET
+	tristate "Intel IPU ICCNET interface"
+	depends on ACPI || OF
+	help
+
+	  Enables support for the Intel ICCNET (Inter-Complex
+	  Communication Network) interface used in Intel IPU
+	  (Infrastructure Processing Unit). It provides a point-to-point
+	  communication link between 2 CPU complexes over a shared memory
+	  transport.
 endif # NETDEVICES
diff --git a/drivers/net/Makefile b/drivers/net/Makefile
index 13743d0e83b5..9938b0dd9099 100644
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -90,3 +90,4 @@ obj-$(CONFIG_FUJITSU_ES) += fjes/
 obj-$(CONFIG_USB4_NET) += thunderbolt/
 obj-$(CONFIG_NETDEVSIM) += netdevsim/
 obj-$(CONFIG_NET_FAILOVER) += net_failover.o
+obj-$(CONFIG_ICCNET) += iccnet.o
diff --git a/drivers/net/iccnet.c b/drivers/net/iccnet.c
new file mode 100644
index 000000000000..693718f3b30a
--- /dev/null
+++ b/drivers/net/iccnet.c
@@ -0,0 +1,541 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright(C) 2020-2025 Intel Corporation */
+
+#include <linux/acpi.h>
+#include <linux/if_arp.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+#define DRIVER_NAME		"iccnet"
+
+#define ICCNET_STS_RX_LOOP	BIT(4) /* RX-loop is in active state */
+#define ICCNET_STS_RUNNING	BIT(8) /* operational state == running */
+#define ICCNET_STS_STOPPED	0x0    /* operational state == stopped */
+
+#define ICCNET_NAPI_WEIGHT	NAPI_POLL_WEIGHT /* Packets per NAPI poll */
+
+#define ICCNET_IPC_REG_STS	0x00   /* Status register - (Read-Only) */
+#define ICCNET_IPC_REG_CLR	0x04   /* Clear register - (Write-Clear) */
+#define ICCNET_IPC_REG_SET	0x08   /* Set/Trigger register - (Read-Write) */
+#define ICCNET_IPC_REG_SZ	16     /* Register space size (in bytes) */
+
+#define ICCNET_IPC_ENABLE	BIT(0) /* Trigger an IPC interrupt */
+#define ICCNET_IPC_CLEAR	GENMASK(31, 0) /* Clear IPC interrupt */
+
+#define ICCNET_LINK_UP		1      /* Link is up */
+#define ICCNET_LINK_DOWN	0      /* Link is down */
+
+#define ICCNET_RING_SET		BIT(31) /* Descriptor is in use */
+#define ICCNET_RING_CLR		0x0     /* Descriptor is free */
+#define ICCNET_RING_MSK		GENMASK(30, 0) /* Mask to extract frame len */
+
+#define ICCNET_MAX_MTU		4088   /* Max MTU size for iccnet */
+#define ICCNET_FRAME_LEN	ICCNET_MAX_MTU
+#define ICCNET_MIN_MTU		ETH_MIN_MTU /* Min MTU size for iccnet */
+#define ICCNET_HDR_SIZE		1024   /* Header size */
+#define ICCNET_DSC_SIZE		4092   /* Descriptor size: FRAME_LEN + 4 */
+
+#define ICCNET_RX_RING_SIZE	SZ_1M  /* Rx ring size */
+#define ICCNET_TX_RING_SIZE	SZ_1M  /* Tx ring size */
+#define ICCNET_MAX_DSC_ELEM	256    /* Max descriptor elements in a ring */
+
+struct iccnet_hdr {
+	u32 link; /* Link status (ICCNET_LINK_UP or ICCNET_LINK_DOWN) */
+	u32 rx_idx; /* Current index in the RX descriptor ring */
+	u32 tx_idx; /* Current index in the TX descriptor ring */
+	u32 nelem; /* Ring size (# of elements in the Ring) */
+
+	u64 st_rx_pkts; /* Total number of received packets */
+	u64 st_tx_pkts; /* Total number of transmitted packets */
+	u64 st_rx_bytes; /* Total number of received bytes */
+	u64 st_tx_bytes; /* Total number of transmitted bytes */
+	u64 st_rx_dropped; /* Number of received packets dropped */
+	u64 st_tx_dropped; /* Number of transmitted packets dropped */
+	u64 st_rx_errors; /* Number of receive errors */
+	u64 st_irq_valid; /* Number of valid IRQs processed */
+	u64 st_irq_none; /* Number of IRQs with no data to process */
+	u64 st_napi_poll; /* Number of times NAPI polling was triggered */
+	u64 st_napi_done; /* Number of times NAPI polling completed */
+
+	u64 rsvd[19];  /* Padding to ensure struct alignment */
+} __packed;
+
+struct iccnet_dsc {
+	u32 status; /* Status word: ownership flag + frame length */
+	u8 data[ICCNET_FRAME_LEN]; /* Packet data buffer */
+} __packed;
+static_assert(sizeof(struct iccnet_dsc) == ICCNET_DSC_SIZE, "ERR dsc_size");
+
+struct iccnet {
+	int opstate; /* Operational state flags (e.g., ICCNET_STS_RUNNING) */
+	void __iomem *base; /* Base address of shared memory (I/O mapped) */
+	void __iomem *ipc_base; /* Base address for interrupt registers */
+
+	struct net_device *ndev; /* Associated network device */
+	struct napi_struct napi; /* NAPI instance for packet reception */
+
+	spinlock_t rx_lock; /* lock to protect Rx Ring */
+	spinlock_t tx_lock; /* lock to protect Tx Ring */
+
+	struct iccnet_hdr *own_hdr; /* Pointer to TX ring header */
+	struct iccnet_dsc *tx_ring; /* Pointer to TX descriptor ring */
+	struct iccnet_hdr *peer_hdr; /* Pointer to RX ring header */
+	struct iccnet_dsc *rx_ring; /* Pointer to RX descriptor ring */
+};
+
+/**
+ * iccnet_irqhandler - Interrupt handler for iccnet
+ * @irq: Interrupt number
+ * @priv: Network device structure
+ *
+ * Handles interrupts for the iccnet device. It clears the interrupt status
+ * register and schedules NAPI if the interrupt is valid.
+ *
+ * Return: IRQ_HANDLED
+ */
+static irqreturn_t iccnet_irqhandler(int irq, void *priv)
+{
+	struct net_device *ndev = priv;
+	struct iccnet *idev;
+	u32 irqstatus;
+
+	idev = netdev_priv(ndev);
+
+	irqstatus = readl(idev->ipc_base + ICCNET_IPC_REG_STS);
+	writel(ICCNET_IPC_CLEAR, idev->ipc_base + ICCNET_IPC_REG_CLR);
+
+	if (irqstatus & ICCNET_IPC_ENABLE) {
+		if (idev->opstate & ICCNET_STS_RUNNING)
+			napi_schedule(&idev->napi);
+		idev->own_hdr->st_irq_valid++;
+	} else {
+		idev->own_hdr->st_irq_none++;
+	}
+
+	return IRQ_HANDLED;
+}
+
+/**
+ * iccnet_start - Start the network interface
+ * @ndev: Network device structure
+ *
+ * Called when the network interface is activated (IFF_UP). This function
+ * enables NAPI, starts the transmit queue, and marks the link as up.
+ *
+ * Return: 0 on success, negative error code on failure
+ */
+static int iccnet_start(struct net_device *ndev)
+{
+	struct iccnet *idev = netdev_priv(ndev);
+
+	napi_enable(&idev->napi);
+	netif_carrier_on(ndev);
+	netif_start_queue(ndev);
+	idev->own_hdr->link = ICCNET_LINK_UP;
+	idev->opstate |= ICCNET_STS_RUNNING;
+	return 0;
+}
+
+/**
+ * iccnet_stop - Stop the network interface
+ * @ndev: Network device structure
+ *
+ * Called when the network interface is deactivated. It stops the transmit
+ * queue, disables NAPI, and marks the link as down.
+ *
+ * Return: 0 on success
+ */
+static int iccnet_stop(struct net_device *ndev)
+{
+	struct iccnet *idev = netdev_priv(ndev);
+
+	if (idev->opstate & ICCNET_STS_RUNNING) {
+		idev->opstate &= ~ICCNET_STS_RUNNING;
+		netif_stop_queue(ndev);
+		netif_carrier_off(ndev);
+		napi_disable(&idev->napi);
+		idev->own_hdr->link = ICCNET_LINK_DOWN;
+	}
+
+	return 0;
+}
+
+/**
+ * iccnet_start_xmit - Transmit a packet
+ * @skb: Socket buffer containing the packet
+ * @ndev: Network device structure
+ *
+ * Handles packet transmission. It checks if the transmit ring is available,
+ * copies the packet data, updates statistics, and notifies hardware.
+ *
+ * Return: NETDEV_TX_OK on success
+ */
+static int iccnet_start_xmit(struct sk_buff *skb, struct net_device *ndev)
+{
+	struct iccnet *idev = netdev_priv(ndev);
+	struct iccnet_dsc *txrptr;
+	int pktlen;
+
+	spin_lock(&idev->tx_lock);
+	txrptr = &idev->tx_ring[idev->own_hdr->tx_idx];
+
+	if ((txrptr->status & ICCNET_RING_SET) ||
+	    idev->peer_hdr->link == ICCNET_LINK_DOWN) {
+		dev_kfree_skb_any(skb);
+		idev->own_hdr->st_tx_dropped++;
+		goto tx_exit;
+	}
+
+	pktlen = (skb->len < ICCNET_MIN_MTU) ? ICCNET_MIN_MTU : skb->len;
+	skb_copy_from_linear_data(skb, txrptr->data, skb->len);
+	idev->own_hdr->st_tx_pkts++;
+	idev->own_hdr->st_tx_bytes += pktlen;
+	txrptr->status = ICCNET_RING_SET | pktlen;
+	writel(ICCNET_IPC_ENABLE, idev->ipc_base + ICCNET_IPC_REG_SET);
+	idev->own_hdr->tx_idx =
+		(idev->own_hdr->tx_idx + 1) % ICCNET_MAX_DSC_ELEM;
+	dev_kfree_skb_any(skb);
+
+tx_exit:
+	spin_unlock(&idev->tx_lock);
+
+	return NETDEV_TX_OK;
+}
+
+/**
+ * iccnet_start_recv - Receive packets
+ * @ndev: Network device structure
+ * @budget: Maximum number of packets to process
+ *
+ * Processes received packets and passes them to the network stack.
+ *
+ * Return: Number of packets processed
+ */
+static int iccnet_start_recv(struct net_device *ndev, int budget)
+{
+	struct iccnet *idev = netdev_priv(ndev);
+	struct iccnet_dsc *rxrptr;
+	struct sk_buff *skb;
+	int npackets = 0;
+	int pktlen;
+
+	spin_lock(&idev->rx_lock);
+	idev->opstate |= ICCNET_STS_RX_LOOP;
+	rxrptr = &idev->rx_ring[idev->own_hdr->rx_idx];
+
+	while ((rxrptr->status & ICCNET_RING_SET) && budget--) {
+		pktlen = rxrptr->status & ICCNET_RING_MSK;
+		skb = napi_alloc_skb(&idev->napi, pktlen + 2);
+		if (!skb) {
+			idev->own_hdr->st_rx_dropped++;
+			break;
+		}
+		skb_reserve(skb, 2); /* adjust alignment (if needed) */
+		skb_put(skb, pktlen); /* set the length of the skb */
+		skb_copy_to_linear_data(skb, rxrptr->data, pktlen);
+		skb->dev = ndev;
+		if ((skb->data[0] & 0xf0) == 0x40)
+			skb->protocol = htons(ETH_P_IP); /* IPv4 */
+		else
+			skb->protocol = htons(ETH_P_IPV6); /* IPv6 */
+		skb->ip_summed = CHECKSUM_UNNECESSARY;
+		napi_gro_receive(&idev->napi, skb);
+		idev->own_hdr->st_rx_pkts++;
+		idev->own_hdr->st_rx_bytes += pktlen;
+		rxrptr->status = ICCNET_RING_CLR;
+		idev->own_hdr->rx_idx =
+			(idev->own_hdr->rx_idx + 1) % ICCNET_MAX_DSC_ELEM;
+		rxrptr = &idev->rx_ring[idev->own_hdr->rx_idx];
+		npackets++;
+	}
+
+	idev->opstate &= ~ICCNET_STS_RX_LOOP;
+	spin_unlock(&idev->rx_lock);
+
+	return npackets;
+}
+
+/**
+ * iccnet_napi_poll - NAPI poll function
+ * @napi: NAPI structure
+ * @budget: Maximum number of packets to process
+ *
+ * Processes received packets within the given budget. Completes polling
+ * if all packets have been processed.
+ *
+ * Return: Number of packets processed
+ */
+static int iccnet_napi_poll(struct napi_struct *napi, int budget)
+{
+	struct iccnet *idev = container_of(napi, struct iccnet, napi);
+	struct net_device *ndev = idev->ndev;
+	int npackets = budget;
+
+	if (idev->opstate == ICCNET_STS_STOPPED)
+		return 0;
+
+	if (!(idev->opstate & ICCNET_STS_RX_LOOP))
+		npackets = iccnet_start_recv(ndev, budget);
+
+	if (npackets < budget) {
+		napi_complete_done(napi, npackets);
+		idev->own_hdr->st_napi_done++;
+	}
+
+	idev->own_hdr->st_napi_poll++;
+
+	return npackets;
+}
+
+/**
+ * iccnet_get_stats64 - Get network statistics
+ * @ndev: Network device structure
+ * @stats: Pointer to statistics structure
+ *
+ * Retrieves the network statistics such as packet count, bytes transmitted,
+ * and errors.
+ */
+static void iccnet_get_stats64(struct net_device *ndev,
+			       struct rtnl_link_stats64 *stats)
+{
+	struct iccnet *idev = netdev_priv(ndev);
+
+	stats->rx_packets = idev->own_hdr->st_rx_pkts;
+	stats->rx_bytes   = idev->own_hdr->st_rx_bytes;
+	stats->tx_packets = idev->own_hdr->st_tx_pkts;
+	stats->tx_bytes   = idev->own_hdr->st_tx_bytes;
+	stats->rx_errors  = idev->own_hdr->st_rx_errors;
+	stats->rx_dropped = idev->own_hdr->st_rx_dropped;
+	stats->tx_dropped = idev->own_hdr->st_tx_dropped;
+}
+
+/**
+ * iccnet_tx_timeout - Handle transmit timeout
+ * @ndev: Network device structure
+ * @txq: Transmit queue index
+ *
+ * Called when a transmit timeout occurs. It restarts the transmit queue.
+ */
+static void iccnet_tx_timeout(struct net_device *ndev, unsigned int txq)
+{
+	netif_wake_queue(ndev);
+}
+
+/**
+ * iccnet_remove - Remove the network device
+ * @pdev: Platform device structure
+ *
+ * Unregisters and frees resources associated with the network device.
+ */
+static void iccnet_remove(struct platform_device *pdev)
+{
+	struct net_device *ndev = platform_get_drvdata(pdev);
+	struct iccnet *idev = netdev_priv(ndev);
+
+	iccnet_stop(ndev);
+	free_irq(ndev->irq, ndev);
+	unregister_netdev(ndev);
+	netif_napi_del(&idev->napi);
+	iounmap(idev->ipc_base);
+	iounmap(idev->base);
+	free_netdev(ndev);
+}
+
+static const struct net_device_ops iccnet_netdev_ops = {
+	.ndo_open			= iccnet_start,
+	.ndo_stop			= iccnet_stop,
+	.ndo_start_xmit			= iccnet_start_xmit,
+	.ndo_get_stats64		= iccnet_get_stats64,
+	.ndo_tx_timeout			= iccnet_tx_timeout
+};
+
+/**
+ * iccnet_setup - Initialize network device structure
+ * @ndev: Network device structure
+ *
+ * Sets up the network device parameters such as MTU, flags, and operations.
+ */
+static void iccnet_setup(struct net_device *ndev)
+{
+	ndev->header_ops = NULL; /* No header */
+	ndev->type = ARPHRD_RAWIP; /* Raw IP */
+	ndev->mtu = ICCNET_MAX_MTU;
+	ndev->min_mtu = ICCNET_MIN_MTU;
+	/* Point-to-Point link with no ARP, no multicast, and no broadcast */
+	ndev->flags = IFF_POINTOPOINT | IFF_NOARP;
+	ndev->flags &= ~(IFF_MULTICAST | IFF_BROADCAST);
+	ndev->netdev_ops = &iccnet_netdev_ops;
+}
+
+/**
+ * iccnet_probe - Probe function for the platform device
+ * @pdev: Platform device structure
+ *
+ * Initializes the network device, maps memory regions, and sets up the driver.
+ *
+ * Return: 0 on success, negative error code on failure
+ */
+static int iccnet_probe(struct platform_device *pdev)
+{
+	struct net_device *ndev;
+	struct resource *res;
+	struct iccnet *idev;
+	struct device *dev;
+	u64 ipc_csr_base;
+	u32 tx_offset = 0;
+	u32 rx_offset = 0;
+	u32 res_size;
+	u8 *membase;
+	int rc = 0;
+
+	dev = &pdev->dev;
+	if (!dev)
+		return -ENODEV;
+
+	ndev = alloc_netdev(sizeof(*idev), DRIVER_NAME, NET_NAME_PREDICTABLE,
+			    iccnet_setup);
+	if (!ndev)
+		return -ENOMEM;
+
+	platform_set_drvdata(pdev, ndev);
+	SET_NETDEV_DEV(ndev, &pdev->dev);
+
+	idev = netdev_priv(ndev);
+	idev->ndev = ndev;
+
+	spin_lock_init(&idev->tx_lock);
+	spin_lock_init(&idev->rx_lock);
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		netdev_err(ndev, "failed to get IORESOURCE_MEM\n");
+		rc = -ENODEV;
+		goto err;
+	}
+
+	res_size = resource_size(res);
+	if (res_size < (ICCNET_TX_RING_SIZE + ICCNET_RX_RING_SIZE)) {
+		netdev_err(ndev, "IORESOURCE_MEM size mismatch\n");
+		rc = -ENODEV;
+		goto err;
+	}
+
+	idev->base = ioremap_wc(res->start, res_size);
+	if (!idev->base) {
+		netdev_err(ndev, "could not map device\n");
+		rc = -ENOMEM;
+		goto err;
+	}
+
+	membase = idev->base;
+	if (!(is_acpi_node(dev->fwnode) || dev_of_node(dev))) {
+		netdev_err(ndev, "Can't get data from DT or ACPI\n");
+		rc = -ENXIO;
+		goto err;
+	}
+
+	rc = device_property_read_u32(dev, "rxring-offset", &rx_offset);
+	if (rc) {
+		netdev_err(ndev, "invalid rxring-offset\n");
+		goto err;
+	}
+
+	rc = device_property_read_u32(dev, "txring-offset", &tx_offset);
+	if (rc) {
+		netdev_err(ndev, "invalid txring-offset\n");
+		goto err;
+	}
+
+	rc = device_property_read_u64(dev, "ipc-reg", &ipc_csr_base);
+	if (rc) {
+		netdev_err(ndev, "invalid ipc-reg\n");
+		goto err;
+	}
+
+	idev->ipc_base =
+		ioremap(ipc_csr_base, ICCNET_IPC_REG_SZ);
+	if (!idev->ipc_base) {
+		netdev_err(ndev, "could not map IPC CSRs\n");
+		rc = -ENOMEM;
+		goto err;
+	}
+
+	idev->own_hdr = (struct iccnet_hdr *)(membase + tx_offset);
+	memset(idev->own_hdr, 0, ICCNET_HDR_SIZE);
+	idev->own_hdr->nelem = ICCNET_MAX_DSC_ELEM;
+	idev->tx_ring = (struct iccnet_dsc *)
+		(membase + tx_offset + ICCNET_HDR_SIZE);
+	memset(idev->tx_ring, 0,
+	       sizeof(struct iccnet_dsc) * ICCNET_MAX_DSC_ELEM);
+	idev->rx_ring = (struct iccnet_dsc *)
+		(membase + rx_offset + ICCNET_HDR_SIZE);
+	memset(idev->rx_ring, 0,
+	       sizeof(struct iccnet_dsc) * ICCNET_MAX_DSC_ELEM);
+	idev->peer_hdr = (struct iccnet_hdr *)(membase + rx_offset);
+	idev->peer_hdr->rx_idx = 0; idev->peer_hdr->tx_idx = 0;
+
+	ndev->irq = platform_get_irq(pdev, 0);
+	if (ndev->irq < 0)
+		goto err;
+
+	writel(ICCNET_IPC_CLEAR, idev->ipc_base + ICCNET_IPC_REG_CLR);
+	rc = request_irq(ndev->irq,
+			 iccnet_irqhandler, 0, ndev->name, ndev);
+	if (rc < 0) {
+		netdev_err(ndev, "error requesting interrupt\n");
+		goto err;
+	}
+
+	rc = register_netdev(ndev);
+	if (rc < 0)
+		goto err;
+
+	netif_napi_add(ndev, &idev->napi, iccnet_napi_poll);
+
+	idev->opstate = ICCNET_STS_STOPPED;
+
+	netdev_info(ndev, "MTU:%u IRQ:%d OF:0x%08x\n",
+		    ICCNET_FRAME_LEN, ndev->irq, tx_offset);
+
+	return 0;
+err:
+	if (ndev->irq)
+		free_irq(ndev->irq, ndev);
+	if (idev->base)
+		iounmap(idev->base);
+	if (idev->ipc_base)
+		iounmap(idev->ipc_base);
+	free_netdev(ndev);
+	return rc;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id iccnet_of_match[] = {
+	{ .compatible = "intel,icc_net" },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, iccnet_of_match);
+#endif
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id iccnet_acpi_match[] = {
+	{ "INTC10C4", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(acpi, iccnet_acpi_match);
+#endif
+
+static struct platform_driver iccnet_driver = {
+	.probe	= iccnet_probe,
+	.remove = iccnet_remove,
+	.driver = {
+		.name = DRIVER_NAME,
+		.of_match_table = iccnet_of_match,
+		.acpi_match_table = ACPI_PTR(iccnet_acpi_match),
+	},
+};
+
+module_platform_driver(iccnet_driver);
+
+MODULE_DESCRIPTION("Intel(R) IPU ICCNET Driver");
+MODULE_LICENSE("GPL v2");
-- 
2.43.0


Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ