// SPDX-License-Identifier: GPL-2.0-only
/*
* SolidRun DPU driver for control plane
*
* Copyright (C) 2022-2023 SolidRun
*
* Author: Alvaro Karsz <alvaro.karsz@solid-run.com>
*
*/
#include <linux/iopoll.h>
#include "snet_vdpa.h"
/* SNET DPU device ID */
#define SNET_DEVICE_ID 0x1000
/* SNET signature */
#define SNET_SIGNATURE 0xD0D06363
/* Max. config version that we can work with */
#define SNET_CFG_VERSION 0x2
/* Queue align */
#define SNET_QUEUE_ALIGNMENT PAGE_SIZE
/* Kick value to notify that new data is available */
#define SNET_KICK_VAL 0x1
#define SNET_CONFIG_OFF 0x0
/* How long we are willing to wait for a SNET device */
#define SNET_DETECT_TIMEOUT 5000000
/* How long should we wait for the DPU to read our config */
#define SNET_READ_CFG_TIMEOUT 3000000
/* Size of configs written to the DPU */
#define SNET_GENERAL_CFG_LEN 36
#define SNET_GENERAL_CFG_VQ_LEN 40
static struct snet *vdpa_to_snet(struct vdpa_device *vdpa)
{
return container_of(vdpa, struct snet, vdpa);
}
static irqreturn_t snet_cfg_irq_hndlr(int irq, void *data)
{
struct snet *snet = data;
/* Call callback if any */
if (likely(snet->cb.callback))
return snet->cb.callback(snet->cb.private);
return IRQ_HANDLED;
}
static irqreturn_t snet_vq_irq_hndlr(int irq, void *data)
{
struct snet_vq *vq = data;
/* Call callback if any */
if (likely(vq->cb.callback))
return vq->cb.callback(vq->cb.private);
return IRQ_HANDLED;
}
static void snet_free_irqs(struct snet *snet)
{
struct psnet *psnet = snet->psnet;
struct pci_dev *pdev;
u32 i;
/* Which Device allcoated the IRQs? */
if (PSNET_FLAG_ON(psnet, SNET_CFG_FLAG_IRQ_PF))
pdev = snet->pdev->physfn;
else
pdev = snet->pdev;
/* Free config's IRQ */
if (snet->cfg_irq != -1) {
devm_free_irq(&pdev->dev, snet->cfg_irq, snet);
snet->cfg_irq = -1;
}
/* Free VQ IRQs */
for (i = 0; i < snet->cfg->vq_num; i++) {
if (snet->vqs[i] && snet->vqs[i]->irq != -1) {
devm_free_irq(&pdev->dev, snet->vqs[i]->irq, snet->vqs[i]);
snet->vqs[i]->irq = -1;
}
}
/* IRQ vectors are freed when the pci remove callback is called */
}
static int snet_set_vq_address(struct vdpa_device *vdev, u16 idx, u64 desc_area,
u64 driver_area, u64 device_area)
{
struct snet *snet = vdpa_to_snet(vdev);
/* save received parameters in vqueue sturct */
snet->vqs[idx]->desc_area = desc_area;
snet->vqs[idx]->driver_area = driver_area;
snet->vqs[idx]->device_area = device_area;
return 0;
}
static void snet_set_vq_num(struct vdpa_device *vdev, u16 idx, u32 num)
{
struct snet *snet = vdpa_to_snet(vdev);
/* save num in vqueue */
snet->vqs[idx]->num = num;
}
static void snet_kick_vq(struct vdpa_device *vdev, u16 idx)
{
struct snet *snet = vdpa_to_snet(vdev);
/* not ready - ignore */
if (unlikely(!snet->vqs[idx]->ready))
return;
iowrite32(SNET_KICK_VAL, snet->vqs[idx]->kick_ptr);
}
static void snet_kick_vq_with_data(struct vdpa_device *vdev, u32 data)
{
struct snet *snet = vdpa_to_snet(vdev);
u16 idx = data & 0xFFFF;
/* not ready - ignore */
if (unlikely(!snet->vqs[idx]->ready))
return;
iowrite32((data & 0xFFFF0000) | SNET_KICK_VAL, snet->vqs[idx]->kick_ptr);
}
static void snet_set_vq_cb(struct vdpa_device *vdev, u16 idx, struct vdpa_callback *cb)
{
struct snet *snet = vdpa_to_snet(vdev);
snet->vqs[idx]->cb.callback = cb->callback;
snet->vqs[idx]->cb.private = cb->private;
}
static void snet_set_vq_ready(struct vdpa_device *vdev, u16 idx, bool