RE: [PATCH v8 1/2] platform/mellanox: Add TmFifo driver for Mellanox BlueField Soc

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 




> -----Original Message-----
> From: Liming Sun <lsun@xxxxxxxxxxxx>
> Sent: Monday, January 28, 2019 7:28 PM
> To: Rob Herring <robh+dt@xxxxxxxxxx>; Mark Rutland
> <mark.rutland@xxxxxxx>; Arnd Bergmann <arnd@xxxxxxxx>; David Woods
> <dwoods@xxxxxxxxxxxx>; Andy Shevchenko <andy@xxxxxxxxxxxxx>; Darren
> Hart <dvhart@xxxxxxxxxxxxx>; Vadim Pasternak <vadimp@xxxxxxxxxxxx>
> Cc: Liming Sun <lsun@xxxxxxxxxxxx>; devicetree@xxxxxxxxxxxxxxx; linux-
> kernel@xxxxxxxxxxxxxxx; platform-driver-x86@xxxxxxxxxxxxxxx
> Subject: [PATCH v8 1/2] platform/mellanox: Add TmFifo driver for Mellanox
> BlueField Soc
> 
> This commit adds the TmFifo platform driver for Mellanox BlueField Soc. TmFifo
> is a shared FIFO which enables external host machine to exchange data with the
> SoC via USB or PCIe. The driver is based on virtio framework and has console
> and network access enabled.
> 
> Reviewed-by: David Woods <dwoods@xxxxxxxxxxxx>
> Signed-off-by: Liming Sun <lsun@xxxxxxxxxxxx>
> ---
>  drivers/platform/mellanox/Kconfig             |   13 +-
>  drivers/platform/mellanox/Makefile            |    1 +
>  drivers/platform/mellanox/mlxbf-tmfifo-regs.h |   67 ++
>  drivers/platform/mellanox/mlxbf-tmfifo.c      | 1289
> +++++++++++++++++++++++++
>  4 files changed, 1369 insertions(+), 1 deletion(-)  create mode 100644
> drivers/platform/mellanox/mlxbf-tmfifo-regs.h
>  create mode 100644 drivers/platform/mellanox/mlxbf-tmfifo.c
> 
> diff --git a/drivers/platform/mellanox/Kconfig
> b/drivers/platform/mellanox/Kconfig
> index cd8a908..a565070 100644
> --- a/drivers/platform/mellanox/Kconfig
> +++ b/drivers/platform/mellanox/Kconfig
> @@ -5,7 +5,7 @@
> 
>  menuconfig MELLANOX_PLATFORM
>  	bool "Platform support for Mellanox hardware"
> -	depends on X86 || ARM || COMPILE_TEST
> +	depends on X86 || ARM || ARM64 || COMPILE_TEST
>  	---help---
>  	  Say Y here to get to see options for platform support for
>  	  Mellanox systems. This option alone does not add any kernel code.
> @@ -34,4 +34,15 @@ config MLXREG_IO
>  	  to system resets operation, system reset causes monitoring and some
>  	  kinds of mux selection.
> 
> +config MLXBF_TMFIFO
> +	tristate "Mellanox BlueField SoC TmFifo platform driver"
> +	depends on ARM64

Why you make it dependent on ARM64?
Should not it work on any host, x86?

> +	default m

User who needs it should select this option.
No need default 'm'.

> +	select VIRTIO_CONSOLE
> +	select VIRTIO_NET
> +	help
> +	  Say y here to enable TmFifo support. The TmFifo driver provides
> +          platform driver support for the TmFifo which supports console
> +          and networking based on the virtio framework.
> +
>  endif # MELLANOX_PLATFORM
> diff --git a/drivers/platform/mellanox/Makefile
> b/drivers/platform/mellanox/Makefile
> index 57074d9c..f0c061d 100644
> --- a/drivers/platform/mellanox/Makefile
> +++ b/drivers/platform/mellanox/Makefile
> @@ -5,3 +5,4 @@
>  #
>  obj-$(CONFIG_MLXREG_HOTPLUG)	+= mlxreg-hotplug.o
>  obj-$(CONFIG_MLXREG_IO) += mlxreg-io.o
> +obj-$(CONFIG_MLXBF_TMFIFO)	+= mlxbf-tmfifo.o
> diff --git a/drivers/platform/mellanox/mlxbf-tmfifo-regs.h
> b/drivers/platform/mellanox/mlxbf-tmfifo-regs.h
> new file mode 100644
> index 0000000..90c9c2cf
> --- /dev/null
> +++ b/drivers/platform/mellanox/mlxbf-tmfifo-regs.h
> @@ -0,0 +1,67 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (c) 2019, Mellanox Technologies. All rights reserved.
> + */
> +
> +#ifndef __MLXBF_TMFIFO_REGS_H__
> +#define __MLXBF_TMFIFO_REGS_H__
> +
> +#include <linux/types.h>
> +
> +#define MLXBF_TMFIFO_TX_DATA 0x0
> +
> +#define MLXBF_TMFIFO_TX_STS 0x8
> +#define MLXBF_TMFIFO_TX_STS__LENGTH 0x0001 #define
> +MLXBF_TMFIFO_TX_STS__COUNT_SHIFT 0 #define
> +MLXBF_TMFIFO_TX_STS__COUNT_WIDTH 9 #define
> +MLXBF_TMFIFO_TX_STS__COUNT_RESET_VAL 0 #define
> +MLXBF_TMFIFO_TX_STS__COUNT_RMASK 0x1ff #define
> +MLXBF_TMFIFO_TX_STS__COUNT_MASK  0x1ff
> +
> +#define MLXBF_TMFIFO_TX_CTL 0x10
> +#define MLXBF_TMFIFO_TX_CTL__LENGTH 0x0001 #define
> +MLXBF_TMFIFO_TX_CTL__LWM_SHIFT 0 #define
> MLXBF_TMFIFO_TX_CTL__LWM_WIDTH
> +8 #define MLXBF_TMFIFO_TX_CTL__LWM_RESET_VAL 128 #define
> +MLXBF_TMFIFO_TX_CTL__LWM_RMASK 0xff #define
> +MLXBF_TMFIFO_TX_CTL__LWM_MASK  0xff #define
> +MLXBF_TMFIFO_TX_CTL__HWM_SHIFT 8 #define
> MLXBF_TMFIFO_TX_CTL__HWM_WIDTH
> +8 #define MLXBF_TMFIFO_TX_CTL__HWM_RESET_VAL 128 #define
> +MLXBF_TMFIFO_TX_CTL__HWM_RMASK 0xff #define
> +MLXBF_TMFIFO_TX_CTL__HWM_MASK  0xff00 #define
> +MLXBF_TMFIFO_TX_CTL__MAX_ENTRIES_SHIFT 32 #define
> +MLXBF_TMFIFO_TX_CTL__MAX_ENTRIES_WIDTH 9 #define
> +MLXBF_TMFIFO_TX_CTL__MAX_ENTRIES_RESET_VAL 256 #define
> +MLXBF_TMFIFO_TX_CTL__MAX_ENTRIES_RMASK 0x1ff #define
> +MLXBF_TMFIFO_TX_CTL__MAX_ENTRIES_MASK  0x1ff00000000ULL
> +
> +#define MLXBF_TMFIFO_RX_DATA 0x0
> +
> +#define MLXBF_TMFIFO_RX_STS 0x8
> +#define MLXBF_TMFIFO_RX_STS__LENGTH 0x0001 #define
> +MLXBF_TMFIFO_RX_STS__COUNT_SHIFT 0 #define
> +MLXBF_TMFIFO_RX_STS__COUNT_WIDTH 9 #define
> +MLXBF_TMFIFO_RX_STS__COUNT_RESET_VAL 0 #define
> +MLXBF_TMFIFO_RX_STS__COUNT_RMASK 0x1ff #define
> +MLXBF_TMFIFO_RX_STS__COUNT_MASK  0x1ff
> +
> +#define MLXBF_TMFIFO_RX_CTL 0x10
> +#define MLXBF_TMFIFO_RX_CTL__LENGTH 0x0001 #define
> +MLXBF_TMFIFO_RX_CTL__LWM_SHIFT 0 #define
> MLXBF_TMFIFO_RX_CTL__LWM_WIDTH
> +8 #define MLXBF_TMFIFO_RX_CTL__LWM_RESET_VAL 128 #define
> +MLXBF_TMFIFO_RX_CTL__LWM_RMASK 0xff #define
> +MLXBF_TMFIFO_RX_CTL__LWM_MASK  0xff #define
> +MLXBF_TMFIFO_RX_CTL__HWM_SHIFT 8 #define
> MLXBF_TMFIFO_RX_CTL__HWM_WIDTH
> +8 #define MLXBF_TMFIFO_RX_CTL__HWM_RESET_VAL 128 #define
> +MLXBF_TMFIFO_RX_CTL__HWM_RMASK 0xff #define
> +MLXBF_TMFIFO_RX_CTL__HWM_MASK  0xff00 #define
> +MLXBF_TMFIFO_RX_CTL__MAX_ENTRIES_SHIFT 32 #define
> +MLXBF_TMFIFO_RX_CTL__MAX_ENTRIES_WIDTH 9 #define
> +MLXBF_TMFIFO_RX_CTL__MAX_ENTRIES_RESET_VAL 256 #define
> +MLXBF_TMFIFO_RX_CTL__MAX_ENTRIES_RMASK 0x1ff #define
> +MLXBF_TMFIFO_RX_CTL__MAX_ENTRIES_MASK  0x1ff00000000ULL
> +
> +#endif /* !defined(__MLXBF_TMFIFO_REGS_H__) */
> diff --git a/drivers/platform/mellanox/mlxbf-tmfifo.c
> b/drivers/platform/mellanox/mlxbf-tmfifo.c
> new file mode 100644
> index 0000000..c1afe47
> --- /dev/null
> +++ b/drivers/platform/mellanox/mlxbf-tmfifo.c
> @@ -0,0 +1,1289 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * Mellanox BlueField SoC TmFifo driver
> + *
> + * Copyright (C) 2019 Mellanox Technologies  */
> +
> +#include <linux/acpi.h>
> +#include <linux/bitfield.h>
> +#include <linux/cache.h>
> +#include <linux/device.h>
> +#include <linux/dma-mapping.h>
> +#include <linux/efi.h>
> +#include <linux/io.h>
> +#include <linux/interrupt.h>
> +#include <linux/irq.h>
> +#include <linux/kernel.h>
> +#include <linux/math64.h>
> +#include <linux/module.h>
> +#include <linux/moduleparam.h>
> +#include <linux/mutex.h>
> +#include <linux/platform_device.h>
> +#include <linux/resource.h>
> +#include <linux/slab.h>
> +#include <linux/types.h>
> +#include <linux/version.h>
> +#include <linux/virtio.h>
> +#include <linux/virtio_config.h>
> +#include <linux/virtio_console.h>
> +#include <linux/virtio_ids.h>
> +#include <linux/virtio_net.h>
> +#include <linux/virtio_ring.h>
> +#include <asm/byteorder.h>

