| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138 |
- // 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 ready)
- {
- struct snet *snet = vdpa_to_snet(vdev);
- snet->vqs[idx]->ready = ready;
- }
- static bool snet_get_vq_ready(struct vdpa_device *vdev, u16 idx)
- {
- struct snet *snet = vdpa_to_snet(vdev);
- return snet->vqs[idx]->ready;
- }
- static bool snet_vq_state_is_initial(struct snet *snet, const struct vdpa_vq_state *state)
- {
- if (SNET_HAS_FEATURE(snet, VIRTIO_F_RING_PACKED)) {
- const struct vdpa_vq_state_packed *p = &state->packed;
- if (p->last_avail_counter == 1 && p->last_used_counter == 1 &&
- p->last_avail_idx == 0 && p->last_used_idx == 0)
- return true;
- } else {
- const struct vdpa_vq_state_split *s = &state->split;
- if (s->avail_index == 0)
- return true;
- }
- return false;
- }
- static int snet_set_vq_state(struct vdpa_device *vdev, u16 idx, const struct vdpa_vq_state *state)
- {
- struct snet *snet = vdpa_to_snet(vdev);
- /* We can set any state for config version 2+ */
- if (SNET_CFG_VER(snet, 2)) {
- memcpy(&snet->vqs[idx]->vq_state, state, sizeof(*state));
- return 0;
- }
- /* Older config - we can't set the VQ state.
- * Return 0 only if this is the initial state we use in the DPU.
- */
- if (snet_vq_state_is_initial(snet, state))
- return 0;
- return -EOPNOTSUPP;
- }
- static int snet_get_vq_state(struct vdpa_device *vdev, u16 idx, struct vdpa_vq_state *state)
- {
- struct snet *snet = vdpa_to_snet(vdev);
- return snet_read_vq_state(snet, idx, state);
- }
- static int snet_get_vq_irq(struct vdpa_device *vdev, u16 idx)
- {
- struct snet *snet = vdpa_to_snet(vdev);
- return snet->vqs[idx]->irq;
- }
- static u32 snet_get_vq_align(struct vdpa_device *vdev)
- {
- return (u32)SNET_QUEUE_ALIGNMENT;
- }
- static int snet_reset_dev(struct snet *snet)
- {
- struct pci_dev *pdev = snet->pdev;
- int ret = 0;
- u32 i;
- /* If status is 0, nothing to do */
- if (!snet->status)
- return 0;
- /* If DPU started, destroy it */
- if (snet->status & VIRTIO_CONFIG_S_DRIVER_OK)
- ret = snet_destroy_dev(snet);
- /* Clear VQs */
- for (i = 0; i < snet->cfg->vq_num; i++) {
- if (!snet->vqs[i])
- continue;
- snet->vqs[i]->cb.callback = NULL;
- snet->vqs[i]->cb.private = NULL;
- snet->vqs[i]->desc_area = 0;
- snet->vqs[i]->device_area = 0;
- snet->vqs[i]->driver_area = 0;
- snet->vqs[i]->ready = false;
- }
- /* Clear config callback */
- snet->cb.callback = NULL;
- snet->cb.private = NULL;
- /* Free IRQs */
- snet_free_irqs(snet);
- /* Reset status */
- snet->status = 0;
- snet->dpu_ready = false;
- if (ret)
- SNET_WARN(pdev, "Incomplete reset to SNET[%u] device, err: %d\n", snet->sid, ret);
- else
- SNET_DBG(pdev, "Reset SNET[%u] device\n", snet->sid);
- return 0;
- }
- static int snet_reset(struct vdpa_device *vdev)
- {
- struct snet *snet = vdpa_to_snet(vdev);
- return snet_reset_dev(snet);
- }
- static size_t snet_get_config_size(struct vdpa_device *vdev)
- {
- struct snet *snet = vdpa_to_snet(vdev);
- return (size_t)snet->cfg->cfg_size;
- }
- static u64 snet_get_features(struct vdpa_device *vdev)
- {
- struct snet *snet = vdpa_to_snet(vdev);
- return snet->cfg->features;
- }
- static int snet_set_drv_features(struct vdpa_device *vdev, u64 features)
- {
- struct snet *snet = vdpa_to_snet(vdev);
- snet->negotiated_features = snet->cfg->features & features;
- return 0;
- }
- static u64 snet_get_drv_features(struct vdpa_device *vdev)
- {
- struct snet *snet = vdpa_to_snet(vdev);
- return snet->negotiated_features;
- }
- static u16 snet_get_vq_num_max(struct vdpa_device *vdev)
- {
- struct snet *snet = vdpa_to_snet(vdev);
- return (u16)snet->cfg->vq_size;
- }
- static void snet_set_config_cb(struct vdpa_device *vdev, struct vdpa_callback *cb)
- {
- struct snet *snet = vdpa_to_snet(vdev);
- snet->cb.callback = cb->callback;
- snet->cb.private = cb->private;
- }
- static u32 snet_get_device_id(struct vdpa_device *vdev)
- {
- struct snet *snet = vdpa_to_snet(vdev);
- return snet->cfg->virtio_id;
- }
- static u32 snet_get_vendor_id(struct vdpa_device *vdev)
- {
- return (u32)PCI_VENDOR_ID_SOLIDRUN;
- }
- static u8 snet_get_status(struct vdpa_device *vdev)
- {
- struct snet *snet = vdpa_to_snet(vdev);
- return snet->status;
- }
- static int snet_write_conf(struct snet *snet)
- {
- u32 off, i, tmp;
- int ret;
- /* No need to write the config twice */
- if (snet->dpu_ready)
- return true;
- /* Snet data :
- *
- * General data: SNET_GENERAL_CFG_LEN bytes long
- * 0 0x4 0x8 0xC 0x10 0x14 0x1C 0x24
- * | MAGIC NUMBER | CFG VER | SNET SID | NUMBER OF QUEUES | IRQ IDX | FEATURES | RSVD |
- *
- * For every VQ: SNET_GENERAL_CFG_VQ_LEN bytes long
- * 0 0x4 0x8
- * | VQ SID AND QUEUE SIZE | IRQ Index |
- * | DESC AREA |
- * | DEVICE AREA |
- * | DRIVER AREA |
- * | VQ STATE (CFG 2+) | RSVD |
- *
- * Magic number should be written last, this is the DPU indication that the data is ready
- */
- /* Init offset */
- off = snet->psnet->cfg.host_cfg_off;
- /* Ignore magic number for now */
- off += 4;
- snet_write32(snet, off, snet->psnet->negotiated_cfg_ver);
- off += 4;
- snet_write32(snet, off, snet->sid);
- off += 4;
- snet_write32(snet, off, snet->cfg->vq_num);
- off += 4;
- snet_write32(snet, off, snet->cfg_irq_idx);
- off += 4;
- snet_write64(snet, off, snet->negotiated_features);
- off += 8;
- /* Ignore reserved */
- off += 8;
- /* Write VQs */
- for (i = 0 ; i < snet->cfg->vq_num ; i++) {
- tmp = (i << 16) | (snet->vqs[i]->num & 0xFFFF);
- snet_write32(snet, off, tmp);
- off += 4;
- snet_write32(snet, off, snet->vqs[i]->irq_idx);
- off += 4;
- snet_write64(snet, off, snet->vqs[i]->desc_area);
- off += 8;
- snet_write64(snet, off, snet->vqs[i]->device_area);
- off += 8;
- snet_write64(snet, off, snet->vqs[i]->driver_area);
- off += 8;
- /* Write VQ state if config version is 2+ */
- if (SNET_CFG_VER(snet, 2))
- snet_write32(snet, off, *(u32 *)&snet->vqs[i]->vq_state);
- off += 4;
- /* Ignore reserved */
- off += 4;
- }
- /* Write magic number - data is ready */
- snet_write32(snet, snet->psnet->cfg.host_cfg_off, SNET_SIGNATURE);
- /* The DPU will ACK the config by clearing the signature */
- ret = readx_poll_timeout(ioread32, snet->bar + snet->psnet->cfg.host_cfg_off,
- tmp, !tmp, 10, SNET_READ_CFG_TIMEOUT);
- if (ret) {
- SNET_ERR(snet->pdev, "Timeout waiting for the DPU to read the config\n");
- return false;
- }
- /* set DPU flag */
- snet->dpu_ready = true;
- return true;
- }
- static int snet_request_irqs(struct pci_dev *pdev, struct snet *snet)
- {
- int ret, i, irq;
- /* Request config IRQ */
- irq = pci_irq_vector(pdev, snet->cfg_irq_idx);
- ret = devm_request_irq(&pdev->dev, irq, snet_cfg_irq_hndlr, 0,
- snet->cfg_irq_name, snet);
- if (ret) {
- SNET_ERR(pdev, "Failed to request IRQ\n");
- return ret;
- }
- snet->cfg_irq = irq;
- /* Request IRQ for every VQ */
- for (i = 0; i < snet->cfg->vq_num; i++) {
- irq = pci_irq_vector(pdev, snet->vqs[i]->irq_idx);
- ret = devm_request_irq(&pdev->dev, irq, snet_vq_irq_hndlr, 0,
- snet->vqs[i]->irq_name, snet->vqs[i]);
- if (ret) {
- SNET_ERR(pdev, "Failed to request IRQ\n");
- return ret;
- }
- snet->vqs[i]->irq = irq;
- }
- return 0;
- }
- static void snet_set_status(struct vdpa_device *vdev, u8 status)
- {
- struct snet *snet = vdpa_to_snet(vdev);
- struct psnet *psnet = snet->psnet;
- struct pci_dev *pdev = snet->pdev;
- int ret;
- bool pf_irqs;
- if (status == snet->status)
- return;
- if ((status & VIRTIO_CONFIG_S_DRIVER_OK) &&
- !(snet->status & VIRTIO_CONFIG_S_DRIVER_OK)) {
- /* Request IRQs */
- pf_irqs = PSNET_FLAG_ON(psnet, SNET_CFG_FLAG_IRQ_PF);
- ret = snet_request_irqs(pf_irqs ? pdev->physfn : pdev, snet);
- if (ret)
- goto set_err;
- /* Write config to the DPU */
- if (snet_write_conf(snet)) {
- SNET_INFO(pdev, "Create SNET[%u] device\n", snet->sid);
- } else {
- snet_free_irqs(snet);
- goto set_err;
- }
- }
- /* Save the new status */
- snet->status = status;
- return;
- set_err:
- snet->status |= VIRTIO_CONFIG_S_FAILED;
- }
- static void snet_get_config(struct vdpa_device *vdev, unsigned int offset,
- void *buf, unsigned int len)
- {
- struct snet *snet = vdpa_to_snet(vdev);
- void __iomem *cfg_ptr = snet->cfg->virtio_cfg + offset;
- u8 *buf_ptr = buf;
- u32 i;
- /* check for offset error */
- if (offset + len > snet->cfg->cfg_size)
- return;
- /* Write into buffer */
- for (i = 0; i < len; i++)
- *buf_ptr++ = ioread8(cfg_ptr + i);
- }
- static void snet_set_config(struct vdpa_device *vdev, unsigned int offset,
- const void *buf, unsigned int len)
- {
- struct snet *snet = vdpa_to_snet(vdev);
- void __iomem *cfg_ptr = snet->cfg->virtio_cfg + offset;
- const u8 *buf_ptr = buf;
- u32 i;
- /* check for offset error */
- if (offset + len > snet->cfg->cfg_size)
- return;
- /* Write into PCI BAR */
- for (i = 0; i < len; i++)
- iowrite8(*buf_ptr++, cfg_ptr + i);
- }
- static int snet_suspend(struct vdpa_device *vdev)
- {
- struct snet *snet = vdpa_to_snet(vdev);
- int ret;
- ret = snet_suspend_dev(snet);
- if (ret)
- SNET_ERR(snet->pdev, "SNET[%u] suspend failed, err: %d\n", snet->sid, ret);
- else
- SNET_DBG(snet->pdev, "Suspend SNET[%u] device\n", snet->sid);
- return ret;
- }
- static int snet_resume(struct vdpa_device *vdev)
- {
- struct snet *snet = vdpa_to_snet(vdev);
- int ret;
- ret = snet_resume_dev(snet);
- if (ret)
- SNET_ERR(snet->pdev, "SNET[%u] resume failed, err: %d\n", snet->sid, ret);
- else
- SNET_DBG(snet->pdev, "Resume SNET[%u] device\n", snet->sid);
- return ret;
- }
- static const struct vdpa_config_ops snet_config_ops = {
- .set_vq_address = snet_set_vq_address,
- .set_vq_num = snet_set_vq_num,
- .kick_vq = snet_kick_vq,
- .kick_vq_with_data = snet_kick_vq_with_data,
- .set_vq_cb = snet_set_vq_cb,
- .set_vq_ready = snet_set_vq_ready,
- .get_vq_ready = snet_get_vq_ready,
- .set_vq_state = snet_set_vq_state,
- .get_vq_state = snet_get_vq_state,
- .get_vq_irq = snet_get_vq_irq,
- .get_vq_align = snet_get_vq_align,
- .reset = snet_reset,
- .get_config_size = snet_get_config_size,
- .get_device_features = snet_get_features,
- .set_driver_features = snet_set_drv_features,
- .get_driver_features = snet_get_drv_features,
- .get_vq_num_min = snet_get_vq_num_max,
- .get_vq_num_max = snet_get_vq_num_max,
- .set_config_cb = snet_set_config_cb,
- .get_device_id = snet_get_device_id,
- .get_vendor_id = snet_get_vendor_id,
- .get_status = snet_get_status,
- .set_status = snet_set_status,
- .get_config = snet_get_config,
- .set_config = snet_set_config,
- .suspend = snet_suspend,
- .resume = snet_resume,
- };
- static int psnet_open_pf_bar(struct pci_dev *pdev, struct psnet *psnet)
- {
- char *name;
- int ret, i, mask = 0;
- /* We don't know which BAR will be used to communicate..
- * We will map every bar with len > 0.
- *
- * Later, we will discover the BAR and unmap all other BARs.
- */
- for (i = 0; i < PCI_STD_NUM_BARS; i++) {
- if (pci_resource_len(pdev, i))
- mask |= (1 << i);
- }
- /* No BAR can be used.. */
- if (!mask) {
- SNET_ERR(pdev, "Failed to find a PCI BAR\n");
- return -ENODEV;
- }
- name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "psnet[%s]-bars", pci_name(pdev));
- if (!name)
- return -ENOMEM;
- ret = pcim_iomap_regions(pdev, mask, name);
- if (ret) {
- SNET_ERR(pdev, "Failed to request and map PCI BARs\n");
- return ret;
- }
- for (i = 0; i < PCI_STD_NUM_BARS; i++) {
- if (mask & (1 << i))
- psnet->bars[i] = pcim_iomap_table(pdev)[i];
- }
- return 0;
- }
- static int snet_open_vf_bar(struct pci_dev *pdev, struct snet *snet)
- {
- char *name;
- int ret;
- name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "snet[%s]-bars", pci_name(pdev));
- if (!name)
- return -ENOMEM;
- /* Request and map BAR */
- ret = pcim_iomap_regions(pdev, BIT(snet->psnet->cfg.vf_bar), name);
- if (ret) {
- SNET_ERR(pdev, "Failed to request and map PCI BAR for a VF\n");
- return ret;
- }
- snet->bar = pcim_iomap_table(pdev)[snet->psnet->cfg.vf_bar];
- return 0;
- }
- static void snet_free_cfg(struct snet_cfg *cfg)
- {
- u32 i;
- if (!cfg->devs)
- return;
- /* Free devices */
- for (i = 0; i < cfg->devices_num; i++) {
- if (!cfg->devs[i])
- break;
- kfree(cfg->devs[i]);
- }
- /* Free pointers to devices */
- kfree(cfg->devs);
- }
- /* Detect which BAR is used for communication with the device. */
- static int psnet_detect_bar(struct psnet *psnet, u32 off)
- {
- unsigned long exit_time;
- int i;
- exit_time = jiffies + usecs_to_jiffies(SNET_DETECT_TIMEOUT);
- /* SNET DPU will write SNET's signature when the config is ready. */
- while (time_before(jiffies, exit_time)) {
- for (i = 0; i < PCI_STD_NUM_BARS; i++) {
- /* Is this BAR mapped? */
- if (!psnet->bars[i])
- continue;
- if (ioread32(psnet->bars[i] + off) == SNET_SIGNATURE)
- return i;
- }
- usleep_range(1000, 10000);
- }
- return -ENODEV;
- }
- static void psnet_unmap_unused_bars(struct pci_dev *pdev, struct psnet *psnet)
- {
- int i, mask = 0;
- for (i = 0; i < PCI_STD_NUM_BARS; i++) {
- if (psnet->bars[i] && i != psnet->barno)
- mask |= (1 << i);
- }
- if (mask)
- pcim_iounmap_regions(pdev, mask);
- }
- /* Read SNET config from PCI BAR */
- static int psnet_read_cfg(struct pci_dev *pdev, struct psnet *psnet)
- {
- struct snet_cfg *cfg = &psnet->cfg;
- u32 i, off;
- int barno;
- /* Move to where the config starts */
- off = SNET_CONFIG_OFF;
- /* Find BAR used for communication */
- barno = psnet_detect_bar(psnet, off);
- if (barno < 0) {
- SNET_ERR(pdev, "SNET config is not ready.\n");
- return barno;
- }
- /* Save used BAR number and unmap all other BARs */
- psnet->barno = barno;
- SNET_DBG(pdev, "Using BAR number %d\n", barno);
- psnet_unmap_unused_bars(pdev, psnet);
- /* load config from BAR */
- cfg->key = psnet_read32(psnet, off);
- off += 4;
- cfg->cfg_size = psnet_read32(psnet, off);
- off += 4;
- cfg->cfg_ver = psnet_read32(psnet, off);
- off += 4;
- /* The negotiated config version is the lower one between this driver's config
- * and the DPU's.
- */
- psnet->negotiated_cfg_ver = min_t(u32, cfg->cfg_ver, SNET_CFG_VERSION);
- SNET_DBG(pdev, "SNET config version %u\n", psnet->negotiated_cfg_ver);
- cfg->vf_num = psnet_read32(psnet, off);
- off += 4;
- cfg->vf_bar = psnet_read32(psnet, off);
- off += 4;
- cfg->host_cfg_off = psnet_read32(psnet, off);
- off += 4;
- cfg->max_size_host_cfg = psnet_read32(psnet, off);
- off += 4;
- cfg->virtio_cfg_off = psnet_read32(psnet, off);
- off += 4;
- cfg->kick_off = psnet_read32(psnet, off);
- off += 4;
- cfg->hwmon_off = psnet_read32(psnet, off);
- off += 4;
- cfg->ctrl_off = psnet_read32(psnet, off);
- off += 4;
- cfg->flags = psnet_read32(psnet, off);
- off += 4;
- /* Ignore Reserved */
- off += sizeof(cfg->rsvd);
- cfg->devices_num = psnet_read32(psnet, off);
- off += 4;
- /* Allocate memory to hold pointer to the devices */
- cfg->devs = kcalloc(cfg->devices_num, sizeof(void *), GFP_KERNEL);
- if (!cfg->devs)
- return -ENOMEM;
- /* Load device configuration from BAR */
- for (i = 0; i < cfg->devices_num; i++) {
- cfg->devs[i] = kzalloc(sizeof(*cfg->devs[i]), GFP_KERNEL);
- if (!cfg->devs[i]) {
- snet_free_cfg(cfg);
- return -ENOMEM;
- }
- /* Read device config */
- cfg->devs[i]->virtio_id = psnet_read32(psnet, off);
- off += 4;
- cfg->devs[i]->vq_num = psnet_read32(psnet, off);
- off += 4;
- cfg->devs[i]->vq_size = psnet_read32(psnet, off);
- off += 4;
- cfg->devs[i]->vfid = psnet_read32(psnet, off);
- off += 4;
- cfg->devs[i]->features = psnet_read64(psnet, off);
- off += 8;
- /* Ignore Reserved */
- off += sizeof(cfg->devs[i]->rsvd);
- cfg->devs[i]->cfg_size = psnet_read32(psnet, off);
- off += 4;
- /* Is the config witten to the DPU going to be too big? */
- if (SNET_GENERAL_CFG_LEN + SNET_GENERAL_CFG_VQ_LEN * cfg->devs[i]->vq_num >
- cfg->max_size_host_cfg) {
- SNET_ERR(pdev, "Failed to read SNET config, the config is too big..\n");
- snet_free_cfg(cfg);
- return -EINVAL;
- }
- }
- return 0;
- }
- static int psnet_alloc_irq_vector(struct pci_dev *pdev, struct psnet *psnet)
- {
- int ret = 0;
- u32 i, irq_num = 0;
- /* Let's count how many IRQs we need, 1 for every VQ + 1 for config change */
- for (i = 0; i < psnet->cfg.devices_num; i++)
- irq_num += psnet->cfg.devs[i]->vq_num + 1;
- ret = pci_alloc_irq_vectors(pdev, irq_num, irq_num, PCI_IRQ_MSIX);
- if (ret != irq_num) {
- SNET_ERR(pdev, "Failed to allocate IRQ vectors\n");
- return ret;
- }
- SNET_DBG(pdev, "Allocated %u IRQ vectors from physical function\n", irq_num);
- return 0;
- }
- static int snet_alloc_irq_vector(struct pci_dev *pdev, struct snet_dev_cfg *snet_cfg)
- {
- int ret = 0;
- u32 irq_num;
- /* We want 1 IRQ for every VQ + 1 for config change events */
- irq_num = snet_cfg->vq_num + 1;
- ret = pci_alloc_irq_vectors(pdev, irq_num, irq_num, PCI_IRQ_MSIX);
- if (ret <= 0) {
- SNET_ERR(pdev, "Failed to allocate IRQ vectors\n");
- return ret;
- }
- return 0;
- }
- static void snet_free_vqs(struct snet *snet)
- {
- u32 i;
- if (!snet->vqs)
- return;
- for (i = 0 ; i < snet->cfg->vq_num ; i++) {
- if (!snet->vqs[i])
- break;
- kfree(snet->vqs[i]);
- }
- kfree(snet->vqs);
- }
- static int snet_build_vqs(struct snet *snet)
- {
- u32 i;
- /* Allocate the VQ pointers array */
- snet->vqs = kcalloc(snet->cfg->vq_num, sizeof(void *), GFP_KERNEL);
- if (!snet->vqs)
- return -ENOMEM;
- /* Allocate the VQs */
- for (i = 0; i < snet->cfg->vq_num; i++) {
- snet->vqs[i] = kzalloc(sizeof(*snet->vqs[i]), GFP_KERNEL);
- if (!snet->vqs[i]) {
- snet_free_vqs(snet);
- return -ENOMEM;
- }
- /* Reset IRQ num */
- snet->vqs[i]->irq = -1;
- /* VQ serial ID */
- snet->vqs[i]->sid = i;
- /* Kick address - every VQ gets 4B */
- snet->vqs[i]->kick_ptr = snet->bar + snet->psnet->cfg.kick_off +
- snet->vqs[i]->sid * 4;
- /* Clear kick address for this VQ */
- iowrite32(0, snet->vqs[i]->kick_ptr);
- }
- return 0;
- }
- static int psnet_get_next_irq_num(struct psnet *psnet)
- {
- int irq;
- spin_lock(&psnet->lock);
- irq = psnet->next_irq++;
- spin_unlock(&psnet->lock);
- return irq;
- }
- static void snet_reserve_irq_idx(struct pci_dev *pdev, struct snet *snet)
- {
- struct psnet *psnet = snet->psnet;
- int i;
- /* one IRQ for every VQ, and one for config changes */
- snet->cfg_irq_idx = psnet_get_next_irq_num(psnet);
- snprintf(snet->cfg_irq_name, SNET_NAME_SIZE, "snet[%s]-cfg[%d]",
- pci_name(pdev), snet->cfg_irq_idx);
- for (i = 0; i < snet->cfg->vq_num; i++) {
- /* Get next free IRQ ID */
- snet->vqs[i]->irq_idx = psnet_get_next_irq_num(psnet);
- /* Write IRQ name */
- snprintf(snet->vqs[i]->irq_name, SNET_NAME_SIZE, "snet[%s]-vq[%d]",
- pci_name(pdev), snet->vqs[i]->irq_idx);
- }
- }
- /* Find a device config based on virtual function id */
- static struct snet_dev_cfg *snet_find_dev_cfg(struct snet_cfg *cfg, u32 vfid)
- {
- u32 i;
- for (i = 0; i < cfg->devices_num; i++) {
- if (cfg->devs[i]->vfid == vfid)
- return cfg->devs[i];
- }
- /* Oppss.. no config found.. */
- return NULL;
- }
- /* Probe function for a physical PCI function */
- static int snet_vdpa_probe_pf(struct pci_dev *pdev)
- {
- struct psnet *psnet;
- int ret = 0;
- bool pf_irqs = false;
- ret = pcim_enable_device(pdev);
- if (ret) {
- SNET_ERR(pdev, "Failed to enable PCI device\n");
- return ret;
- }
- /* Allocate a PCI physical function device */
- psnet = kzalloc(sizeof(*psnet), GFP_KERNEL);
- if (!psnet)
- return -ENOMEM;
- /* Init PSNET spinlock */
- spin_lock_init(&psnet->lock);
- pci_set_master(pdev);
- pci_set_drvdata(pdev, psnet);
- /* Open SNET MAIN BAR */
- ret = psnet_open_pf_bar(pdev, psnet);
- if (ret)
- goto free_psnet;
- /* Try to read SNET's config from PCI BAR */
- ret = psnet_read_cfg(pdev, psnet);
- if (ret)
- goto free_psnet;
- /* If SNET_CFG_FLAG_IRQ_PF flag is set, we should use
- * PF MSI-X vectors
- */
- pf_irqs = PSNET_FLAG_ON(psnet, SNET_CFG_FLAG_IRQ_PF);
- if (pf_irqs) {
- ret = psnet_alloc_irq_vector(pdev, psnet);
- if (ret)
- goto free_cfg;
- }
- SNET_DBG(pdev, "Enable %u virtual functions\n", psnet->cfg.vf_num);
- ret = pci_enable_sriov(pdev, psnet->cfg.vf_num);
- if (ret) {
- SNET_ERR(pdev, "Failed to enable SR-IOV\n");
- goto free_irq;
- }
- /* Create HW monitor device */
- if (PSNET_FLAG_ON(psnet, SNET_CFG_FLAG_HWMON)) {
- #if IS_ENABLED(CONFIG_HWMON)
- psnet_create_hwmon(pdev);
- #else
- SNET_WARN(pdev, "Can't start HWMON, CONFIG_HWMON is not enabled\n");
- #endif
- }
- return 0;
- free_irq:
- if (pf_irqs)
- pci_free_irq_vectors(pdev);
- free_cfg:
- snet_free_cfg(&psnet->cfg);
- free_psnet:
- kfree(psnet);
- return ret;
- }
- /* Probe function for a virtual PCI function */
- static int snet_vdpa_probe_vf(struct pci_dev *pdev)
- {
- struct pci_dev *pdev_pf = pdev->physfn;
- struct psnet *psnet = pci_get_drvdata(pdev_pf);
- struct snet_dev_cfg *dev_cfg;
- struct snet *snet;
- u32 vfid;
- int ret;
- bool pf_irqs = false;
- /* Get virtual function id.
- * (the DPU counts the VFs from 1)
- */
- ret = pci_iov_vf_id(pdev);
- if (ret < 0) {
- SNET_ERR(pdev, "Failed to find a VF id\n");
- return ret;
- }
- vfid = ret + 1;
- /* Find the snet_dev_cfg based on vfid */
- dev_cfg = snet_find_dev_cfg(&psnet->cfg, vfid);
- if (!dev_cfg) {
- SNET_WARN(pdev, "Failed to find a VF config..\n");
- return -ENODEV;
- }
- /* Which PCI device should allocate the IRQs?
- * If the SNET_CFG_FLAG_IRQ_PF flag set, the PF device allocates the IRQs
- */
- pf_irqs = PSNET_FLAG_ON(psnet, SNET_CFG_FLAG_IRQ_PF);
- ret = pcim_enable_device(pdev);
- if (ret) {
- SNET_ERR(pdev, "Failed to enable PCI VF device\n");
- return ret;
- }
- /* Request for MSI-X IRQs */
- if (!pf_irqs) {
- ret = snet_alloc_irq_vector(pdev, dev_cfg);
- if (ret)
- return ret;
- }
- /* Allocate vdpa device */
- snet = vdpa_alloc_device(struct snet, vdpa, &pdev->dev, &snet_config_ops, 1, 1, NULL,
- false);
- if (!snet) {
- SNET_ERR(pdev, "Failed to allocate a vdpa device\n");
- ret = -ENOMEM;
- goto free_irqs;
- }
- /* Init control mutex and spinlock */
- mutex_init(&snet->ctrl_lock);
- spin_lock_init(&snet->ctrl_spinlock);
- /* Save pci device pointer */
- snet->pdev = pdev;
- snet->psnet = psnet;
- snet->cfg = dev_cfg;
- snet->dpu_ready = false;
- snet->sid = vfid;
- /* Reset IRQ value */
- snet->cfg_irq = -1;
- ret = snet_open_vf_bar(pdev, snet);
- if (ret)
- goto put_device;
- /* Create a VirtIO config pointer */
- snet->cfg->virtio_cfg = snet->bar + snet->psnet->cfg.virtio_cfg_off;
- /* Clear control registers */
- snet_ctrl_clear(snet);
- pci_set_master(pdev);
- pci_set_drvdata(pdev, snet);
- ret = snet_build_vqs(snet);
- if (ret)
- goto put_device;
- /* Reserve IRQ indexes,
- * The IRQs may be requested and freed multiple times,
- * but the indexes won't change.
- */
- snet_reserve_irq_idx(pf_irqs ? pdev_pf : pdev, snet);
- /*set DMA device*/
- snet->vdpa.dma_dev = &pdev->dev;
- /* Register VDPA device */
- ret = vdpa_register_device(&snet->vdpa, snet->cfg->vq_num);
- if (ret) {
- SNET_ERR(pdev, "Failed to register vdpa device\n");
- goto free_vqs;
- }
- return 0;
- free_vqs:
- snet_free_vqs(snet);
- put_device:
- put_device(&snet->vdpa.dev);
- free_irqs:
- if (!pf_irqs)
- pci_free_irq_vectors(pdev);
- return ret;
- }
- static int snet_vdpa_probe(struct pci_dev *pdev, const struct pci_device_id *id)
- {
- if (pdev->is_virtfn)
- return snet_vdpa_probe_vf(pdev);
- else
- return snet_vdpa_probe_pf(pdev);
- }
- static void snet_vdpa_remove_pf(struct pci_dev *pdev)
- {
- struct psnet *psnet = pci_get_drvdata(pdev);
- pci_disable_sriov(pdev);
- /* If IRQs are allocated from the PF, we should free the IRQs */
- if (PSNET_FLAG_ON(psnet, SNET_CFG_FLAG_IRQ_PF))
- pci_free_irq_vectors(pdev);
- snet_free_cfg(&psnet->cfg);
- kfree(psnet);
- }
- static void snet_vdpa_remove_vf(struct pci_dev *pdev)
- {
- struct snet *snet = pci_get_drvdata(pdev);
- struct psnet *psnet = snet->psnet;
- vdpa_unregister_device(&snet->vdpa);
- snet_free_vqs(snet);
- /* If IRQs are allocated from the VF, we should free the IRQs */
- if (!PSNET_FLAG_ON(psnet, SNET_CFG_FLAG_IRQ_PF))
- pci_free_irq_vectors(pdev);
- }
- static void snet_vdpa_remove(struct pci_dev *pdev)
- {
- if (pdev->is_virtfn)
- snet_vdpa_remove_vf(pdev);
- else
- snet_vdpa_remove_pf(pdev);
- }
- static struct pci_device_id snet_driver_pci_ids[] = {
- { PCI_DEVICE_SUB(PCI_VENDOR_ID_SOLIDRUN, SNET_DEVICE_ID,
- PCI_VENDOR_ID_SOLIDRUN, SNET_DEVICE_ID) },
- { 0 },
- };
- MODULE_DEVICE_TABLE(pci, snet_driver_pci_ids);
- static struct pci_driver snet_vdpa_driver = {
- .name = "snet-vdpa-driver",
- .id_table = snet_driver_pci_ids,
- .probe = snet_vdpa_probe,
- .remove = snet_vdpa_remove,
- };
- module_pci_driver(snet_vdpa_driver);
- MODULE_AUTHOR("Alvaro Karsz <alvaro.karsz@solid-run.com>");
- MODULE_DESCRIPTION("SolidRun vDPA driver");
- MODULE_LICENSE("GPL v2");
|