From 75f8c72dbb2ee7b1cbcfedb6ac38c0360a3e885b Mon Sep 17 00:00:00 2001 From: yangshuyong Date: Tue, 27 Feb 2024 22:00:21 +0800 Subject: [PATCH] drivers/pci/pci_qemu_edu: update qemu edu driver code Signed-off-by: yangshuyong Signed-off-by: Bowen Wang Signed-off-by: lipengfei28 --- drivers/Kconfig | 1 - drivers/Makefile | 1 - drivers/pci/Kconfig | 6 + drivers/pci/Make.defs | 4 + drivers/pci/pci_drivers.c | 11 + drivers/pci/pci_qemu_edu.c | 488 ++++++++++++++++++++++++++++++ drivers/virt/Kconfig | 22 -- drivers/virt/Make.defs | 33 -- drivers/virt/qemu_edu.c | 470 ---------------------------- include/nuttx/pci/pci_qemu_edu.h | 60 ++++ include/nuttx/pci/pci_qemu_test.h | 4 - 11 files changed, 569 insertions(+), 531 deletions(-) create mode 100644 drivers/pci/pci_qemu_edu.c delete mode 100644 drivers/virt/Kconfig delete mode 100644 drivers/virt/Make.defs delete mode 100644 drivers/virt/qemu_edu.c create mode 100644 include/nuttx/pci/pci_qemu_edu.h diff --git a/drivers/Kconfig b/drivers/Kconfig index 22603be3316..fe95fa7600e 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -60,4 +60,3 @@ source "drivers/dma/Kconfig" source "drivers/devicetree/Kconfig" source "drivers/reset/Kconfig" source "drivers/pci/Kconfig" -source "drivers/virt/Kconfig" diff --git a/drivers/Makefile b/drivers/Makefile index 40ed2d38af1..e5344abc079 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -78,7 +78,6 @@ include segger/Make.defs include usrsock/Make.defs include reset/Make.defs include pci/Make.defs -include virt/Make.defs ifeq ($(CONFIG_SPECIFIC_DRIVERS),y) -include platform/Make.defs diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig index 86d9d34714b..f6396b2e81a 100644 --- a/drivers/pci/Kconfig +++ b/drivers/pci/Kconfig @@ -25,4 +25,10 @@ config PCI_QEMU_TEST ---help--- Driver for QEMU PCI test device +config PCI_QEMU_EDU + bool "Driver for QEMU EDU test device" + default n + ---help--- + Driver for QEMU EDU test device + endif # PCI diff --git a/drivers/pci/Make.defs b/drivers/pci/Make.defs index 46851a4ef81..efdfd660825 100644 --- a/drivers/pci/Make.defs +++ b/drivers/pci/Make.defs @@ -25,6 +25,10 @@ ifeq ($(CONFIG_PCI_QEMU_TEST),y) CSRCS += pci_qemu_test.c endif +ifeq ($(CONFIG_PCI_QEMU_EDU),y) +CSRCS += pci_qemu_edu.c +endif + # Include PCI device driver build support DEPPATH += --dep-path pci diff --git a/drivers/pci/pci_drivers.c b/drivers/pci/pci_drivers.c index 6946b002716..b746e5b11c4 100644 --- a/drivers/pci/pci_drivers.c +++ b/drivers/pci/pci_drivers.c @@ -25,6 +25,7 @@ #include #include +#include #include /**************************************************************************** @@ -53,6 +54,16 @@ int pci_register_drivers(void) } #endif + /* Initialization qemu edu driver */ + +#ifdef CONFIG_PCI_QEMU_EDU + ret = pci_register_qemu_edu_driver(); + if (ret < 0) + { + pcierr("pci_register_qemu_edu_driver failed, ret=%d\n", ret); + } +#endif + UNUSED(ret); return ret; } diff --git a/drivers/pci/pci_qemu_edu.c b/drivers/pci/pci_qemu_edu.c new file mode 100644 index 00000000000..37b1627052c --- /dev/null +++ b/drivers/pci/pci_qemu_edu.c @@ -0,0 +1,488 @@ +/**************************************************************************** + * drivers/pci/pci_qemu_edu.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define PCI_QEMU_EDU_CONTROL_BAR_ID 0 + +/* Registers defined for device. Size 4 for < 0x80. Size 8 for >= 0x80. */ + +#define PCI_QEMU_EDU_REG_ID 0x00 /* Identification */ +#define PCI_QEMU_EDU_REG_LIVE 0x04 /* Liveness Check */ +#define PCI_QEMU_EDU_REG_FAC 0x08 /* Factorial Computation */ +#define PCI_QEMU_EDU_REG_STATUS 0x20 /* Status */ +#define PCI_QEMU_EDU_REG_INT_STATUS 0x24 /* Interupt Status */ +#define PCI_QEMU_EDU_REG_INT_RAISE 0x60 /* Raise an interrupt */ +#define PCI_QEMU_EDU_REG_INT_ACK 0x64 /* Acknowledge interrupt */ +#define PCI_QEMU_EDU_REG_DMA_SOURCE 0x80 /* Source address for DMA transfer */ +#define PCI_QEMU_EDU_REG_DMA_DEST 0x88 /* Destination address for DMA transfer */ +#define PCI_QEMU_EDU_REG_DMA_COUNT 0x90 /* Size of area to transfer with DMA */ +#define PCI_QEMU_EDU_REG_DMA_CMD 0x98 /* Control DMA tranfer */ + +/* One 4096 bytes long buffer at offset 0x40000 is available in the + * EDU device + */ + +#define PCI_QEMU_EDU_DMABUF_OFFSET 0x40000 + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct pci_qemu_edu_priv_s +{ + FAR struct pci_device_s *dev; + uintptr_t base_addr; + sem_t isr_done; + uint32_t test_result; +}; + +/**************************************************************************** + * Private Functions Definitions + ****************************************************************************/ + +static uint32_t pci_qemu_edu_read_reg32(FAR struct pci_qemu_edu_priv_s *priv, + int reg); +static void pci_qemu_edu_write_reg32(FAR struct pci_qemu_edu_priv_s *priv, + int reg, uint32_t val); +static void pci_qemu_edu_write_reg64(FAR struct pci_qemu_edu_priv_s *priv, + int reg, uint64_t val); + +static void pci_qemu_edu_test_poll(FAR struct pci_qemu_edu_priv_s *priv); +static void pci_qemu_edu_test_intx(FAR struct pci_qemu_edu_priv_s *priv); +static int pci_qemu_edu_interrupt(int irq, FAR void *context, FAR void *arg); + +static int pci_qemu_edu_probe(FAR struct pci_device_s *dev); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static const struct pci_device_id_s g_pci_qemu_edu_id_table[] = +{ + { PCI_DEVICE(0x1234, 0x11e8), }, + { } +}; + +static struct pci_driver_s g_pci_qemu_edu_pci_drv = +{ + .id_table = g_pci_qemu_edu_id_table, + .probe = pci_qemu_edu_probe, +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: pci_qemu_edu_read_reg32 + * + * Description: + * Provide a read interface for 32bit mapped registers + * + * Input Parameters: + * priv - Edu driver private data + * reg - Register offset + * + * Returned Value: + * Register value + * + ****************************************************************************/ + +static uint32_t pci_qemu_edu_read_reg32(FAR struct pci_qemu_edu_priv_s *priv, + int reg) +{ + return *(FAR volatile uint32_t *)(priv->base_addr + reg); +} + +/**************************************************************************** + * Name: pci_qemu_edu_write_reg32 + * + * Description: + * Provide a write interface for 32bit mapped registers + * + * Input Parameters: + * priv - Edu driver private data + * reg - Register offset + * val - Value to assign to register + * + ****************************************************************************/ + +static void pci_qemu_edu_write_reg32(FAR struct pci_qemu_edu_priv_s *priv, + int reg, uint32_t val) +{ + *(FAR volatile uint32_t *)(priv->base_addr + reg) = val; +} + +/**************************************************************************** + * Name: pci_qemu_edu_write_reg64 + * + * Description: + * Provide a write interface for 64bit mapped registers + * + * Input Parameters: + * priv - Edu driver private data + * reg - Register offset + * val - Value to assign to register + * + ****************************************************************************/ + +static void pci_qemu_edu_write_reg64(FAR struct pci_qemu_edu_priv_s *priv, + int reg, uint64_t val) +{ + *(FAR volatile uint64_t *)(priv->base_addr + reg) = val; +} + +/**************************************************************************** + * Name: pci_qemu_edu_test_poll + * + * Description: + * Performs basic functional test of PCI device and MMIO using polling + * of mapped register interfaces. + * + * Input Parameters: + * priv - Edu driver private data + * + ****************************************************************************/ + +static void pci_qemu_edu_test_poll(FAR struct pci_qemu_edu_priv_s *priv) +{ + uint32_t test_value; + uint32_t test_read; + + pciinfo("Identification: 0x%08" PRIx32 "u\n", + pci_qemu_edu_read_reg32(priv, PCI_QEMU_EDU_REG_ID)); + + /* Test Live Check */ + + test_value = 0xdeadbeef; + pci_qemu_edu_write_reg32(priv, PCI_QEMU_EDU_REG_LIVE, test_value); + test_read = pci_qemu_edu_read_reg32(priv, PCI_QEMU_EDU_REG_LIVE); + pciinfo("Live Check: Wrote: 0x%08" PRIx32 " Read: 0x%08" PRIx32 + " Error Bits 0x%08" PRIx32 "\n", + test_value, test_read, test_read ^ ~test_value); + pciinfo("TEST %s\n", ((test_read ^ ~test_value) == 0) ? "PASS" : "FAIL"); + + /* Test Factorial */ + + test_value = 10; + pci_qemu_edu_write_reg32(priv, PCI_QEMU_EDU_REG_STATUS, 0); + pci_qemu_edu_write_reg32(priv, PCI_QEMU_EDU_REG_FAC, test_value); + while (pci_qemu_edu_read_reg32(priv, PCI_QEMU_EDU_REG_STATUS) & 0x01) + { + pciinfo("Waiting to compute factorial..."); + usleep(10000); + } + + test_read = pci_qemu_edu_read_reg32(priv, PCI_QEMU_EDU_REG_FAC); + pciinfo("Computed factorial of %" PRIu32 " as %" PRIu32 "\n", + test_value, test_read); + pciinfo("TEST %s\n", (test_read == 3628800) ? "PASS" : "FAIL"); +} + +/**************************************************************************** + * Name: pci_qemu_edu_test_intx + * + * Description: + * Performs basic functional test of PCI device and MMIO using INTx + * + * Input Parameters: + * priv - Struct containing internal state of driver + * + ****************************************************************************/ + +static void pci_qemu_edu_test_intx(FAR struct pci_qemu_edu_priv_s *priv) +{ + uint32_t test_value; + + pciinfo("Identification: 0x%08" PRIx32 "u\n", + pci_qemu_edu_read_reg32(priv, PCI_QEMU_EDU_REG_ID)); + + /* Test Read/Write */ + + test_value = 0xdeadbeef; + pciinfo("Triggering interrupt with value 0x%08" PRIx32 "\n", test_value); + pci_qemu_edu_write_reg32(priv, PCI_QEMU_EDU_REG_INT_RAISE, test_value); + nxsem_wait(&priv->isr_done); + pciinfo("TEST %s\n", (priv->test_result == test_value) ? "PASS" : "FAIL"); + + /* Test Factorial */ + + test_value = 5; + pciinfo("Computing factorial of %" PRIu32 "\n", test_value); + pci_qemu_edu_write_reg32(priv, PCI_QEMU_EDU_REG_STATUS, 0x80); + pci_qemu_edu_write_reg32(priv, PCI_QEMU_EDU_REG_FAC, test_value); + nxsem_wait(&priv->isr_done); + pciinfo("TEST %s\n", (priv->test_result == 120) ? "PASS" : "FAIL"); + + /* Test ISR Status Cleanup */ + + pci_qemu_edu_write_reg32(priv, PCI_QEMU_EDU_REG_INT_RAISE, test_value); + nxsem_wait(&priv->isr_done); + pciinfo("TEST %s\n", (priv->test_result == test_value) ? + "PASS" : "FAIL"); +} + +/**************************************************************************** + * Name: pci_qemu_edu_test_dma + * + * Description: + * Performs dma functional test of PCI device + * + * Input Parameters: + * priv - Struct containing internal state of driver + * + ****************************************************************************/ + +static void pci_qemu_edu_test_dma(FAR struct pci_qemu_edu_priv_s *priv) +{ + const uint64_t dev_addr = PCI_QEMU_EDU_DMABUF_OFFSET; + const size_t block_size = 2048; + FAR void *test_block; + uint32_t tx_checksum; + uint32_t rx_checksum; + uint32_t psrand; + int i; + + pciinfo("Identification: 0x%08" PRIx32 "u\n", + pci_qemu_edu_read_reg32(priv, PCI_QEMU_EDU_REG_ID)); + + test_block = kmm_malloc(block_size); + for (i = 0; i < block_size; i++) + { + *((FAR uint8_t *)test_block + i) = i & 0xff; + } + + tx_checksum = 0; + psrand = 0x0011223344; + for (i = 0; i < block_size / 4; i++) + { + /* Fill the memory block with "random" data */ + + psrand ^= psrand << 13; + psrand ^= psrand >> 17; + psrand ^= psrand << 5; + *((FAR uint32_t *)test_block + i) = psrand; + tx_checksum += psrand; + } + + pciinfo("Test block checksum 0x%08" PRIx32 "\n", tx_checksum); + pci_qemu_edu_write_reg64(priv, PCI_QEMU_EDU_REG_DMA_SOURCE, + (uint64_t)test_block); + pci_qemu_edu_write_reg64(priv, PCI_QEMU_EDU_REG_DMA_DEST, dev_addr); + pci_qemu_edu_write_reg64(priv, PCI_QEMU_EDU_REG_DMA_COUNT, + (uint64_t)block_size); + pci_qemu_edu_write_reg32(priv, PCI_QEMU_EDU_REG_STATUS, 0x00); + pci_qemu_edu_write_reg64(priv, PCI_QEMU_EDU_REG_DMA_CMD, 0x01 | 0x04); + nxsem_wait(&priv->isr_done); + + pciinfo("DMA transfer to device complete.\n"); + + pci_qemu_edu_write_reg64(priv, PCI_QEMU_EDU_REG_DMA_DEST, + (uint64_t)test_block); + pci_qemu_edu_write_reg64(priv, PCI_QEMU_EDU_REG_DMA_SOURCE, dev_addr); + pci_qemu_edu_write_reg64(priv, PCI_QEMU_EDU_REG_DMA_COUNT, + (uint64_t)block_size); + pci_qemu_edu_write_reg32(priv, PCI_QEMU_EDU_REG_STATUS, 0x00); + pci_qemu_edu_write_reg64(priv, PCI_QEMU_EDU_REG_DMA_CMD, + 0x01 | 0x02 | 0x04); + nxsem_wait(&priv->isr_done); + + pciinfo("DMA transfer from device complete.\n"); + rx_checksum = 0; + for (i = 0; i < block_size / 4; i++) + { + rx_checksum += *((FAR uint32_t *)test_block + i); + } + + kmm_free(test_block); + + pciinfo("Received block checksum 0x%08" PRIx32 "\n", rx_checksum); + pciinfo("TEST %s\n", (rx_checksum == tx_checksum) ? "PASS" : "FAIL"); +} + +/**************************************************************************** + * Name: pci_qemu_edu_interrupt + * + * Description: + * EDU interrupt handler + * + ****************************************************************************/ + +static int pci_qemu_edu_interrupt(int irq, FAR void *context, FAR void *arg) +{ + FAR struct pci_qemu_edu_priv_s *priv = + (FAR struct pci_qemu_edu_priv_s *)arg; + uint32_t status; + + status = pci_qemu_edu_read_reg32(priv, PCI_QEMU_EDU_REG_INT_STATUS); + pci_qemu_edu_write_reg32(priv, PCI_QEMU_EDU_REG_INT_ACK, ~0u); + switch (status) + { + /* Factorial triggered */ + + case 0x1: + { + priv->test_result = + pci_qemu_edu_read_reg32(priv, PCI_QEMU_EDU_REG_FAC); + pciinfo("Computed factorial: %" PRIu32 "\n", priv->test_result); + break; + } + + /* DMA triggered */ + + case 0x100: + { + pciinfo("DMA transfer complete\n"); + break; + } + + /* Generic write */ + + default: + { + priv->test_result = status; + pciinfo("Received value: 0x%08" PRIx32 "\n", status); + break; + } + } + + nxsem_post(&priv->isr_done); + return OK; +} + +/**************************************************************************** + * Name: pci_qemu_edu_probe + * + * Description: + * Initialize device + * + ****************************************************************************/ + +static int pci_qemu_edu_probe(FAR struct pci_device_s *dev) +{ + struct pci_qemu_edu_priv_s priv; + unsigned int flags; + uint8_t irq; + int ret; + + /* Enable EDU device */ + + ret = pci_enable_device(dev); + if (ret < 0) + { + pcierr("Enable device failed, ret=%d\n", ret); + return ret; + } + + pci_set_master(dev); + + /* Initialize the edu driver */ + + pciinfo("EDU Device Init\n"); + + priv.dev = dev; + flags = pci_resource_flags(dev, PCI_QEMU_EDU_CONTROL_BAR_ID); + if ((flags & PCI_RESOURCE_MEM) != PCI_RESOURCE_MEM) + { + ret = -ENODEV; + pcierr("Control bar expected to be MMIO, flags=0x%x\n", flags); + goto err; + } + + priv.base_addr = (uintptr_t)pci_map_bar(dev, PCI_QEMU_EDU_CONTROL_BAR_ID); + if (priv.base_addr == 0) + { + ret = -ENOMEM; + pcierr("Control BAR is not valid\n"); + goto err; + } + + nxsem_init(&priv.isr_done, 0, 0); + + /* Run Poll Tests */ + + pciinfo("POLL TEST\n"); + + pci_qemu_edu_test_poll(&priv); + + /* Run IRQ Tests */ + + pci_read_config_byte(dev, PCI_INTERRUPT_LINE, &irq); + irq = IRQ0 + irq; + pciinfo("IRQ TEST: Attaching IRQ %u to %p\n", irq, pci_qemu_edu_interrupt); + + irq_attach(irq, pci_qemu_edu_interrupt, &priv); + up_enable_irq(irq); + + pci_qemu_edu_test_intx(&priv); + pci_qemu_edu_test_dma(&priv); + + up_disable_irq(irq); + irq_detach(irq); + + /* Run MSI Tests */ + + /* Uninitialize the driver */ + + nxsem_destroy(&priv.isr_done); + + /* TODO: add pci unmap api */ + +err: + pci_clear_master(dev); + pci_disable_device(dev); + return ret; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: pci_register_qemu_edu_driver + * + * Description: + * Register a pci driver + * + ****************************************************************************/ + +int pci_register_qemu_edu_driver(void) +{ + return pci_register_driver(&g_pci_qemu_edu_pci_drv); +} diff --git a/drivers/virt/Kconfig b/drivers/virt/Kconfig deleted file mode 100644 index 5eea5905087..00000000000 --- a/drivers/virt/Kconfig +++ /dev/null @@ -1,22 +0,0 @@ -# -# For a description of the syntax of this configuration file, -# see the file kconfig-language.txt in the NuttX tools repository. -# -# -# -menuconfig VIRT - bool "Virtualization" - default n - ---help--- - Drivers for virtualized and emulated devices - -if VIRT - -config VIRT_QEMU_EDU - bool "Driver for QEMU EDU test device" - default n - select PCI - ---help--- - Driver for QEMU EDU test device - -endif # VIRT diff --git a/drivers/virt/Make.defs b/drivers/virt/Make.defs deleted file mode 100644 index 6785f7f44de..00000000000 --- a/drivers/virt/Make.defs +++ /dev/null @@ -1,33 +0,0 @@ -############################################################################ -# drivers/virt/Make.defs -# -# Licensed to the Apache Software Foundation (ASF) under one or more -# contributor license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright ownership. The -# ASF licenses this file to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance with the -# License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# -############################################################################ - -ifeq ($(CONFIG_VIRT_QEMU_EDU),y) -CSRCS += qemu_edu.c -endif - -# Include virt device driver build support -# -ifeq ($(CONFIG_VIRT),y) - -DEPPATH += --dep-path virt -VPATH += :virt -CFLAGS += ${INCDIR_PREFIX}$(TOPDIR)$(DELIM)drivers$(DELIM)virt - -endif diff --git a/drivers/virt/qemu_edu.c b/drivers/virt/qemu_edu.c deleted file mode 100644 index 0117d995eb6..00000000000 --- a/drivers/virt/qemu_edu.c +++ /dev/null @@ -1,470 +0,0 @@ -/***************************************************************************** - * drivers/virt/qemu_edu.c - * - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. The - * ASF licenses this file to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance with the - * License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - * - *****************************************************************************/ - -/***************************************************************************** - * Included Files - *****************************************************************************/ - -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -/***************************************************************************** - * Pre-processor Definitions - *****************************************************************************/ - -/* Registers defined for device. Size 4 for < 0x80. Size 8 for >= 0x80. */ - -#define EDU_REG_ID 0x00 /* Identification */ -#define EDU_REG_LIVE 0x04 /* Liveness Check */ -#define EDU_REG_FAC 0x08 /* Factorial Computation */ -#define EDU_REG_STATUS 0x20 /* Status */ -#define EDU_REG_INT_STATUS 0x24 /* Interupt Status */ -#define EDU_REG_INT_RAISE 0x60 /* Raise an interrupt */ -#define EDU_REG_INT_ACK 0x64 /* Acknowledge interrupt */ -#define EDU_REG_DMA_SOURCE 0x80 /* Source address for DMA transfer */ -#define EDU_REG_DMA_DEST 0x88 /* Destination address for DMA transfer */ -#define EDU_REG_DMA_COUNT 0x90 /* Size of area to transfer with DMA */ -#define EDU_REG_DMA_CMD 0x98 /* Control DMA tranfer */ - -#define EDU_CONTROL_BAR_ID 0 -#define EDU_CONTROL_BAR_OFFSET PCI_HEADER_NORM_BAR0 - -/* One 4096 bytes long buffer at offset 0x40000 is available in the - * EDU device - */ - -#define QEMU_EDU_DMABUF_OFFSET 0x40000 - -/***************************************************************************** - * Private Types - *****************************************************************************/ - -struct qemu_edu_priv_s -{ - uintptr_t base_addr; - sem_t isr_done; - uint32_t test_result; -}; - -/***************************************************************************** - * Private Functions Definitions - *****************************************************************************/ - -static void qemu_edu_write_reg32(uintptr_t addr, uint32_t val); - -static uint32_t qemu_edu_read_reg32(uintptr_t addr); - -static void qemu_edu_write_reg64(uintptr_t addr, uint64_t val); - -static void qemu_edu_test_poll(FAR struct pci_dev_s *dev, - uintptr_t base_addr); - -static void qemu_edu_test_intx(FAR struct pci_dev_s *dev, - struct qemu_edu_priv_s *drv_priv); - -static int qemu_edu_interrupt(int irq, void *context, FAR void *arg); - -static int qemu_edu_probe(FAR struct pci_bus_s *bus, - FAR const struct pci_dev_type_s *type, - uint16_t bdf); - -/***************************************************************************** - * Public Data - *****************************************************************************/ - -const struct pci_dev_type_s g_pci_type_qemu_edu = -{ - .vendor = 0x1234, - .device = 0x11e8, - .class_rev = PCI_ID_ANY, - .name = "Qemu PCI EDU device", - .probe = qemu_edu_probe -}; - -/***************************************************************************** - * Private Functions - *****************************************************************************/ - -/***************************************************************************** - * Name: qemu_edu_write_reg32 - * - * Description: - * Provide a write interface for 32bit mapped registers - * - * Input Parameters: - * addr - Register address - * val - Value to assign to register - * - *****************************************************************************/ - -static void qemu_edu_write_reg32(uintptr_t addr, uint32_t val) -{ - *(volatile uint32_t *)addr = val; -} - -/***************************************************************************** - * Name: qemu_edu_read_reg32 - * - * Description: - * Provide a read interface for 32bit mapped registers - * - * Returned Value: - * Register value - * - *****************************************************************************/ - -static uint32_t qemu_edu_read_reg32(uintptr_t addr) -{ - return *(volatile uint32_t *)addr; -} - -/***************************************************************************** - * Name: qemu_edu_write_reg64 - * - * Description: - * Provide a write interface for 64bit mapped registers - * - * Input Parameters: - * addr - Register address - * val - Value to assign to register - * - *****************************************************************************/ - -static void qemu_edu_write_reg64(uintptr_t addr, uint64_t val) -{ - *(volatile uint64_t *)addr = val; -} - -/***************************************************************************** - * Name: qemu_edu_test_poll - * - * Description: - * Performs basic functional test of PCI device and MMIO using polling - * of mapped register interfaces. - * - * Input Parameters: - * bus - An PCI device - * base_addr - Base address of device register space - * - *****************************************************************************/ - -static void qemu_edu_test_poll(FAR struct pci_dev_s *dev, uintptr_t base_addr) -{ - uint32_t test_value; - uint32_t test_read; - - pciinfo("Identification: 0x%08xu\n", - qemu_edu_read_reg32(base_addr + EDU_REG_ID)); - - /* Test Live Check */ - - test_value = 0xdeadbeef; - qemu_edu_write_reg32(base_addr + EDU_REG_LIVE, test_value); - test_read = qemu_edu_read_reg32(base_addr + EDU_REG_LIVE); - pciinfo("Live Check: Wrote: 0x%08x Read: 0x%08x Error Bits 0x%08x\n", - test_value, test_read, test_read ^ ~test_value); - pciinfo("TEST %s\n", ((test_read ^ ~test_value) == 0) ? "PASS" : "FAIL"); - - /* Test Factorial */ - - test_value = 10; - qemu_edu_write_reg32(base_addr + EDU_REG_STATUS, 0); - qemu_edu_write_reg32(base_addr + EDU_REG_FAC, test_value); - while (qemu_edu_read_reg32(base_addr + EDU_REG_STATUS) & 0x01) - { - pciinfo("Waiting to compute factorial..."); - usleep(10000); - } - - test_read = qemu_edu_read_reg32(base_addr + EDU_REG_FAC); - pciinfo("Computed factorial of %d as %d\n", test_value, test_read); - pciinfo("TEST %s\n", (test_read == 3628800) ? "PASS" : "FAIL"); -} - -/***************************************************************************** - * Name: qemu_edu_test_intx - * - * Description: - * Performs basic functional test of PCI device and MMIO using INTx - * - * Input Parameters: - * bus - An PCI device - * drv_priv - Struct containing internal state of driver - * - *****************************************************************************/ - -static void qemu_edu_test_intx(FAR struct pci_dev_s *dev, - FAR struct qemu_edu_priv_s *drv_priv) -{ - uintptr_t base_addr = drv_priv->base_addr; - uint32_t test_value; - - pciinfo("Identification: 0x%08xu\n", - qemu_edu_read_reg32(base_addr + EDU_REG_ID)); - - /* Test Read/Write */ - - test_value = 0xdeadbeef; - pciinfo("Triggering interrupt with value 0x%08x\n", test_value); - qemu_edu_write_reg32(base_addr + EDU_REG_INT_RAISE, test_value); - sem_wait(&drv_priv->isr_done); - pciinfo("TEST %s\n", - (drv_priv->test_result == test_value) ? "PASS" : "FAIL"); - - /* Test Factorial */ - - test_value = 5; - pciinfo("Computing factorial of %d\n", test_value); - qemu_edu_write_reg32(base_addr + EDU_REG_STATUS, 0x80); - qemu_edu_write_reg32(base_addr + EDU_REG_FAC, test_value); - sem_wait(&drv_priv->isr_done); - pciinfo("TEST %s\n", (drv_priv->test_result == 120) ? "PASS" : "FAIL"); - - /* Test ISR Status Cleanup */ - - qemu_edu_write_reg32(base_addr + EDU_REG_INT_RAISE, test_value); - sem_wait(&drv_priv->isr_done); - pciinfo("TEST %s\n", - (drv_priv->test_result == test_value) ? "PASS" : "FAIL"); -} - -/***************************************************************************** - * Name: qemu_edu_test_dma - * - * Description: - * Performs dma functional test of PCI device - * - * Input Parameters: - * bus - An PCI device - * drv_priv - Struct containing internal state of driver - * - *****************************************************************************/ - -static void qemu_edu_test_dma(FAR struct pci_dev_s *dev, - FAR struct qemu_edu_priv_s *drv_priv) -{ - uintptr_t base_addr = drv_priv->base_addr; - FAR void *test_block; - size_t block_size = 2048; - int i; - uint32_t psrand; - uint32_t tx_checksum; - uint32_t rx_checksum; - uint32_t dev_addr = QEMU_EDU_DMABUF_OFFSET; - - pciinfo("Identification: 0x%08xu\n", - qemu_edu_read_reg32(base_addr + EDU_REG_ID)); - - test_block = kmm_malloc(block_size); - for (i = 0; i < block_size; i++) - { - *((uint8_t *)test_block + i) = i & 0xff; - } - - tx_checksum = 0; - psrand = 0x0011223344; - for (i = 0; i < (block_size / 4); i++) - { - /* Fill the memory block with "random" data */ - - psrand ^= psrand << 13; - psrand ^= psrand >> 17; - psrand ^= psrand << 5; - *((uint32_t *)test_block + i) = psrand; - tx_checksum += psrand; - } - - pciinfo("Test block checksum 0x%08x\n", tx_checksum); - qemu_edu_write_reg64(base_addr + EDU_REG_DMA_SOURCE, (uint64_t)test_block); - qemu_edu_write_reg64(base_addr + EDU_REG_DMA_DEST, (uint64_t)dev_addr); - qemu_edu_write_reg64(base_addr + EDU_REG_DMA_COUNT, (uint64_t)block_size); - qemu_edu_write_reg32(base_addr + EDU_REG_STATUS, 0x00); - qemu_edu_write_reg64(base_addr + EDU_REG_DMA_CMD, 0x01 | 0x04); - sem_wait(&drv_priv->isr_done); - - pciinfo("DMA transfer to device complete.\n"); - - qemu_edu_write_reg64(base_addr + EDU_REG_DMA_DEST, (uint64_t)test_block); - qemu_edu_write_reg64(base_addr + EDU_REG_DMA_SOURCE, (uint64_t)dev_addr); - qemu_edu_write_reg64(base_addr + EDU_REG_DMA_COUNT, (uint64_t)block_size); - qemu_edu_write_reg32(base_addr + EDU_REG_STATUS, 0x00); - qemu_edu_write_reg64(base_addr + EDU_REG_DMA_CMD, 0x01 | 0x02 | 0x04); - sem_wait(&drv_priv->isr_done); - - pciinfo("DMA transfer from device complete.\n"); - rx_checksum = 0; - for (i = 0; i < block_size / 4; i++) - { - rx_checksum += *((uint32_t *)test_block + i); - } - - pciinfo("Received block checksum 0x%08x\n", rx_checksum); - pciinfo("TEST %s\n", (rx_checksum == tx_checksum) ? "PASS" : "FAIL"); -} - -/***************************************************************************** - * Name: qemu_edu_interrupt - * - * Description: - * EDU interrupt handler - * - *****************************************************************************/ - -static int qemu_edu_interrupt(int irq, FAR void *context, FAR void *arg) -{ - FAR struct qemu_edu_priv_s *drv_priv = (struct qemu_edu_priv_s *)arg; - uintptr_t base_addr; - uint32_t status; - - base_addr = drv_priv->base_addr; - status = qemu_edu_read_reg32(base_addr + EDU_REG_INT_STATUS); - - qemu_edu_write_reg32(base_addr + EDU_REG_INT_ACK, ~0U); - switch (status) - { - /* Factorial triggered */ - - case 0x1: - { - drv_priv->test_result - = qemu_edu_read_reg32(base_addr + EDU_REG_FAC); - pciinfo("Computed factorial: %d\n", drv_priv->test_result); - break; - } - - /* DMA triggered */ - - case 0x100: - { - pciinfo("DMA transfer complete\n"); - break; - } - - /* Generic write */ - - default: - { - drv_priv->test_result = status; - pciinfo("Received value: 0x%08x\n", status); - break; - } - } - - sem_post(&drv_priv->isr_done); - return OK; -} - -/***************************************************************************** - * Name: qemu_edu_probe - * - * Description: - * Initialize device - * - *****************************************************************************/ - -static int qemu_edu_probe(FAR struct pci_bus_s *bus, - FAR const struct pci_dev_type_s *type, - uint16_t bdf) -{ - struct qemu_edu_priv_s drv_priv; - struct pci_dev_s dev; - uint32_t bar; - uintptr_t bar_addr; - uint8_t irq; - - /* Get dev */ - - dev.bus = bus; - dev.type = type; - dev.bdf = bdf; - - pci_enable_bus_master(&dev); - pciinfo("Enabled bus mastering\n"); - pci_enable_io(&dev, PCI_SYS_RES_MEM); - pciinfo("Enabled memory resources\n"); - - if (pci_bar_valid(&dev, EDU_CONTROL_BAR_ID) != OK) - { - pcierr("Control BAR is not valid\n"); - DEBUGPANIC(); - return -EINVAL; - } - - bar_addr = pci_bar_addr(&dev, EDU_CONTROL_BAR_ID); - bar = bus->ops->pci_cfg_read(&dev, EDU_CONTROL_BAR_OFFSET, 4); - if ((bar & PCI_BAR_LAYOUT_MASK) != PCI_BAR_LAYOUT_MEM) - { - pcierr("Control bar expected to be MMIO\n"); - DEBUGPANIC(); - return -EINVAL; - } - - if (bus->ops->pci_map_bar(bar_addr, - pci_bar_size(&dev, EDU_CONTROL_BAR_ID)) != OK) - { - pcierr("Failed to map address space\n"); - DEBUGPANIC(); - return -EINVAL; - } - - pciinfo("Device Initialized\n"); - - /* Run Poll Tests */ - - qemu_edu_test_poll(&dev, bar_addr); - - /* Run IRQ Tests */ - - drv_priv.base_addr = bar_addr; - sem_init(&drv_priv.isr_done, 0, 0); - sem_setprotocol(&drv_priv.isr_done, SEM_PRIO_NONE); - - irq = IRQ0 + bus->ops->pci_cfg_read(&dev, PCI_HEADER_NORM_INT_LINE, 1); - pciinfo("Attaching IRQ %d to %p\n", irq, qemu_edu_interrupt); - irq_attach(irq, (xcpt_t)qemu_edu_interrupt, (void *)&drv_priv); - up_enable_irq(irq); - - qemu_edu_test_intx(&dev, &drv_priv); - qemu_edu_test_dma(&dev, &drv_priv); - - up_disable_irq(irq); - irq_detach(irq); - sem_destroy(&drv_priv.isr_done); - - /* Run MSI Tests */ - - /* Really should be cleaning up the mapped memory */ - - return OK; -} diff --git a/include/nuttx/pci/pci_qemu_edu.h b/include/nuttx/pci/pci_qemu_edu.h new file mode 100644 index 00000000000..176b26beeb1 --- /dev/null +++ b/include/nuttx/pci/pci_qemu_edu.h @@ -0,0 +1,60 @@ +/**************************************************************************** + * include/nuttx/pci/pci_qemu_edu.h + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +#ifndef __INCLUDE_NUTTX_PCI_PCI_QEMU_EDU_H +#define __INCLUDE_NUTTX_PCI_PCI_QEMU_EDU_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#ifdef CONFIG_PCI_QEMU_EDU + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +#ifdef __cplusplus +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +/**************************************************************************** + * Name: pci_register_qemu_edu_driver + * + * Description: + * register a pci driver + * + ****************************************************************************/ + +int pci_register_qemu_edu_driver(void); + +#undef EXTERN +#ifdef __cplusplus +} +#endif + +#endif /* CONFIG_PCI_QEMU_EDU */ +#endif /* __INCLUDE_NUTTX_PCI_PCI_QEMU_EDU_H */ diff --git a/include/nuttx/pci/pci_qemu_test.h b/include/nuttx/pci/pci_qemu_test.h index 83ea97f98d9..d22e55ae013 100644 --- a/include/nuttx/pci/pci_qemu_test.h +++ b/include/nuttx/pci/pci_qemu_test.h @@ -51,10 +51,6 @@ extern "C" int pci_register_qemu_test_driver(void); #endif -#ifdef CONFIG_VIRT_QEMU_EDU -extern const struct pci_dev_type_s g_pci_type_qemu_edu; -#endif - #undef EXTERN #ifdef __cplusplus }