Is it must ti include from asm?
Could it be replaced with something like
#include <linux/byteorder/generic.h>

> +
> +#include "mlxbf-tmfifo-regs.h"
> +
> +/* Vring size. */
> +#define MLXBF_TMFIFO_VRING_SIZE			1024
> +
> +/* Console Tx buffer size. */
> +#define MLXBF_TMFIFO_CONS_TX_BUF_SIZE		(32 * 1024)
> +
> +/* House-keeping timer interval. */
> +static int mlxbf_tmfifo_timer_interval = HZ / 10;
> +
> +/* Global lock. */
> +static DEFINE_MUTEX(mlxbf_tmfifo_lock);
> +
> +/* Virtual devices sharing the TM FIFO. */
> +#define MLXBF_TMFIFO_VDEV_MAX		(VIRTIO_ID_CONSOLE + 1)
> +
> +/* Struct declaration. */
> +struct mlxbf_tmfifo;
> +
> +/* Structure to maintain the ring state. */ struct mlxbf_tmfifo_vring {
> +	void *va;			/* virtual address */
> +	dma_addr_t dma;			/* dma address */
> +	struct virtqueue *vq;		/* virtqueue pointer */
> +	struct vring_desc *desc;	/* current desc */
> +	struct vring_desc *desc_head;	/* current desc head */
> +	int cur_len;			/* processed len in current desc */
> +	int rem_len;			/* remaining length to be processed */
> +	int size;			/* vring size */
> +	int align;			/* vring alignment */
> +	int id;				/* vring id */
> +	int vdev_id;			/* TMFIFO_VDEV_xxx */
> +	u32 pkt_len;			/* packet total length */
> +	__virtio16 next_avail;		/* next avail desc id */
> +	struct mlxbf_tmfifo *fifo;	/* pointer back to the tmfifo */
> +};
> +
> +/* Interrupt types. */
> +enum {
> +	MLXBF_TM_RX_LWM_IRQ,		/* Rx low water mark irq */
> +	MLXBF_TM_RX_HWM_IRQ,		/* Rx high water mark irq */
> +	MLXBF_TM_TX_LWM_IRQ,		/* Tx low water mark irq */
> +	MLXBF_TM_TX_HWM_IRQ,		/* Tx high water mark irq */
> +	MLXBF_TM_IRQ_CNT
> +};
> +
> +/* Ring types (Rx & Tx). */
> +enum {
> +	MLXBF_TMFIFO_VRING_RX,		/* Rx ring */
> +	MLXBF_TMFIFO_VRING_TX,		/* Tx ring */
> +	MLXBF_TMFIFO_VRING_NUM
> +};
> +
> +struct mlxbf_tmfifo_vdev {
> +	struct virtio_device vdev;	/* virtual device */
> +	u8 status;
> +	u64 features;
> +	union {				/* virtio config space */
> +		struct virtio_console_config cons;
> +		struct virtio_net_config net;
> +	} config;
> +	struct mlxbf_tmfifo_vring vrings[MLXBF_TMFIFO_VRING_NUM];
> +	u8 *tx_buf;			/* tx buffer */
> +	u32 tx_head;			/* tx buffer head */
> +	u32 tx_tail;			/* tx buffer tail */
> +};
> +
> +struct mlxbf_tmfifo_irq_info {
> +	struct mlxbf_tmfifo *fifo;	/* tmfifo structure */
> +	int irq;			/* interrupt number */
> +	int index;			/* array index */
> +};
> +
> +/* TMFIFO device structure */
> +struct mlxbf_tmfifo {
> +	struct mlxbf_tmfifo_vdev *vdev[MLXBF_TMFIFO_VDEV_MAX]; /*
> devices */
> +	struct platform_device *pdev;	/* platform device */
> +	struct mutex lock;		/* fifo lock */
> +	void __iomem *rx_base;		/* mapped register base */
> +	void __iomem *tx_base;		/* mapped register base */
> +	int tx_fifo_size;		/* number of entries of the Tx FIFO */
> +	int rx_fifo_size;		/* number of entries of the Rx FIFO */
> +	unsigned long pend_events;	/* pending bits for deferred process */
> +	struct mlxbf_tmfifo_irq_info irq_info[MLXBF_TM_IRQ_CNT]; /* irq info
> */
> +	struct work_struct work;	/* work struct for deferred process */
> +	struct timer_list timer;	/* keepalive timer */
> +	struct mlxbf_tmfifo_vring *vring[2];	/* current Tx/Rx ring */
> +	bool is_ready;			/* ready flag */
> +	spinlock_t spin_lock;		/* spin lock */
> +};
> +
> +union mlxbf_tmfifo_msg_hdr {
> +	struct {
> +		u8 type;		/* message type */
> +		__be16 len;		/* payload length */
> +		u8 unused[5];		/* reserved, set to 0 */
> +	} __packed;
> +	u64 data;
> +};
> +
> +/*
> + * Default MAC.
> + * This MAC address will be read from EFI persistent variable if configured.
> + * It can also be reconfigured with standard Linux tools.
> + */
> +static u8 mlxbf_tmfifo_net_default_mac[6] = {
> +	0x00, 0x1A, 0xCA, 0xFF, 0xFF, 0x01};
> +
> +/* MTU setting of the virtio-net interface. */
> +#define MLXBF_TMFIFO_NET_MTU		1500
> +
> +/* Maximum L2 header length. */
> +#define MLXBF_TMFIFO_NET_L2_OVERHEAD	36
> +
> +/* Supported virtio-net features. */
> +#define MLXBF_TMFIFO_NET_FEATURES	((1UL << VIRTIO_NET_F_MTU)
> | \
> +					 (1UL << VIRTIO_NET_F_STATUS) | \
> +					 (1UL << VIRTIO_NET_F_MAC))
> +
> +/* Return the consumed Tx buffer space. */ static int
> +mlxbf_tmfifo_vdev_tx_buf_len(struct mlxbf_tmfifo_vdev *vdev) {
> +	return ((vdev->tx_tail >= vdev->tx_head) ?
> +	       (vdev->tx_tail - vdev->tx_head) :
> +	       (MLXBF_TMFIFO_CONS_TX_BUF_SIZE - vdev->tx_head +
> +vdev->tx_tail)); }

I would suggest to split the above. 

> +
> +/* Return the available Tx buffer space. */ static int
> +mlxbf_tmfifo_vdev_tx_buf_avail(struct mlxbf_tmfifo_vdev *vdev) {
> +	return (MLXBF_TMFIFO_CONS_TX_BUF_SIZE - 8 -

Thins about some extra define for
"MLXBF_TMFIFO_CONS_TX_BUF_SIZE - 8"

> +		mlxbf_tmfifo_vdev_tx_buf_len(vdev));
> +}
> +
> +/* Update Tx buffer pointer after pushing data. */ static void
> +mlxbf_tmfifo_vdev_tx_buf_push(struct mlxbf_tmfifo_vdev *vdev,
> +					  u32 len)
> +{
> +	vdev->tx_tail += len;
> +	if (vdev->tx_tail >= MLXBF_TMFIFO_CONS_TX_BUF_SIZE)
> +		vdev->tx_tail -= MLXBF_TMFIFO_CONS_TX_BUF_SIZE; }
> +
> +/* Update Tx buffer pointer after popping data. */ static void
> +mlxbf_tmfifo_vdev_tx_buf_pop(struct mlxbf_tmfifo_vdev *vdev,
> +					 u32 len)
> +{
> +	vdev->tx_head += len;
> +	if (vdev->tx_head >= MLXBF_TMFIFO_CONS_TX_BUF_SIZE)
> +		vdev->tx_head -= MLXBF_TMFIFO_CONS_TX_BUF_SIZE; }
> +
> +/* Allocate vrings for the fifo. */
> +static int mlxbf_tmfifo_alloc_vrings(struct mlxbf_tmfifo *fifo,
> +				     struct mlxbf_tmfifo_vdev *tm_vdev,
> +				     int vdev_id)
> +{
> +	struct mlxbf_tmfifo_vring *vring;
> +	dma_addr_t dma;
> +	int i, size;
> +	void *va;
> +
> +	for (i = 0; i < ARRAY_SIZE(tm_vdev->vrings); i++) {
> +		vring = &tm_vdev->vrings[i];
> +		vring->fifo = fifo;
> +		vring->size = MLXBF_TMFIFO_VRING_SIZE;
> +		vring->align = SMP_CACHE_BYTES;
> +		vring->id = i;
> +		vring->vdev_id = vdev_id;
> +
> +		size = PAGE_ALIGN(vring_size(vring->size, vring->align));
> +		va = dma_alloc_coherent(tm_vdev->vdev.dev.parent, size,
> &dma,
> +					GFP_KERNEL);
> +		if (!va) {
> +			dev_err(tm_vdev->vdev.dev.parent,
> +				"vring allocation failed\n");
> +			return -EINVAL;
> +		}
> +
> +		vring->va = va;
> +		vring->dma = dma;
> +	}
> +
> +	return 0;
> +}
> +
> +/* Free vrings of the fifo device. */
> +static void mlxbf_tmfifo_free_vrings(struct mlxbf_tmfifo *fifo, int
> +vdev_id) {
> +	struct mlxbf_tmfifo_vdev *tm_vdev = fifo->vdev[vdev_id];
> +	struct mlxbf_tmfifo_vring *vring;
> +	int i, size;
> +
> +	for (i = 0; i < ARRAY_SIZE(tm_vdev->vrings); i++) {
> +		vring = &tm_vdev->vrings[i];
> +
> +		size = PAGE_ALIGN(vring_size(vring->size, vring->align));
> +		if (vring->va) {
> +			dma_free_coherent(tm_vdev->vdev.dev.parent, size,
> +					  vring->va, vring->dma);
> +			vring->va = NULL;
> +			if (vring->vq) {
> +				vring_del_virtqueue(vring->vq);
> +				vring->vq = NULL;
> +			}
> +		}
> +	}
> +}
> +
> +/* Free interrupts of the fifo device. */ static void
> +mlxbf_tmfifo_free_irqs(struct mlxbf_tmfifo *fifo) {
> +	int i, irq;
> +
> +	for (i = 0; i < MLXBF_TM_IRQ_CNT; i++) {
> +		irq = fifo->irq_info[i].irq;
> +		if (irq) {
> +			fifo->irq_info[i].irq = 0;
> +			disable_irq(irq);
> +			free_irq(irq, (u8 *)fifo + i);
> +		}
> +	}
> +}
> +
> +/* Interrupt handler. */
> +static irqreturn_t mlxbf_tmfifo_irq_handler(int irq, void *arg) {
> +	struct mlxbf_tmfifo_irq_info *irq_info;
> +
> +	irq_info = (struct mlxbf_tmfifo_irq_info *)arg;
> +
> +	if (irq_info->index < MLXBF_TM_IRQ_CNT &&
> +	    !test_and_set_bit(irq_info->index, &irq_info->fifo->pend_events))
> +		schedule_work(&irq_info->fifo->work);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +/* Nothing to do for now. */
> +static void mlxbf_tmfifo_virtio_dev_release(struct device *dev) { }

If there is nothing to do - no reason to have it.

> +
> +/* Get the next packet descriptor from the vring. */ static inline
> +struct vring_desc * mlxbf_tmfifo_virtio_get_next_desc(struct virtqueue
> +*vq) {
> +	struct mlxbf_tmfifo_vring *vring;
> +	unsigned int idx, head;
> +	struct vring *vr;
> +
> +	vring = (struct mlxbf_tmfifo_vring *)vq->priv;
> +	vr = (struct vring *)virtqueue_get_vring(vq);
> +
> +	if (!vr || vring->next_avail == vr->avail->idx)
> +		return NULL;
> +
> +	idx = vring->next_avail % vr->num;
> +	head = vr->avail->ring[idx];
> +	BUG_ON(head >= vr->num);
> +	vring->next_avail++;
> +	return &vr->desc[head];
> +}
> +
> +static inline void mlxbf_tmfifo_virtio_release_desc(
> +	struct virtio_device *vdev, struct vring *vr,
> +	struct vring_desc *desc, u32 len)
> +{
> +	unsigned int idx;
> +
> +	idx = vr->used->idx % vr->num;
> +	vr->used->ring[idx].id = desc - vr->desc;
> +	vr->used->ring[idx].len = cpu_to_virtio32(vdev, len);
> +
> +	/* Virtio could poll and check the 'idx' to decide
> +	 * whether the desc is done or not. Add a memory
> +	 * barrier here to make sure the update above completes
> +	 * before updating the idx.
> +	 */
> +	mb();
> +	vr->used->idx++;
> +}
> +
> +/* Get the total length of a descriptor chain. */ static inline u32
> +mlxbf_tmfifo_virtio_get_pkt_len(struct virtio_device *vdev,
> +						  struct vring_desc *desc,
> +						  struct vring *vr)
> +{
> +	u32 len = 0, idx;
> +
> +	while (desc) {
> +		len += virtio32_to_cpu(vdev, desc->len);
> +		if (!(virtio16_to_cpu(vdev, desc->flags) &
> VRING_DESC_F_NEXT))
> +			break;
> +		idx = virtio16_to_cpu(vdev, desc->next);
> +		desc = &vr->desc[idx];
> +	}
> +
> +	return len;
> +}
> +
> +static void mlxbf_tmfifo_release_pkt(struct virtio_device *vdev,
> +				     struct mlxbf_tmfifo_vring *vring,
> +				     struct vring_desc **desc)
> +{
> +	struct vring *vr = (struct vring *)virtqueue_get_vring(vring->vq);
> +	struct vring_desc *desc_head;
> +	uint32_t pkt_len = 0;
> +
> +	if (!vr)
> +		return;
> +
> +	if (desc != NULL && *desc != NULL && vring->desc_head != NULL) {
> +		desc_head = vring->desc_head;
> +		pkt_len = vring->pkt_len;
> +	} else {
> +		desc_head = mlxbf_tmfifo_virtio_get_next_desc(vring->vq);
> +		if (desc_head != NULL) {
> +			pkt_len = mlxbf_tmfifo_virtio_get_pkt_len(
> +				vdev, desc_head, vr);
> +		}
> +	}
> +
> +	if (desc_head != NULL)
> +		mlxbf_tmfifo_virtio_release_desc(vdev, vr, desc_head,
> pkt_len);
> +
> +	if (desc != NULL)
> +		*desc = NULL;
> +	vring->pkt_len = 0;
> +}
> +
> +/* House-keeping timer. */
> +static void mlxbf_tmfifo_timer(struct timer_list *arg) {
> +	struct mlxbf_tmfifo *fifo;
> +
> +	fifo = container_of(arg, struct mlxbf_tmfifo, timer);
> +
> +	/*
> +	 * Wake up the work handler to poll the Rx FIFO in case interrupt
> +	 * missing or any leftover bytes stuck in the FIFO.
> +	 */
> +	test_and_set_bit(MLXBF_TM_RX_HWM_IRQ, &fifo->pend_events);
> +
> +	/*
> +	 * Wake up Tx handler in case virtio has queued too many packets
> +	 * and are waiting for buffer return.
> +	 */
> +	test_and_set_bit(MLXBF_TM_TX_LWM_IRQ, &fifo->pend_events);
> +
> +	schedule_work(&fifo->work);
> +
> +	mod_timer(&fifo->timer, jiffies + mlxbf_tmfifo_timer_interval); }
> +
> +/* Buffer the console output. */
> +static void mlxbf_tmfifo_console_output(struct mlxbf_tmfifo_vdev *cons,
> +					struct virtqueue *vq)
> +{
> +	struct vring *vr = (struct vring *)virtqueue_get_vring(vq);
> +	struct vring_desc *head_desc, *desc = NULL;
> +	struct virtio_device *vdev = &cons->vdev;
> +	u32 len, pkt_len, idx;
> +	void *addr;
> +
> +	for (;;) {

It's better to modify it as while(on some condition)

> +		head_desc = mlxbf_tmfifo_virtio_get_next_desc(vq);
> +		if (head_desc == NULL)
> +			break;
> +
> +		/* Release the packet if no more space. */
> +		pkt_len = mlxbf_tmfifo_virtio_get_pkt_len(vdev, head_desc,
> vr);
> +		if (pkt_len > mlxbf_tmfifo_vdev_tx_buf_avail(cons)) {
> +			mlxbf_tmfifo_virtio_release_desc(vdev, vr, head_desc,
> +							 pkt_len);

Why do you break line here?

> +			break;
> +		}
> +
> +		desc = head_desc;
> +
> +		while (desc != NULL) {
> +			addr = phys_to_virt(virtio64_to_cpu(vdev, desc->addr));
> +			len = virtio32_to_cpu(vdev, desc->len);
> +
> +			if (len <= MLXBF_TMFIFO_CONS_TX_BUF_SIZE -
> +			    cons->tx_tail) {

Why do you break line here? Also below I see few strange breaks.

> +				memcpy(cons->tx_buf + cons->tx_tail, addr,
> len);
> +			} else {
> +				u32 seg;
> +
> +				seg = MLXBF_TMFIFO_CONS_TX_BUF_SIZE -
> +					cons->tx_tail;
> +				memcpy(cons->tx_buf + cons->tx_tail, addr,
> seg);
> +				addr += seg;
> +				memcpy(cons->tx_buf, addr, len - seg);
> +			}
> +			mlxbf_tmfifo_vdev_tx_buf_push(cons, len);
> +
> +			if (!(virtio16_to_cpu(vdev, desc->flags) &
> +			    VRING_DESC_F_NEXT))
> +				break;
> +			idx = virtio16_to_cpu(vdev, desc->next);
> +			desc = &vr->desc[idx];
> +		}
> +
> +		mlxbf_tmfifo_virtio_release_desc(vdev, vr, head_desc,
> pkt_len);
> +	}
> +}
> +
> +/* Rx & Tx processing of a virtual queue. */ static void
> +mlxbf_tmfifo_virtio_rxtx(struct virtqueue *vq, bool is_rx) {
> +	int num_avail = 0, hdr_len, tx_reserve;
> +	struct mlxbf_tmfifo_vring *vring;
> +	struct mlxbf_tmfifo_vdev *cons;
> +	struct virtio_device *vdev;
> +	struct mlxbf_tmfifo *fifo;
> +	struct vring_desc *desc;
> +	unsigned long flags;
> +	struct vring *vr;
> +	u64 sts, data;
> +	u32 len, idx;
> +	void *addr;
> +
> +	if (!vq)
> +		return;
> +
> +	vring = (struct mlxbf_tmfifo_vring *)vq->priv;
> +	fifo = vring->fifo;
> +	vr = (struct vring *)virtqueue_get_vring(vq);
> +
> +	if (!fifo->vdev[vring->vdev_id])
> +		return;
> +	vdev = &fifo->vdev[vring->vdev_id]->vdev;
> +	cons = fifo->vdev[VIRTIO_ID_CONSOLE];
> +
> +	/* Don't continue if another vring is running. */
> +	if (fifo->vring[is_rx] != NULL && fifo->vring[is_rx] != vring)
> +		return;
> +
> +	/* tx_reserve is used to reserved some room in FIFO for console. */
> +	if (vring->vdev_id == VIRTIO_ID_NET) {
> +		hdr_len = sizeof(struct virtio_net_hdr);
> +		tx_reserve = fifo->tx_fifo_size / 16;

Use some define instead of 16/

> +	} else {
> +		BUG_ON(vring->vdev_id != VIRTIO_ID_CONSOLE);
> +		hdr_len = 0;
> +		tx_reserve = 1;
> +	}
> +
> +	desc = vring->desc;
> +
> +	while (1) {

I see there are few drivers in platform which use while (1)
But it looks better to use while(some condition)
and instead of break change this condition to false.

> +		/* Get available FIFO space. */
> +		if (num_avail == 0) {
> +			if (is_rx) {
> +				/* Get the number of available words in FIFO.
> */
> +				sts = readq(fifo->rx_base +
> +					    MLXBF_TMFIFO_RX_STS);
> +				num_avail = FIELD_GET(
> +
> 	MLXBF_TMFIFO_RX_STS__COUNT_MASK, sts);

				num_avail = FIELD_GET(TMFIFO_RX_STS__COUNT_MASK, sts);

> +
> +				/* Don't continue if nothing in FIFO. */
> +				if (num_avail <= 0)
> +					break;
> +			} else {
> +				/* Get available space in FIFO. */
> +				sts = readq(fifo->tx_base +
> +					    MLXBF_TMFIFO_TX_STS);
> +				num_avail = fifo->tx_fifo_size - tx_reserve -
> +					FIELD_GET(
> +
> 	MLXBF_TMFIFO_TX_STS__COUNT_MASK,
> +						sts);

Same as above.

> +
> +				if (num_avail <= 0)
> +					break;
> +			}
> +		}
> +
> +		/* Console output always comes from the Tx buffer. */
> +		if (!is_rx && vring->vdev_id == VIRTIO_ID_CONSOLE &&
> +		    cons != NULL && cons->tx_buf != NULL) {
> +			union mlxbf_tmfifo_msg_hdr hdr;
> +			int size;
> +
> +			size = mlxbf_tmfifo_vdev_tx_buf_len(cons);
> +			if (num_avail < 2 || size == 0)
> +				return;
> +			if (size + sizeof(hdr) > num_avail * sizeof(u64))
> +				size = num_avail * sizeof(u64) - sizeof(hdr);
> +			/* Write header. */
> +			hdr.data = 0;
> +			hdr.type = VIRTIO_ID_CONSOLE;
> +			hdr.len = htons(size);
> +			writeq(cpu_to_le64(hdr.data),
> +			       fifo->tx_base + MLXBF_TMFIFO_TX_DATA);
> +
> +			spin_lock_irqsave(&fifo->spin_lock, flags);
> +			while (size > 0) {
> +				addr = cons->tx_buf + cons->tx_head;
> +
> +				if (cons->tx_head + sizeof(u64) <=
> +				    MLXBF_TMFIFO_CONS_TX_BUF_SIZE) {
> +					memcpy(&data, addr, sizeof(u64));
> +				} else {
> +					int partial;
> +
> +					partial =
> +
> 	MLXBF_TMFIFO_CONS_TX_BUF_SIZE -
> +						cons->tx_head;
> +
> +					memcpy(&data, addr, partial);
> +					memcpy((u8 *)&data + partial,
> +					       cons->tx_buf,
> +					       sizeof(u64) - partial);
> +				}
> +				writeq(data,
> +				       fifo->tx_base + MLXBF_TMFIFO_TX_DATA);
> +
> +				if (size >= sizeof(u64)) {
> +					mlxbf_tmfifo_vdev_tx_buf_pop(
> +						cons, sizeof(u64));
> +					size -= sizeof(u64);
> +				} else {
> +					mlxbf_tmfifo_vdev_tx_buf_pop(
> +						cons, size);
> +					size = 0;
> +				}
> +			}
> +			spin_unlock_irqrestore(&fifo->spin_lock, flags);
> +			return;
> +		}
> +
> +		/* Get the desc of next packet. */
> +		if (!desc) {
> +			/* Save the head desc of the chain. */
> +			vring->desc_head =
> +				mlxbf_tmfifo_virtio_get_next_desc(vq);
> +			if (!vring->desc_head) {
> +				vring->desc = NULL;
> +				return;
> +			}
> +			desc = vring->desc_head;
> +			vring->desc = desc;
> +
> +			if (is_rx && vring->vdev_id == VIRTIO_ID_NET) {
> +				struct virtio_net_hdr *net_hdr;
> +
> +				/* Initialize the packet header. */
> +				net_hdr = (struct virtio_net_hdr *)
> +					phys_to_virt(virtio64_to_cpu(
> +						vdev, desc->addr));
> +				memset(net_hdr, 0, sizeof(*net_hdr));
> +			}
> +		}
> +
> +		/* Beginning of each packet. */
> +		if (vring->pkt_len == 0) {
> +			int vdev_id, vring_change = 0;
> +			union mlxbf_tmfifo_msg_hdr hdr;
> +
> +			num_avail--;
> +
> +			/* Read/Write packet length. */
> +			if (is_rx) {
> +				hdr.data = readq(fifo->rx_base +
> +						 MLXBF_TMFIFO_RX_DATA);
> +				hdr.data = le64_to_cpu(hdr.data);
> +
> +				/* Skip the length 0 packet (keepalive). */
> +				if (hdr.len == 0)
> +					continue;
> +
> +				/* Check packet type. */
> +				if (hdr.type == VIRTIO_ID_NET) {
> +					struct virtio_net_config *config;
> +
> +					vdev_id = VIRTIO_ID_NET;
> +					hdr_len = sizeof(struct virtio_net_hdr);
> +					config =
> +					    &fifo->vdev[vdev_id]->config.net;
> +					if (ntohs(hdr.len) > config->mtu +
> +
> 	MLXBF_TMFIFO_NET_L2_OVERHEAD)
> +						continue;
> +				} else if (hdr.type == VIRTIO_ID_CONSOLE) {
> +					vdev_id = VIRTIO_ID_CONSOLE;
> +					hdr_len = 0;
> +				} else {
> +					continue;
> +				}
> +
> +				/*
> +				 * Check whether the new packet still belongs
> +				 * to this vring or not. If not, update the
> +				 * pkt_len of the new vring and return.
> +				 */
> +				if (vdev_id != vring->vdev_id) {
> +					struct mlxbf_tmfifo_vdev *dev2 =
> +						fifo->vdev[vdev_id];
> +
> +					if (!dev2)
> +						break;
> +					vring->desc = desc;
> +					vring =
> +					  &dev2-
> >vrings[MLXBF_TMFIFO_VRING_RX];
> +					vring_change = 1;
> +				}
> +				vring->pkt_len = ntohs(hdr.len) + hdr_len;
> +			} else {
> +				vring->pkt_len =
> +					mlxbf_tmfifo_virtio_get_pkt_len(
> +						vdev, desc, vr);
> +
> +				hdr.data = 0;
> +				hdr.type = (vring->vdev_id == VIRTIO_ID_NET) ?
> +					VIRTIO_ID_NET :
> +					VIRTIO_ID_CONSOLE;
> +				hdr.len = htons(vring->pkt_len - hdr_len);
> +				writeq(cpu_to_le64(hdr.data),
> +				       fifo->tx_base + MLXBF_TMFIFO_TX_DATA);
> +			}
> +
> +			vring->cur_len = hdr_len;
> +			vring->rem_len = vring->pkt_len;
> +			fifo->vring[is_rx] = vring;
> +
> +			if (vring_change)
> +				return;
> +			continue;
> +		}
> +
> +		/* Check available space in this desc. */
> +		len = virtio32_to_cpu(vdev, desc->len);
> +		if (len > vring->rem_len)
> +			len = vring->rem_len;
> +
> +		/* Check if the current desc is already done. */
> +		if (vring->cur_len == len)
> +			goto check_done;
> +
> +		addr = phys_to_virt(virtio64_to_cpu(vdev, desc->addr));
> +
> +		/* Read a word from FIFO for Rx. */
> +		if (is_rx) {
> +			data = readq(fifo->rx_base +
> MLXBF_TMFIFO_RX_DATA);
> +			data = le64_to_cpu(data);
> +		}
> +
> +		if (vring->cur_len + sizeof(u64) <= len) {
> +			/* The whole word. */
> +			if (is_rx) {
> +				memcpy(addr + vring->cur_len, &data,
> +				       sizeof(u64));
> +			} else {
> +				memcpy(&data, addr + vring->cur_len,
> +				       sizeof(u64));
> +			}

Why not just.
Also few places like this one below.

			if (is_rx)
				memcpy(addr + vring->cur_len, &data, sizeof(u64));
			else
				memcpy(&data, addr + vring->cur_len, sizeof(u64));

> +			vring->cur_len += sizeof(u64);
> +		} else {
> +			/* Leftover bytes. */
> +			BUG_ON(vring->cur_len > len);
> +			if (is_rx) {
> +				memcpy(addr + vring->cur_len, &data,
> +				       len - vring->cur_len);
> +			} else {
> +				memcpy(&data, addr + vring->cur_len,
> +				       len - vring->cur_len);
> +			}
> +			vring->cur_len = len;
> +		}
> +
> +		/* Write the word into FIFO for Tx. */
> +		if (!is_rx) {
> +			writeq(cpu_to_le64(data),
> +			       fifo->tx_base + MLXBF_TMFIFO_TX_DATA);
> +		}
> +
> +		num_avail--;
> +
> +check_done:
> +		/* Check whether this desc is full or completed. */
> +		if (vring->cur_len == len) {
> +			vring->cur_len = 0;
> +			vring->rem_len -= len;
> +
> +			/* Get the next desc on the chain. */
> +			if (vring->rem_len > 0 &&
> +			    (virtio16_to_cpu(vdev, desc->flags) &
> +						VRING_DESC_F_NEXT)) {
> +				idx = virtio16_to_cpu(vdev, desc->next);
> +				desc = &vr->desc[idx];
> +				continue;
> +			}
> +
> +			/* Done and release the desc. */
> +			mlxbf_tmfifo_release_pkt(vdev, vring, &desc);
> +			fifo->vring[is_rx] = NULL;
> +
> +			/* Notify upper layer that packet is done. */
> +			spin_lock_irqsave(&fifo->spin_lock, flags);
> +			vring_interrupt(0, vq);
> +			spin_unlock_irqrestore(&fifo->spin_lock, flags);
> +			continue;
> +		}
> +	}
> +
> +	/* Save the current desc. */
> +	vring->desc = desc;
> +}

I suggest to split mlxbf_tmfifo_virtio_rxtx() into few small routines.


> +
> +/* The notify function is called when new buffers are posted. */ static
> +bool mlxbf_tmfifo_virtio_notify(struct virtqueue *vq) {
> +	struct mlxbf_tmfifo_vring *vring;
> +	struct mlxbf_tmfifo *fifo;
> +	unsigned long flags;
> +
> +	vring = (struct mlxbf_tmfifo_vring *)vq->priv;
> +	fifo = vring->fifo;
> +
> +	/*
> +	 * Virtio maintains vrings in pairs, even number ring for Rx
> +	 * and odd number ring for Tx.
> +	 */
> +	if (!(vring->id & 1)) {
> +		/* Set the RX HWM bit to start Rx. */
> +		if (!test_and_set_bit(MLXBF_TM_RX_HWM_IRQ, &fifo-
> >pend_events))
> +			schedule_work(&fifo->work);
> +	} else {
> +		/*
> +		 * Console could make blocking call with interrupts disabled.
> +		 * In such case, the vring needs to be served right away. For
> +		 * other cases, just set the TX LWM bit to start Tx in the
> +		 * worker handler.
> +		 */
> +		if (vring->vdev_id == VIRTIO_ID_CONSOLE) {
> +			spin_lock_irqsave(&fifo->spin_lock, flags);
> +			mlxbf_tmfifo_console_output(
> +				fifo->vdev[VIRTIO_ID_CONSOLE], vq);

			mlxbf_tmfifo_console_output(fifo->vdev[VIRTIO_ID_CONSOLE], vq);

> +			spin_unlock_irqrestore(&fifo->spin_lock, flags);
> +			schedule_work(&fifo->work);
> +		} else if (!test_and_set_bit(MLXBF_TM_TX_LWM_IRQ,
> +					     &fifo->pend_events))
> +			schedule_work(&fifo->work);

		If {
		} else if {
		}

For consistency.

> +	}
> +
> +	return true;
> +}
> +
> +/* Work handler for Rx and Tx case. */
> +static void mlxbf_tmfifo_work_handler(struct work_struct *work) {
> +	struct mlxbf_tmfifo_vdev *tm_vdev;
> +	struct mlxbf_tmfifo *fifo;
> +	int i;
> +
> +	fifo = container_of(work, struct mlxbf_tmfifo, work);
> +	if (!fifo->is_ready)
> +		return;
> +
> +	mutex_lock(&fifo->lock);
> +
> +	/* Tx (Send data to the TmFifo). */
> +	if (test_and_clear_bit(MLXBF_TM_TX_LWM_IRQ, &fifo->pend_events)
> &&
> +		       fifo->irq_info[MLXBF_TM_TX_LWM_IRQ].irq) {
> +		for (i = 0; i < MLXBF_TMFIFO_VDEV_MAX; i++) {

I suggest to define local variable vq.
And have below:
				mlxbf_tmfifo_virtio_rxtx(vq, false);

> +			tm_vdev = fifo->vdev[i];
> +			if (tm_vdev != NULL) {
> +				mlxbf_tmfifo_virtio_rxtx(
> +				    tm_vdev-
> >vrings[MLXBF_TMFIFO_VRING_TX].vq,
> +				    false);
> +			}
> +		}
> +	}
> +
> +	/* Rx (Receive data from the TmFifo). */
> +	if (test_and_clear_bit(MLXBF_TM_RX_HWM_IRQ, &fifo->pend_events)
> &&
> +		       fifo->irq_info[MLXBF_TM_RX_HWM_IRQ].irq) {
> +		for (i = 0; i < MLXBF_TMFIFO_VDEV_MAX; i++) {
> +			tm_vdev = fifo->vdev[i];

Same as above.

> +			if (tm_vdev != NULL) {
> +				mlxbf_tmfifo_virtio_rxtx(
> +				    tm_vdev-
> >vrings[MLXBF_TMFIFO_VRING_RX].vq,
> +				    true);
> +			}
> +		}
> +	}
> +
> +	mutex_unlock(&fifo->lock);
> +}
> +
> +/* Get the array of feature bits for this device. */ static u64
> +mlxbf_tmfifo_virtio_get_features(struct virtio_device *vdev) {
> +	struct mlxbf_tmfifo_vdev *tm_vdev;
> +
> +	tm_vdev = container_of(vdev, struct mlxbf_tmfifo_vdev, vdev);
> +	return tm_vdev->features;
> +}
> +
> +/* Confirm device features to use. */
> +static int mlxbf_tmfifo_virtio_finalize_features(struct virtio_device
> +*vdev) {
> +	struct mlxbf_tmfifo_vdev *tm_vdev;
> +
> +	tm_vdev = container_of(vdev, struct mlxbf_tmfifo_vdev, vdev);
> +	tm_vdev->features = vdev->features;
> +
> +	return 0;
> +}
> +
> +/* Free virtqueues found by find_vqs(). */ static void
> +mlxbf_tmfifo_virtio_del_vqs(struct virtio_device *vdev) {
> +	struct mlxbf_tmfifo_vdev *tm_vdev;
> +	struct mlxbf_tmfifo_vring *vring;
> +	struct virtqueue *vq;
> +	int i;
> +
> +	tm_vdev = container_of(vdev, struct mlxbf_tmfifo_vdev, vdev);
> +
> +	for (i = 0; i < ARRAY_SIZE(tm_vdev->vrings); i++) {
> +		vring = &tm_vdev->vrings[i];
> +
> +		/* Release the pending packet. */
> +		if (vring->desc != NULL) {
> +			mlxbf_tmfifo_release_pkt(&tm_vdev->vdev, vring,
> +						 &vring->desc);
> +		}
> +
> +		vq = vring->vq;
> +		if (vq) {
> +			vring->vq = NULL;
> +			vring_del_virtqueue(vq);
> +		}
> +	}
> +}
> +
> +/* Create and initialize the virtual queues. */ static int
> +mlxbf_tmfifo_virtio_find_vqs(struct virtio_device *vdev,
> +					unsigned int nvqs,
> +					struct virtqueue *vqs[],
> +					vq_callback_t *callbacks[],
> +					const char * const names[],
> +					const bool *ctx,
> +					struct irq_affinity *desc)
> +{
> +	struct mlxbf_tmfifo_vdev *tm_vdev;
> +	struct mlxbf_tmfifo_vring *vring;
> +	int i, ret = -EINVAL, size;

Don't initialize ret with -EINVAL.

> +	struct virtqueue *vq;
> +
> +	tm_vdev = container_of(vdev, struct mlxbf_tmfifo_vdev, vdev);
> +	if (nvqs > ARRAY_SIZE(tm_vdev->vrings))
> +		return -EINVAL;
> +
> +	for (i = 0; i < nvqs; ++i) {
> +		if (!names[i])
> +			goto error;
> +		vring = &tm_vdev->vrings[i];
> +
> +		/* zero vring */
> +		size = vring_size(vring->size, vring->align);
> +		memset(vring->va, 0, size);
> +		vq = vring_new_virtqueue(i, vring->size, vring->align, vdev,
> +					 false, false, vring->va,
> +					 mlxbf_tmfifo_virtio_notify,
> +					 callbacks[i], names[i]);
> +		if (!vq) {
> +			dev_err(&vdev->dev, "vring_new_virtqueue failed\n");
> +			ret = -ENOMEM;
> +			goto error;
> +		}
> +
> +		vqs[i] = vq;
> +		vring->vq = vq;
> +		vq->priv = vring;
> +	}
> +
> +	return 0;
> +
> +error:
> +	mlxbf_tmfifo_virtio_del_vqs(vdev);
> +	return ret;
> +}
> +
> +/* Read the status byte. */
> +static u8 mlxbf_tmfifo_virtio_get_status(struct virtio_device *vdev) {
> +	struct mlxbf_tmfifo_vdev *tm_vdev;
> +
> +	tm_vdev = container_of(vdev, struct mlxbf_tmfifo_vdev, vdev);
> +
> +	return tm_vdev->status;
> +}
> +
> +/* Write the status byte. */
> +static void mlxbf_tmfifo_virtio_set_status(struct virtio_device *vdev,
> +					   u8 status)
> +{
> +	struct mlxbf_tmfifo_vdev *tm_vdev;
> +
> +	tm_vdev = container_of(vdev, struct mlxbf_tmfifo_vdev, vdev);
> +	tm_vdev->status = status;
> +}
> +
> +/* Reset the device. Not much here for now. */ static void
> +mlxbf_tmfifo_virtio_reset(struct virtio_device *vdev) {
> +	struct mlxbf_tmfifo_vdev *tm_vdev;
> +
> +	tm_vdev = container_of(vdev, struct mlxbf_tmfifo_vdev, vdev);
> +	tm_vdev->status = 0;
> +}
> +
> +/* Read the value of a configuration field. */ static void
> +mlxbf_tmfifo_virtio_get(struct virtio_device *vdev,
> +			      unsigned int offset,
> +			      void *buf,
> +			      unsigned int len)
> +{
> +	struct mlxbf_tmfifo_vdev *tm_vdev;
> +
> +	tm_vdev = container_of(vdev, struct mlxbf_tmfifo_vdev, vdev);
> +

	unsigned int pos = offset + len;

	if (pos > sizeof(tm_vdev->config) || pos < len)


> +	if (offset + len > sizeof(tm_vdev->config) || offset + len < len) {
> +		dev_err(vdev->dev.parent, "virtio_get access out of bounds\n");
> +		return;
> +	}
> +
> +	memcpy(buf, (u8 *)&tm_vdev->config + offset, len); }
> +
> +/* Write the value of a configuration field. */ static void
> +mlxbf_tmfifo_virtio_set(struct virtio_device *vdev,
> +				 unsigned int offset,
> +				 const void *buf,
> +				 unsigned int len)
> +{
> +	struct mlxbf_tmfifo_vdev *tm_vdev;
> +
> +	tm_vdev = container_of(vdev, struct mlxbf_tmfifo_vdev, vdev);
> +
> +	if (offset + len > sizeof(tm_vdev->config) || offset + len < len) {

Same as above.

> +		dev_err(vdev->dev.parent, "virtio_get access out of bounds\n");
> +		return;
> +	}
> +
> +	memcpy((u8 *)&tm_vdev->config + offset, buf, len); }
> +
> +/* Virtio config operations. */
> +static const struct virtio_config_ops mlxbf_tmfifo_virtio_config_ops = {
> +	.get_features = mlxbf_tmfifo_virtio_get_features,
> +	.finalize_features = mlxbf_tmfifo_virtio_finalize_features,
> +	.find_vqs = mlxbf_tmfifo_virtio_find_vqs,
> +	.del_vqs = mlxbf_tmfifo_virtio_del_vqs,
> +	.reset = mlxbf_tmfifo_virtio_reset,
> +	.set_status = mlxbf_tmfifo_virtio_set_status,
> +	.get_status = mlxbf_tmfifo_virtio_get_status,
> +	.get = mlxbf_tmfifo_virtio_get,
> +	.set = mlxbf_tmfifo_virtio_set,
> +};
> +
> +/* Create vdev type in a tmfifo. */
> +int mlxbf_tmfifo_create_vdev(struct mlxbf_tmfifo *fifo, int vdev_id,
> +			     u64 features, void *config, u32 size) {
> +	struct mlxbf_tmfifo_vdev *tm_vdev;
> +	int ret = 0;
> +
> +	mutex_lock(&fifo->lock);
> +
> +	tm_vdev = fifo->vdev[vdev_id];
> +	if (tm_vdev != NULL) {
> +		pr_err("vdev %d already exists\n", vdev_id);
> +		ret = -EEXIST;
> +		goto already_exist;
> +	}
> +
> +	tm_vdev = kzalloc(sizeof(*tm_vdev), GFP_KERNEL);
> +	if (!tm_vdev) {
> +		ret = -ENOMEM;
> +		goto already_exist;
> +	}
> +
> +	tm_vdev->vdev.id.device = vdev_id;
> +	tm_vdev->vdev.config = &mlxbf_tmfifo_virtio_config_ops;
> +	tm_vdev->vdev.dev.parent = &fifo->pdev->dev;
> +	tm_vdev->vdev.dev.release = mlxbf_tmfifo_virtio_dev_release;
> +	tm_vdev->features = features;
> +	if (config)
> +		memcpy(&tm_vdev->config, config, size);
> +	if (mlxbf_tmfifo_alloc_vrings(fifo, tm_vdev, vdev_id)) {
> +		pr_err("Unable to allocate vring\n");
> +		ret = -ENOMEM;
> +		goto alloc_vring_fail;
> +	}
> +	if (vdev_id == VIRTIO_ID_CONSOLE) {
> +		tm_vdev->tx_buf =
> kmalloc(MLXBF_TMFIFO_CONS_TX_BUF_SIZE,
> +					  GFP_KERNEL);
> +	}
> +	fifo->vdev[vdev_id] = tm_vdev;
> +
> +	/* Register the virtio device. */
> +	ret = register_virtio_device(&tm_vdev->vdev);
> +	if (ret) {
> +		dev_err(&fifo->pdev->dev, "register_virtio_device() failed\n");
> +		goto register_fail;
> +	}
> +
> +	mutex_unlock(&fifo->lock);
> +	return 0;
> +
> +register_fail:
> +	mlxbf_tmfifo_free_vrings(fifo, vdev_id);
> +	fifo->vdev[vdev_id] = NULL;
> +alloc_vring_fail:
> +	kfree(tm_vdev);
> +already_exist:
> +	mutex_unlock(&fifo->lock);
> +	return ret;
> +}
> +
> +/* Delete vdev type from a tmfifo. */
> +int mlxbf_tmfifo_delete_vdev(struct mlxbf_tmfifo *fifo, int vdev_id) {
> +	struct mlxbf_tmfifo_vdev *tm_vdev;
> +
> +	mutex_lock(&fifo->lock);
> +
> +	/* Unregister vdev. */
> +	tm_vdev = fifo->vdev[vdev_id];
> +	if (tm_vdev) {
> +		unregister_virtio_device(&tm_vdev->vdev);
> +		mlxbf_tmfifo_free_vrings(fifo, vdev_id);
> +		kfree(tm_vdev->tx_buf);
> +		kfree(tm_vdev);
> +		fifo->vdev[vdev_id] = NULL;
> +	}
> +
> +	mutex_unlock(&fifo->lock);
> +
> +	return 0;
> +}
> +
> +/* Device remove function. */
> +static int mlxbf_tmfifo_remove(struct platform_device *pdev) {

Locate it after probe.
If you'll use all devm_, like Andy noted:
devm_ioremap
devm_ioremap_resource
devm_kzalloc
devm_request_mem_region
you can drop all kfree, release_mem_region, iounmap

And make the below as a separate routine, something like
mlxbf_tmfifo_cleanup(), if you still need it.

> +	struct mlxbf_tmfifo *fifo = platform_get_drvdata(pdev);
> +	struct resource *rx_res, *tx_res;
> +	int i;
> +
> +	if (fifo) {
> +		mutex_lock(&mlxbf_tmfifo_lock);
> +
> +		fifo->is_ready = false;
> +
> +		/* Stop the timer. */
> +		del_timer_sync(&fifo->timer);
> +
> +		/* Release interrupts. */
> +		mlxbf_tmfifo_free_irqs(fifo);
> +
> +		/* Cancel the pending work. */
> +		cancel_work_sync(&fifo->work);
> +
> +		for (i = 0; i < MLXBF_TMFIFO_VDEV_MAX; i++)
> +			mlxbf_tmfifo_delete_vdev(fifo, i);
> +
> +		/* Release IO resources. */
> +		if (fifo->rx_base)
> +			iounmap(fifo->rx_base);
> +		if (fifo->tx_base)
> +			iounmap(fifo->tx_base);
> +
> +		platform_set_drvdata(pdev, NULL);
> +		kfree(fifo);
> +
> +		mutex_unlock(&mlxbf_tmfifo_lock);
> +	}
> +
> +	rx_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	if (rx_res)
> +		release_mem_region(rx_res->start, resource_size(rx_res));
> +	tx_res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
> +	if (tx_res)
> +		release_mem_region(tx_res->start, resource_size(tx_res));
> +
> +	return 0;
> +}
> +
> +/* Read the configured network MAC address from efi variable. */ static
> +void mlxbf_tmfifo_get_cfg_mac(u8 *mac) {
> +	efi_char16_t name[] = {
> +		'R', 's', 'h', 'i', 'm', 'M', 'a', 'c', 'A', 'd', 'd', 'r', 0 };


Could it be moved out and set like:
static const efi_char16_t mlxbf_tmfifo_efi_name[] = "...";
Could you check if the are some examples in kernel, please?

> +	efi_guid_t guid = EFI_GLOBAL_VARIABLE_GUID;
> +	efi_status_t status;
> +	unsigned long size;
> +	u8 buf[6];
> +
> +	size = sizeof(buf);
> +	status = efi.get_variable(name, &guid, NULL, &size, buf);
> +	if (status == EFI_SUCCESS && size == sizeof(buf))
> +		memcpy(mac, buf, sizeof(buf));
> +}
> +
> +/* Probe the TMFIFO. */
> +static int mlxbf_tmfifo_probe(struct platform_device *pdev) {
> +	struct virtio_net_config net_config;
> +	struct resource *rx_res, *tx_res;
> +	struct mlxbf_tmfifo *fifo;
> +	int i, ret;
> +	u64 ctl;
> +
> +	/* Get the resource of the Rx & Tx FIFO. */
> +	rx_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	tx_res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
> +	if (!rx_res || !tx_res) {
> +		ret = -EINVAL;
> +		goto err;
> +	}
> +
> +	if (request_mem_region(rx_res->start,
> +			       resource_size(rx_res), "bf-tmfifo") == NULL) {
> +		ret = -EBUSY;
> +		goto early_err;
> +	}
> +
> +	if (request_mem_region(tx_res->start,
> +			       resource_size(tx_res), "bf-tmfifo") == NULL) {
> +		release_mem_region(rx_res->start, resource_size(rx_res));
> +		ret = -EBUSY;
> +		goto early_err;
> +	}
> +
> +	ret = -ENOMEM;
> +	fifo = kzalloc(sizeof(struct mlxbf_tmfifo), GFP_KERNEL);
> +	if (!fifo)
> +		goto err;
> +
> +	fifo->pdev = pdev;
> +	platform_set_drvdata(pdev, fifo);
> +
> +	spin_lock_init(&fifo->spin_lock);
> +	INIT_WORK(&fifo->work, mlxbf_tmfifo_work_handler);
> +
> +	timer_setup(&fifo->timer, mlxbf_tmfifo_timer, 0);
> +	fifo->timer.function = mlxbf_tmfifo_timer;
> +
> +	for (i = 0; i < MLXBF_TM_IRQ_CNT; i++) {
> +		fifo->irq_info[i].index = i;
> +		fifo->irq_info[i].fifo = fifo;
> +		fifo->irq_info[i].irq = platform_get_irq(pdev, i);
> +		ret = request_irq(fifo->irq_info[i].irq,
> +				  mlxbf_tmfifo_irq_handler, 0,
> +				  "tmfifo", &fifo->irq_info[i]);
> +		if (ret) {
> +			pr_err("Unable to request irq\n");
> +			fifo->irq_info[i].irq = 0;
> +			goto err;
> +		}
> +	}
> +
> +	fifo->rx_base = ioremap(rx_res->start, resource_size(rx_res));
> +	if (!fifo->rx_base)
> +		goto err;
> +
> +	fifo->tx_base = ioremap(tx_res->start, resource_size(tx_res));
> +	if (!fifo->tx_base)
> +		goto err;
> +
> +	/* Get Tx FIFO size and set the low/high watermark. */
> +	ctl = readq(fifo->tx_base + MLXBF_TMFIFO_TX_CTL);
> +	fifo->tx_fifo_size =
> +		FIELD_GET(MLXBF_TMFIFO_TX_CTL__MAX_ENTRIES_MASK,
> ctl);
> +	ctl = (ctl & ~MLXBF_TMFIFO_TX_CTL__LWM_MASK) |
> +		FIELD_PREP(MLXBF_TMFIFO_TX_CTL__LWM_MASK,
> +			   fifo->tx_fifo_size / 2);
> +	ctl = (ctl & ~MLXBF_TMFIFO_TX_CTL__HWM_MASK) |
> +		FIELD_PREP(MLXBF_TMFIFO_TX_CTL__HWM_MASK,
> +			   fifo->tx_fifo_size - 1);
> +	writeq(ctl, fifo->tx_base + MLXBF_TMFIFO_TX_CTL);
> +
> +	/* Get Rx FIFO size and set the low/high watermark. */
> +	ctl = readq(fifo->rx_base + MLXBF_TMFIFO_RX_CTL);
> +	fifo->rx_fifo_size =
> +		FIELD_GET(MLXBF_TMFIFO_RX_CTL__MAX_ENTRIES_MASK,
> ctl);
> +	ctl = (ctl & ~MLXBF_TMFIFO_RX_CTL__LWM_MASK) |
> +		FIELD_PREP(MLXBF_TMFIFO_RX_CTL__LWM_MASK, 0);
> +	ctl = (ctl & ~MLXBF_TMFIFO_RX_CTL__HWM_MASK) |
> +		FIELD_PREP(MLXBF_TMFIFO_RX_CTL__HWM_MASK, 1);
> +	writeq(ctl, fifo->rx_base + MLXBF_TMFIFO_RX_CTL);
> +
> +	mutex_init(&fifo->lock);
> +
> +	/* Create the console vdev. */
> +	ret = mlxbf_tmfifo_create_vdev(fifo, VIRTIO_ID_CONSOLE, 0, NULL, 0);
> +	if (ret)
> +		goto err;
> +
> +	/* Create the network vdev. */
> +	memset(&net_config, 0, sizeof(net_config));
> +	net_config.mtu = MLXBF_TMFIFO_NET_MTU;
> +	net_config.status = VIRTIO_NET_S_LINK_UP;
> +	memcpy(net_config.mac, mlxbf_tmfifo_net_default_mac, 6);
> +	mlxbf_tmfifo_get_cfg_mac(net_config.mac);
> +	ret = mlxbf_tmfifo_create_vdev(fifo, VIRTIO_ID_NET,
> +		MLXBF_TMFIFO_NET_FEATURES, &net_config,
> sizeof(net_config));
> +	if (ret)
> +		goto err;
> +
> +	mod_timer(&fifo->timer, jiffies + mlxbf_tmfifo_timer_interval);
> +
> +	fifo->is_ready = true;
> +
> +	return 0;
> +
> +err:
> +	mlxbf_tmfifo_remove(pdev);
> +early_err:
> +	dev_err(&pdev->dev, "Probe Failed\n");
> +	return ret;
> +}
> +
> +static const struct of_device_id mlxbf_tmfifo_match[] = {
> +	{ .compatible = "mellanox,bf-tmfifo" },
> +	{},
> +};
> +MODULE_DEVICE_TABLE(of, mlxbf_tmfifo_match);
> +
> +static const struct acpi_device_id mlxbf_tmfifo_acpi_match[] = {
> +	{ "MLNXBF01", 0 },
> +	{},
> +};
> +MODULE_DEVICE_TABLE(acpi, mlxbf_tmfifo_acpi_match);
> +
> +static struct platform_driver mlxbf_tmfifo_driver = {
> +	.probe = mlxbf_tmfifo_probe,
> +	.remove = mlxbf_tmfifo_remove,
> +	.driver = {
> +		.name = "bf-tmfifo",
> +		.of_match_table = mlxbf_tmfifo_match,
> +		.acpi_match_table = ACPI_PTR(mlxbf_tmfifo_acpi_match),
> +	},
> +};
> +
> +module_platform_driver(mlxbf_tmfifo_driver);
> +
> +MODULE_DESCRIPTION("Mellanox BlueField SoC TmFifo Driver");
> +MODULE_LICENSE("GPL"); MODULE_AUTHOR("Mellanox Technologies");
> --
> 1.8.3.1





[Index of Archives]     [Linux Kernel Development]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux