From 18f97bf2f8f88fe41b77b2ffee8ffb3447ef98fa Mon Sep 17 00:00:00 2001 From: Yang Chung-Fan Date: Mon, 4 May 2020 18:44:11 +0900 Subject: [PATCH] pcie: add framework Squashed commits: 1. x86_64: qemu: implement pci-e functions and enumerate pci-e devices on boot 2. virt: add qemu pci-testdev driver 3. pcie: types array should be null terminated 4. pcie: enable don't take flags, hardcoded enabling flags 5. pcie: checking bar > 4 for 64bit bars are sufficient 6. pcie: qemu: remove not used header 7. pcie: qemu: return -EINVAL if buffer argument is NULL 8. pcie: make pcie enumerate routine as common instead of architecture dependent 9. pcie: cosmetic changes to fit check tools 10. pcie: create MSI/MSIX related marcos and simplify the msi/msix routines --- Kconfig | 35 ++ boards/x86_64/intel64/qemu-intel64/Kconfig | 8 + .../intel64/qemu-intel64/include/board.h | 2 + .../x86_64/intel64/qemu-intel64/src/Makefile | 4 + .../intel64/qemu-intel64/src/qemu_boot.c | 6 + .../intel64/qemu-intel64/src/qemu_pcie.c | 398 ++++++++++++++++ .../qemu-intel64/src/qemu_pcie_readwrite.h | 240 ++++++++++ drivers/Kconfig | 2 + drivers/Makefile | 2 + drivers/pcie/Kconfig | 20 + drivers/pcie/Make.defs | 32 ++ drivers/pcie/pcie_root.c | 446 ++++++++++++++++++ drivers/virt/Kconfig | 22 + drivers/virt/Make.defs | 37 ++ drivers/virt/qemu_pci_test.c | 130 +++++ include/debug.h | 18 + include/nuttx/pcie/pcie.h | 352 ++++++++++++++ include/nuttx/virt/qemu_pci.h | 53 +++ 18 files changed, 1807 insertions(+) create mode 100644 boards/x86_64/intel64/qemu-intel64/src/qemu_pcie.c create mode 100644 boards/x86_64/intel64/qemu-intel64/src/qemu_pcie_readwrite.h create mode 100644 drivers/pcie/Kconfig create mode 100644 drivers/pcie/Make.defs create mode 100644 drivers/pcie/pcie_root.c create mode 100644 drivers/virt/Kconfig create mode 100644 drivers/virt/Make.defs create mode 100644 drivers/virt/qemu_pci_test.c create mode 100644 include/nuttx/pcie/pcie.h create mode 100644 include/nuttx/virt/qemu_pci.h diff --git a/Kconfig b/Kconfig index 603c6f924d..a0d8b941c5 100644 --- a/Kconfig +++ b/Kconfig @@ -2196,6 +2196,41 @@ config DEBUG_IPC_INFO endif # DEBUG_IPC +======= +config DEBUG_PCIE + bool "PCI-E Debug Features" + default n + depends on PCIE + ---help--- + Enable PCIE driver debug features. + + Support for this debug option is architecture-specific and may not + be available for some MCUs. + +if DEBUG_PCIE + +config DEBUG_PCIE_ERROR + bool "PCI-E Error Output" + default n + depends on DEBUG_ERROR + ---help--- + Enable PCI-E driver error output to SYSLOG. + +config DEBUG_PCIE_WARN + bool "PCI-E Warnings Output" + default n + depends on DEBUG_WARN + ---help--- + Enable PCI-E driver warning output to SYSLOG. + +config DEBUG_PCIE_INFO + bool "PCI-E Informational Output" + default n + depends on DEBUG_INFO + ---help--- + Enable PCI-E driver informational output to SYSLOG. + +endif # DEBUG_PCIE endif # DEBUG_FEATURES config ARCH_HAVE_STACKCHECK diff --git a/boards/x86_64/intel64/qemu-intel64/Kconfig b/boards/x86_64/intel64/qemu-intel64/Kconfig index f72f3c094c..595127192b 100644 --- a/boards/x86_64/intel64/qemu-intel64/Kconfig +++ b/boards/x86_64/intel64/qemu-intel64/Kconfig @@ -2,3 +2,11 @@ # For a description of the syntax of this configuration file, # see the file kconfig-language.txt in the NuttX tools repository. # +# +config QEMU_PCIE + bool "Initialize and enumerate PCI-E Bus" + default n + select PCIE + + ---help--- + Enables initialization and scaning of standard x86-64 pcie bus. diff --git a/boards/x86_64/intel64/qemu-intel64/include/board.h b/boards/x86_64/intel64/qemu-intel64/include/board.h index 9cb23e995b..ae7b588407 100644 --- a/boards/x86_64/intel64/qemu-intel64/include/board.h +++ b/boards/x86_64/intel64/qemu-intel64/include/board.h @@ -66,6 +66,8 @@ extern "C" * Public Function Prototypes ****************************************************************************/ +void qemu_pcie_init(void); + #undef EXTERN #if defined(__cplusplus) } diff --git a/boards/x86_64/intel64/qemu-intel64/src/Makefile b/boards/x86_64/intel64/qemu-intel64/src/Makefile index 7bdacd1f3c..9ab32d1766 100644 --- a/boards/x86_64/intel64/qemu-intel64/src/Makefile +++ b/boards/x86_64/intel64/qemu-intel64/src/Makefile @@ -26,4 +26,8 @@ ifeq ($(CONFIG_BOARDCTL),y) CSRCS += qemu_appinit.c endif +ifeq ($(CONFIG_QEMU_PCIE),y) + CSRCS += qemu_pcie.c +endif + include $(TOPDIR)/boards/Board.mk diff --git a/boards/x86_64/intel64/qemu-intel64/src/qemu_boot.c b/boards/x86_64/intel64/qemu-intel64/src/qemu_boot.c index 32cdcb33ec..d4b02b1926 100644 --- a/boards/x86_64/intel64/qemu-intel64/src/qemu_boot.c +++ b/boards/x86_64/intel64/qemu-intel64/src/qemu_boot.c @@ -66,6 +66,12 @@ void x86_64_boardinitialize(void) uart_putreg(CONFIG_16550_UART1_BASE, UART_MCR_OFFSET, UART_MCR_OUT2); #endif +#ifdef CONFIG_QEMU_PCIE + /* Initialization of system */ + + qemu_pcie_init(); +#endif + /* Configure on-board LEDs if LED support has been selected. */ #ifdef CONFIG_ARCH_LEDS diff --git a/boards/x86_64/intel64/qemu-intel64/src/qemu_pcie.c b/boards/x86_64/intel64/qemu-intel64/src/qemu_pcie.c new file mode 100644 index 0000000000..0580593fad --- /dev/null +++ b/boards/x86_64/intel64/qemu-intel64/src/qemu_pcie.c @@ -0,0 +1,398 @@ +/**************************************************************************** + * boards/x86_64/intel64/qemu-intel64/src/qemu_pcie.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. + * + ****************************************************************************/ + +/* The MSI and MSI-X vector setup function are taken from Jailhouse inmate + * library + * + * Jailhouse, a Linux-based partitioning hypervisor + * + * Copyright (c) Siemens AG, 2014 + * + * Authors: + * Jan Kiszka + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + * + * Alternatively, you can use or redistribute this file under the following + * BSD license: + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include + +#include + +#include "qemu_pcie_readwrite.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/**************************************************************************** + * Private Functions Definitions + ****************************************************************************/ + +static int qemu_pci_cfg_write(FAR struct pcie_dev_s *dev, uintptr_t addr, + FAR const void *buffer, unsigned int size); + +static int qemu_pci_cfg_read(FAR struct pcie_dev_s *dev, uintptr_t addr, + FAR void *buffer, unsigned int size); + +static int qemu_pci_map_bar(FAR struct pcie_dev_s *dev, uint32_t addr, + unsigned long length); + +static int qemu_pci_map_bar64(FAR struct pcie_dev_s *dev, uint64_t addr, + unsigned long length); + +static int qemu_pci_msix_register(FAR struct pcie_dev_s *dev, + uint32_t vector, uint32_t index); + +static int qemu_pci_msi_register(FAR struct pcie_dev_s *dev, + uint16_t vector); + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +struct pcie_bus_ops_s qemu_pcie_bus_ops = +{ + .pci_cfg_write = qemu_pci_cfg_write, + .pci_cfg_read = qemu_pci_cfg_read, + .pci_map_bar = qemu_pci_map_bar, + .pci_map_bar64 = qemu_pci_map_bar64, + .pci_msix_register = qemu_pci_msix_register, + .pci_msi_register = qemu_pci_msi_register, +}; + +struct pcie_bus_s qemu_pcie_bus = +{ + .ops = &qemu_pcie_bus_ops, +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: qemu_pci_cfg_write + * + * Description: + * Write 8, 16, 32, 64 bits data to PCI-E configuration space of device + * specified by dev + * + * Input Parameters: + * bdf - Device private data + * buffer - A pointer to the read-only buffer of data to be written + * size - The number of bytes to send from the buffer + * + * Returned Value: + * 0: success, <0: A negated errno + * + ****************************************************************************/ + +static int qemu_pci_cfg_write(FAR struct pcie_dev_s *dev, uintptr_t addr, + FAR const void *buffer, unsigned int size) +{ + if (!buffer) + return -EINVAL; + + switch (size) + { + case 1: + case 2: + case 4: + return __qemu_pci_cfg_write(dev->bdf, addr, buffer, size); + case 8: + return __qemu_pci_cfg_write(dev->bdf, addr, buffer, size); + default: + return -EINVAL; + } +} + +/**************************************************************************** + * Name: qemu_pci_cfg_read + * + * Description: + * Read 8, 16, 32, 64 bits data from PCI-E configuration space of device + * specified by dev + * + * Input Parameters: + * dev - Device private data + * buffer - A pointer to a buffer to receive the data from the device + * size - The requested number of bytes to be read + * + * Returned Value: + * 0: success, <0: A negated errno + * + ****************************************************************************/ + +static int qemu_pci_cfg_read(FAR struct pcie_dev_s *dev, uintptr_t addr, + FAR void *buffer, unsigned int size) +{ + if (!buffer) + return -EINVAL; + + switch (size) + { + case 1: + case 2: + case 4: + return __qemu_pci_cfg_read(dev->bdf, addr, buffer, size); + case 8: + return __qemu_pci_cfg_read64(dev->bdf, addr, buffer, size); + default: + return -EINVAL; + } +} + +/**************************************************************************** + * Name: qemu_pci_map_bar + * + * Description: + * Map address in a 32 bits bar in the memory address space + * + * Input Parameters: + * dev - Device private data + * bar - Bar number + * length - Map length, multiple of PAGE_SIZE + * ret - Bar Content + * + * Returned Value: + * 0: success, <0: A negated errno + * + ****************************************************************************/ + +static int qemu_pci_map_bar(FAR struct pcie_dev_s *dev, uint32_t addr, + unsigned long length) +{ + up_map_region((void *)((uintptr_t)addr), length, + X86_PAGE_WR | X86_PAGE_PRESENT | X86_PAGE_NOCACHE | X86_PAGE_GLOBAL); + + return OK; +} + +/**************************************************************************** + * Name: qemu_pci_map_bar64 + * + * Description: + * Map address in a 64 bits bar in the memory address space + * + * Input Parameters: + * dev - Device private data + * bar - Bar number + * length - Map length, multiple of PAGE_SIZE + * ret - Bar Content + * + * Returned Value: + * 0: success, <0: A negated errno + * + ****************************************************************************/ + +static int qemu_pci_map_bar64(FAR struct pcie_dev_s *dev, uint64_t addr, + unsigned long length) +{ + up_map_region((void *)((uintptr_t)addr), length, + X86_PAGE_WR | X86_PAGE_PRESENT | X86_PAGE_NOCACHE | X86_PAGE_GLOBAL); + + return OK; +} + +/**************************************************************************** + * Name: qemu_pci_msix_register + * + * Description: + * Map a device MSI-X vector to a platform IRQ vector + * + * Input Parameters: + * dev - Device + * vector - IRQ number of the platform + * index - Device MSI-X vector number + * + * Returned Value: + * <0: Mapping failed + * 0: Mapping succeed + * + ****************************************************************************/ + +static int qemu_pci_msix_register(FAR struct pcie_dev_s *dev, + uint32_t vector, uint32_t index) +{ + unsigned int bar; + uint16_t message_control; + uint32_t table_bar_ind; + uint32_t table_addr_32; + uint64_t msix_table_addr = 0; + + int cap = pci_find_cap(dev, PCI_CAP_MSIX); + if (cap < 0) + return -EINVAL; + + __qemu_pci_cfg_read(dev->bdf, cap + PCI_MSIX_MCR, + &message_control, PCI_MSIX_MCR_SIZE); + + /* bounds check */ + + if (index > (message_control & PCI_MSIX_MCR_TBL_MASK)) + return -EINVAL; + + __qemu_pci_cfg_read(dev->bdf, cap + PCI_MSIX_TBL, + &table_bar_ind, PCI_MSIX_TBL_SIZE); + + bar = (table_bar_ind & PCI_MSIX_BIR_MASK); + + if (!pci_get_bar(dev, bar, &table_addr_32)) + { + /* 32 bit bar */ + + msix_table_addr = table_addr_32; + } + else + { + pci_get_bar64(dev, bar, &msix_table_addr); + } + + msix_table_addr &= ~0xf; + msix_table_addr += table_bar_ind & ~PCI_MSIX_BIR_MASK; + + /* enable and mask */ + + message_control |= (PCI_MSIX_MCR_EN | PCI_MSIX_MCR_FMASK); + __qemu_pci_cfg_write(dev->bdf, cap + PCI_MSIX_MCR, + &message_control, PCI_MSIX_MCR_SIZE); + + msix_table_addr += PCI_MSIX_TBL_ENTRY_SIZE * index; + mmio_write32((uint32_t *)(msix_table_addr + PCI_MSIX_TBL_LO_ADDR), + 0xfee00000 | up_apic_cpu_id() << PCI_MSIX_APIC_ID_OFFSET); + mmio_write32((uint32_t *)(msix_table_addr + PCI_MSIX_TBL_HI_ADDR), + 0); + mmio_write32((uint32_t *)(msix_table_addr + PCI_MSIX_TBL_MSG_DATA), + vector); + mmio_write32((uint32_t *)(msix_table_addr + PCI_MSIX_TBL_VEC_CTL), + 0); + + /* enable and unmask */ + + message_control &= ~PCI_MSIX_MCR_FMASK; + + __qemu_pci_cfg_write(dev->bdf, cap + PCI_MSIX_MCR, + &message_control, PCI_MSIX_MCR_SIZE); + + return 0; +} + +/**************************************************************************** + * Name: qemu_pci_msi_register + * + * Description: + * Map device MSI vectors to a platform IRQ vector + * + * Input Parameters: + * dev - Device + * vector - IRQ number of the platform + * + * Returned Value: + * <0: Mapping failed + * 0: Mapping succeed + * + ****************************************************************************/ + +static int qemu_pci_msi_register(FAR struct pcie_dev_s *dev, uint16_t vector) +{ + uint16_t ctl; + uint16_t data; + + int cap = pci_find_cap(dev, PCI_CAP_MSI); + if (cap < 0) + return -1; + + uint32_t dest = 0xfee00000 | (up_apic_cpu_id() << PCI_MSI_APIC_ID_OFFSET); + __qemu_pci_cfg_write(dev->bdf, cap + PCI_MSI_MAR, &dest, PCI_MSI_MAR_SIZE); + + __qemu_pci_cfg_read(dev->bdf, cap + PCI_MSI_MCR, &ctl, PCI_MSI_MCR_SIZE); + if ((ctl & PCI_MSI_MCR_64) == PCI_MSI_MCR_64) + { + uint32_t tmp = 0; + __qemu_pci_cfg_write(dev->bdf, + cap + PCI_MSI_MAR64_HI, &tmp, + PCI_MSI_MAR64_HI_SIZE); + data = cap + PCI_MSI_MDR64; + } + else + { + data = cap + PCI_MSI_MDR; + } + + __qemu_pci_cfg_write(dev->bdf, data, &vector, PCI_MSI_MDR_SIZE); + + __qemu_pci_cfg_write(dev->bdf, cap + PCI_MSI_MCR, &vector, + PCI_MSI_MCR_SIZE); + + uint16_t tmp = PCI_MSI_MCR_EN; + + __qemu_pci_cfg_write(dev->bdf, cap + PCI_MSI_MCR, &tmp, PCI_MSI_MCR_SIZE); + + return OK; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: qemu_pcie_init + * + * Description: + * Initialize the PCI-E bus * + * + ****************************************************************************/ + +void qemu_pcie_init(void) +{ + pcie_initialize(&qemu_pcie_bus); +} diff --git a/boards/x86_64/intel64/qemu-intel64/src/qemu_pcie_readwrite.h b/boards/x86_64/intel64/qemu-intel64/src/qemu_pcie_readwrite.h new file mode 100644 index 0000000000..01fc2712fe --- /dev/null +++ b/boards/x86_64/intel64/qemu-intel64/src/qemu_pcie_readwrite.h @@ -0,0 +1,240 @@ +/**************************************************************************** + * boards/x86_64/intel64/qemu-intel64/src/qemu_pcie_readwrite.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. + * + ****************************************************************************/ + +/* The PCI-E Definitions and part of the access routines are taken from + * Jailhouse inmate library + * + * Jailhouse, a Linux-based partitioning hypervisor + * + * Copyright (c) Siemens AG, 2014 + * + * Authors: + * Jan Kiszka + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + * + * Alternatively, you can use or redistribute this file under the following + * BSD license: + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __INCLUDE_NUTTX_PCIE_PCIE_READWRITE_H +#define __INCLUDE_NUTTX_PCIE_PCIE_READWRITE_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include + +#include + +#include +#include + +#include "up_arch.h" +#include "up_internal.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define PCI_REG_ADDR_PORT 0xcf8 +#define PCI_REG_DATA_PORT 0xcfc + +#define PCI_CONE (1 << 31) + +/**************************************************************************** + * Name: __qemu_pci_cfg_write + * + * Description: + * Write 8, 16, 32 bits data to PCI-E configuration space of device + * specified by dev + * + * Input Parameters: + * bfd - Device private data + * buffer - A pointer to the read-only buffer of data to be written + * size - The number of bytes to send from the buffer + * + * Returned Value: + * 0: success, <0: A negated errno + * + ****************************************************************************/ + +static inline int __qemu_pci_cfg_write(uint16_t bfd, uintptr_t addr, + FAR const void *buffer, + unsigned int size) +{ + if (!buffer) + return -EINVAL; + + outl(PCI_CONE | ((uint32_t)bfd << 8) | (addr & 0xfc), PCI_REG_ADDR_PORT); + + switch (size) + { + case 1: + outb(*(uint8_t *)(buffer), PCI_REG_DATA_PORT + (addr & 0x3)); + break; + case 2: + outw(*(uint16_t *)(buffer), PCI_REG_DATA_PORT + (addr & 0x3)); + break; + case 4: + outl(*(uint32_t *)(buffer), PCI_REG_DATA_PORT); + break; + default: + return -EINVAL; + } + return OK; +} + +/**************************************************************************** + * Name: __qemu_pci_cfg_write64 + * + * Description: + * Write 64 bits data to PCI-E configuration space of device + * specified by dev + * + * Input Parameters: + * bfd - Device private data + * buffer - A pointer to the read-only buffer of data to be written + * size - The number of bytes to send from the buffer + * + * Returned Value: + * 0: success, <0: A negated errno + * + ****************************************************************************/ + +static inline int __qemu_pci_cfg_write64(uint16_t bfd, uintptr_t addr, + FAR const void *buffer, + unsigned int size) +{ + int ret; + + if (!buffer) + return -EINVAL; + + ret = __qemu_pci_cfg_write(bfd, addr + 4, buffer + 4, 4); + ret |= __qemu_pci_cfg_write(bfd, addr, buffer, 4); + + return ret; +} + +/**************************************************************************** + * Name: __qemu_pci_cfg_read + * + * Description: + * Read 8, 16, 32 bits data from PCI-E configuration space of device + * specified by dev + * + * Input Parameters: + * dev - Device private data + * buffer - A pointer to a buffer to receive the data from the device + * size - The requested number of bytes to be read + * + * Returned Value: + * 0: success, <0: A negated errno + * + ****************************************************************************/ + +static inline int __qemu_pci_cfg_read(uint16_t bfd, uintptr_t addr, + FAR void *buffer, unsigned int size) +{ + if (!buffer) + return -EINVAL; + + outl(PCI_CONE | ((uint32_t)bfd << 8) | (addr & 0xfc), PCI_REG_ADDR_PORT); + + switch (size) + { + case 1: + *(uint8_t *)(buffer) = inb(PCI_REG_DATA_PORT + (addr & 0x3)); + break; + case 2: + *(uint16_t *)(buffer) = inw(PCI_REG_DATA_PORT + (addr & 0x3)); + break; + case 4: + *(uint32_t *)(buffer) = inl(PCI_REG_DATA_PORT); + break; + default: + return -EINVAL; + } + + return OK; +} + +/**************************************************************************** + * Name: __qemu_pci_cfg_read + * + * Description: + * Read 64 bits data from PCI-E configuration space of device + * specified by dev + * + * Input Parameters: + * dev - Device private data + * buffer - A pointer to a buffer to receive the data from the device + * size - The requested number of bytes to be read + * + * Returned Value: + * 0: success, <0: A negated errno + * + ****************************************************************************/ + +static inline int __qemu_pci_cfg_read64(uint16_t bfd, + uintptr_t addr, + FAR void *buffer, + unsigned int size) +{ + int ret; + + if (!buffer) + return -EINVAL; + + ret = __qemu_pci_cfg_read(bfd, addr + 4, buffer + 4, 4); + ret |= __qemu_pci_cfg_read(bfd, addr, buffer, 4); + + return ret; +} + +#endif /* __INCLUDE_NUTTX_PCIE_PCIE_READWRITE_H */ diff --git a/drivers/Kconfig b/drivers/Kconfig index 328b726641..da93ca190f 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -59,3 +59,5 @@ source "drivers/usrsock/Kconfig" source "drivers/dma/Kconfig" source "drivers/devicetree/Kconfig" source "drivers/reset/Kconfig" +source "drivers/pcie/Kconfig" +source "drivers/virt/Kconfig" diff --git a/drivers/Makefile b/drivers/Makefile index 197ceb3215..f5584c5370 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -77,6 +77,8 @@ include rc/Make.defs include segger/Make.defs include usrsock/Make.defs include reset/Make.defs +include pcie/Make.defs +include virt/Make.defs ifeq ($(CONFIG_SPECIFIC_DRIVERS),y) -include platform/Make.defs diff --git a/drivers/pcie/Kconfig b/drivers/pcie/Kconfig new file mode 100644 index 0000000000..7ac9db568b --- /dev/null +++ b/drivers/pcie/Kconfig @@ -0,0 +1,20 @@ +# +# For a description of the syntax of this configuration file, +# see the file kconfig-language.txt in the NuttX tools repository. +# + +menuconfig PCIE + bool "Support for PCI-E Bus" + default n + ---help--- + Enables support for the PCI-E bus. + Backend bust be provided by per-arch or per-board implementation.. + +if PCIE +config PCIE_MAX_BDF + hex "Maximum bdf to scan on PCI-E bus" + default 0x10000 + ---help--- + The maximum bdf number to be scaned on PCI-E bus + +endif diff --git a/drivers/pcie/Make.defs b/drivers/pcie/Make.defs new file mode 100644 index 0000000000..68efee8123 --- /dev/null +++ b/drivers/pcie/Make.defs @@ -0,0 +1,32 @@ +############################################################################ +# drivers/pcie/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. +# +############################################################################ + +# Don't build anything if there is no CAN support + +ifeq ($(CONFIG_PCIE),y) + +CSRCS += pcie_root.c + +# Include PCIE device driver build support + +DEPPATH += --dep-path pcie +VPATH += :pcie +CFLAGS += ${shell $(INCDIR) $(INCDIROPT) "$(CC)" $(TOPDIR)$(DELIM)drivers$(DELIM)pcie} +endif diff --git a/drivers/pcie/pcie_root.c b/drivers/pcie/pcie_root.c new file mode 100644 index 0000000000..b85c5862fa --- /dev/null +++ b/drivers/pcie/pcie_root.c @@ -0,0 +1,446 @@ +/**************************************************************************** + * nuttx/drivers/pcie/pcie_root.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 + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +struct pcie_dev_type_s *pci_device_types[] = +{ +#ifdef CONFIG_VIRT_QEMU_PCI_TEST + &pcie_type_qemu_pci_test, +#endif /* CONFIG_VIRT_QEMU_PCI_TEST */ + NULL, +}; + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: pci_enumerate + * + * Description: + * Scan the PCI bus and enumerate the devices. + * Initialize any recognized devices, given in types. + * + * Input Parameters: + * bus - PCI-E bus structure + * type - List of pointers to devices types recognized, NULL terminated + * + * Returned Value: + * 0: success, <0: A negated errno + * + ****************************************************************************/ + +int pci_enumerate(FAR struct pcie_bus_s *bus, + FAR struct pcie_dev_type_s **types) +{ + unsigned int bdf; + uint16_t vid; + uint16_t id; + uint16_t rev; + struct pcie_dev_s tmp_dev; + struct pcie_dev_type_s tmp_type = + { + .name = "Unknown", + .vendor = PCI_ID_ANY, + .device = PCI_ID_ANY, + .class_rev = PCI_ID_ANY, + .probe = NULL, + }; + + if (!bus) + return -EINVAL; + if (!types) + return -EINVAL; + + for (bdf = 0; bdf < CONFIG_PCIE_MAX_BDF; bdf++) + { + tmp_dev.bus = bus; + tmp_dev.type = &tmp_type; + tmp_dev.bdf = bdf; + + bus->ops->pci_cfg_read(&tmp_dev, PCI_CFG_VENDOR_ID, &vid, 2); + bus->ops->pci_cfg_read(&tmp_dev, PCI_CFG_DEVICE_ID, &id, 2); + bus->ops->pci_cfg_read(&tmp_dev, PCI_CFG_REVERSION, &rev, 2); + + if (vid == PCI_ID_ANY) + continue; + + pciinfo("[%02x:%02x.%x] Found %04x:%04x, class/reversion %08x\n", + bdf >> 8, (bdf >> 3) & 0x1f, bdf & 0x3, + vid, id, rev); + + for (int i = 0; types[i] != NULL; i++) + { + if (types[i]->vendor == PCI_ID_ANY || + types[i]->vendor == vid) + { + if (types[i]->device == PCI_ID_ANY || + types[i]->device == id) + { + if (types[i]->class_rev == PCI_ID_ANY || + types[i]->class_rev == rev) + { + if (types[i]->probe) + { + pciinfo("[%02x:%02x.%x] %s\n", + bdf >> 8, (bdf >> 3) & 0x1f, bdf & 0x3, + types[i]->name); + types[i]->probe(bus, types[i], bdf); + } + else + { + pcierr("[%02x:%02x.%x] Error: Invalid \ + device probe function\n", + bdf >> 8, (bdf >> 3) & 0x1f, bdf & 0x3); + } + break; + } + } + } + } + } + + return OK; +} + +/**************************************************************************** + * Name: pcie_initialize + * + * Description: + * Initialize the PCI-E bus and enumerate the devices with give devices + * type array + * + * Input Parameters: + * bus - An PCIE bus + * types - A array of PCIE device types + * num - Number of device types + * + * Returned Value: + * OK if the driver was successfully register; A negated errno value is + * returned on any failure. + * + ****************************************************************************/ + +int pcie_initialize(FAR struct pcie_bus_s *bus) +{ + return pci_enumerate(bus, pci_device_types); +} + +/**************************************************************************** + * Name: pci_enable_device + * + * Description: + * Enable device with MMIO + * + * Input Parameters: + * dev - device + * + * Return value: + * -EINVAL: error + * OK: OK + * + ****************************************************************************/ + +int pci_enable_device(FAR struct pcie_dev_s *dev) +{ + uint16_t old_cmd; + uint16_t cmd; + + dev->bus->ops->pci_cfg_read(dev, PCI_CFG_COMMAND, &old_cmd, 2); + + cmd = old_cmd | (PCI_CMD_MASTER | PCI_CMD_MEM); + + dev->bus->ops->pci_cfg_write(dev, PCI_CFG_COMMAND, &cmd, 2); + + pciinfo("%02x:%02x.%x, CMD: %x -> %x\n", + dev->bdf >> 8, (dev->bdf >> 3) & 0x1f, dev->bdf & 0x3, + old_cmd, cmd); + + return OK; +} + +/**************************************************************************** + * Name: pci_find_cap + * + * Description: + * Search through the PCI-e device capability list to find given capability. + * + * Input Parameters: + * dev - Device + * cap - Bitmask of capability + * + * Returned Value: + * -1: Capability not supported + * other: the offset in PCI configuration space to the capability structure + * + ****************************************************************************/ + +int pci_find_cap(FAR struct pcie_dev_s *dev, uint16_t cap) +{ + uint8_t pos = PCI_CFG_CAP_PTR - 1; + uint16_t status; + uint8_t rcap; + + dev->bus->ops->pci_cfg_read(dev, PCI_CFG_STATUS, &status, 2); + + if (!(status & PCI_STS_CAPS)) + return -EINVAL; + + while (1) + { + dev->bus->ops->pci_cfg_read(dev, pos + 1, &pos, 1); + if (pos == 0) + return -EINVAL; + + dev->bus->ops->pci_cfg_read(dev, pos, &rcap, 1); + + if (rcap == cap) + return pos; + } +} + +/**************************************************************************** + * Name: pci_get_bar + * + * Description: + * Get a 32 bits bar + * + * Input Parameters: + * dev - Device private data + * bar - Bar number + * ret - Bar Content + * + * Returned Value: + * 0: success, <0: A negated errno + * + ****************************************************************************/ + +int pci_get_bar(FAR struct pcie_dev_s *dev, uint32_t bar, + uint32_t *ret) +{ + if (bar > 5) + return -EINVAL; + + dev->bus->ops->pci_cfg_read(dev, PCI_CFG_BAR + bar * 4, ret, 4); + + return OK; +} + +/**************************************************************************** + * Name: pci_get_bar64 + * + * Description: + * Get a 64 bits bar + * + * Input Parameters: + * dev - Device private data + * bar - Bar number + * ret - Bar Content + * + * Returned Value: + * 0: success, <0: A negated errno + * + ****************************************************************************/ + +int pci_get_bar64(FAR struct pcie_dev_s *dev, uint32_t bar, + uint64_t *ret) +{ + if (bar > 4 || ((bar % 2) != 0)) + return -EINVAL; + + uint32_t barmem1; + uint32_t barmem2; + + dev->bus->ops->pci_cfg_read(dev, PCI_CFG_BAR + bar * 4, &barmem1, 4); + dev->bus->ops->pci_cfg_read(dev, PCI_CFG_BAR + bar * 4 + 4, &barmem2, 4); + + *ret = ((uint64_t)barmem2 << 32) | barmem1; + + return OK; +} + +/**************************************************************************** + * Name: pci_set_bar + * + * Description: + * Set a 32 bits bar + * + * Input Parameters: + * dev - Device private data + * bar - Bar number + * val - Bar Content + * + * Returned Value: + * 0: success, <0: A negated errno + * + ****************************************************************************/ + +int pci_set_bar(FAR struct pcie_dev_s *dev, uint32_t bar, + uint32_t val) +{ + if (bar > 5) + return -EINVAL; + + dev->bus->ops->pci_cfg_write(dev, PCI_CFG_BAR + bar * 4, &val, 4); + + return OK; +} + +/**************************************************************************** + * Name: pci_set_bar64 + * + * Description: + * Set a 64 bits bar + * + * Input Parameters: + * dev - Device private data + * bar - Bar number + * val - Bar Content + * + * Returned Value: + * 0: success, <0: A negated errno + * + ****************************************************************************/ + +int pci_set_bar64(FAR struct pcie_dev_s *dev, uint32_t bar, + uint64_t val) +{ + if (bar > 4 || ((bar % 2) != 0)) + return -EINVAL; + + uint32_t barmem1 = (uint32_t)val; + uint32_t barmem2 = (uint32_t)(val >> 32); + + dev->bus->ops->pci_cfg_write(dev, PCI_CFG_BAR + bar * 4, &barmem1, 4); + dev->bus->ops->pci_cfg_write(dev, PCI_CFG_BAR + bar * 4 + 4, &barmem2, 4); + + return OK; +} + +/**************************************************************************** + * Name: pci_map_bar + * + * Description: + * Map address in a 32 bits bar in the flat memory address space + * + * Input Parameters: + * dev - Device private data + * bar - Bar number + * length - Map length, multiple of PAGE_SIZE + * ret - Bar Content if not NULL + * + * Returned Value: + * 0: success, <0: A negated errno + * + ****************************************************************************/ + +int pci_map_bar(FAR struct pcie_dev_s *dev, uint32_t bar, + unsigned long length, uint32_t *ret) +{ + if (bar > 5) + return -EINVAL; + + uint32_t barmem; + + dev->bus->ops->pci_cfg_read(dev, PCI_CFG_BAR + bar * 4, &barmem, 4); + + if (((bar % 2) == 0 && + (barmem & PCI_BAR_64BIT) == PCI_BAR_64BIT) || + (barmem & PCI_BAR_IO) == PCI_BAR_IO) + return -EINVAL; + + if (!dev->bus->ops->pci_map_bar) + return -EINVAL; + + dev->bus->ops->pci_map_bar(dev, barmem, length); + + if (ret) + *ret = barmem; + + return OK; +} + +/**************************************************************************** + * Name: pci_map_bar64 + * + * Description: + * Map address in a 64 bits bar in the flat memory address space + * + * Input Parameters: + * dev - Device private data + * bar - Bar number + * length - Map length, multiple of PAGE_SIZE + * ret - Bar Content if not NULL + * + * Returned Value: + * 0: success, <0: A negated errno + * + ****************************************************************************/ + +int pci_map_bar64(FAR struct pcie_dev_s *dev, uint32_t bar, + unsigned long length, uint64_t *ret) +{ + if (bar > 4 || ((bar % 2) != 0)) + return -EINVAL; + + uint32_t barmem1; + uint32_t barmem2; + uint64_t barmem; + + dev->bus->ops->pci_cfg_read(dev, PCI_CFG_BAR + bar * 4, &barmem1, 4); + + if ((barmem1 & PCI_BAR_64BIT) != PCI_BAR_64BIT || + (barmem1 & PCI_BAR_IO) == PCI_BAR_IO) + return -EINVAL; + + dev->bus->ops->pci_cfg_read(dev, PCI_CFG_BAR + bar * 4 + 4, &barmem2, 4); + + barmem = ((uint64_t)barmem2 << 32) | barmem1; + + if (!dev->bus->ops->pci_map_bar64) + return -EINVAL; + + dev->bus->ops->pci_map_bar64(dev, barmem, length); + + if (ret) + *ret = barmem; + + return OK; +} diff --git a/drivers/virt/Kconfig b/drivers/virt/Kconfig new file mode 100644 index 0000000000..bf8fb8591c --- /dev/null +++ b/drivers/virt/Kconfig @@ -0,0 +1,22 @@ +# +# 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_PCI_TEST + bool "Driver for QEMU PCI test device" + default n + select PCIE + ---help--- + Driver for QEMU PCI test device + +endif # VIRT diff --git a/drivers/virt/Make.defs b/drivers/virt/Make.defs new file mode 100644 index 0000000000..8ccfbe06c5 --- /dev/null +++ b/drivers/virt/Make.defs @@ -0,0 +1,37 @@ +############################################################################ +# drivers/pcie/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. +# +############################################################################ + +# Don't build anything if there is no CAN support + +ifeq ($(CONFIG_VIRT_QEMU_PCI_TEST),y) + +CSRCS += qemu_pci_test.c + +endif + +# Include virt device driver build support +# +ifeq ($(CONFIG_VIRT),y) + +DEPPATH += --dep-path virt +VPATH += :virt +CFLAGS += ${shell $(INCDIR) $(INCDIROPT) "$(CC)" $(TOPDIR)$(DELIM)drivers$(DELIM)virt} + +endif diff --git a/drivers/virt/qemu_pci_test.c b/drivers/virt/qemu_pci_test.c new file mode 100644 index 0000000000..d0df753121 --- /dev/null +++ b/drivers/virt/qemu_pci_test.c @@ -0,0 +1,130 @@ +/***************************************************************************** + * drivers/virt/qemu_pci_test.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 + +/***************************************************************************** + * Pre-processor Definitions + *****************************************************************************/ + +/***************************************************************************** + * Private Types + *****************************************************************************/ + +struct pci_test_dev_hdr_s +{ + volatile uint8_t test; /* write-only, starts a given test number */ + volatile uint8_t width_type; /* read-only, type and width of access for a given test. + * 1,2,4 for byte,word or long write. + * any other value if test not supported on this BAR */ + volatile uint8_t pad0[2]; + volatile uint32_t offset; /* read-only, offset in this BAR for a given test */ + volatile uint32_t data; /* read-only, data to use for a given test */ + volatile uint32_t count; /* for debugging. number of writes detected. */ + volatile uint8_t name[]; /* for debugging. 0-terminated ASCII string. */ +}; + +/***************************************************************************** + * Public Functions + *****************************************************************************/ + +/***************************************************************************** + * Name: qemu_pci_test_probe + * + * Description: + * Initialize device + *****************************************************************************/ + +int qemu_pci_test_probe(FAR struct pcie_bus_s *bus, + FAR struct pcie_dev_type_s *type, uint16_t bdf) +{ + uint32_t bar[2]; + struct pcie_dev_s dev = + { + .bus = bus, + .type = type, + .bdf = bdf, + }; + + pci_enable_device(&dev); + + for (int ii = 0; ii < 2; ii++) + { + pci_get_bar(&dev, ii, bar + ii); + + if ((bar[ii] & PCI_BAR_IO) != PCI_BAR_IO) + { + pciinfo("Mapping BAR%d: %x\n", ii, bar[ii]); + + pci_map_bar(&dev, ii, 0x1000, NULL); + + struct pci_test_dev_hdr_s *ptr = + (struct pci_test_dev_hdr_s *)(uintptr_t)bar[ii]; + + int i = 0; + while (1) + { + ptr->test = i; + + if (ptr->width_type != 1 && + ptr->width_type != 2 && + ptr->width_type != 4) + break; + + pciinfo("Test[%d] Size:%d %s\n", + i, ptr->width_type, + ptr->name); + + i++; + } + } + } + + return OK; +} + +/***************************************************************************** + * Public Data + *****************************************************************************/ + +struct pcie_dev_type_s pcie_type_qemu_pci_test = +{ + .vendor = 0x1b36, + .device = 0x0005, + .class_rev = PCI_ID_ANY, + .name = "Qemu PCI test device", + .probe = qemu_pci_test_probe +}; diff --git a/include/debug.h b/include/debug.h index e6b9163cd7..955ddf6e2c 100644 --- a/include/debug.h +++ b/include/debug.h @@ -920,6 +920,24 @@ # define ipcinfo _none #endif +#ifdef CONFIG_DEBUG_PCIE_ERROR +# define pcierr _err +#else +# define pcierr _none +#endif + +#ifdef CONFIG_DEBUG_PCIE_WARN +# define pciwarn _warn +#else +# define pciwarn _none +#endif + +#ifdef CONFIG_DEBUG_PCIE_INFO +# define pciinfo _info +#else +# define pciinfo _none +#endif + /* Buffer dumping macros do not depend on varargs */ #ifdef CONFIG_DEBUG_ERROR diff --git a/include/nuttx/pcie/pcie.h b/include/nuttx/pcie/pcie.h new file mode 100644 index 0000000000..fffc791a2d --- /dev/null +++ b/include/nuttx/pcie/pcie.h @@ -0,0 +1,352 @@ +/**************************************************************************** + * include/nuttx/pcie/pcie.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_PCIE_PCIE_H +#define __INCLUDE_NUTTX_PCIE_PCIE_H + +#ifdef CONFIG_PCIE + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include + +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define PCI_CFG_VENDOR_ID 0x000 +#define PCI_CFG_DEVICE_ID 0x002 +#define PCI_CFG_COMMAND 0x004 +# define PCI_CMD_IO (1 << 0) +# define PCI_CMD_MEM (1 << 1) +# define PCI_CMD_MASTER (1 << 2) +# define PCI_CMD_INTX_OFF (1 << 10) +#define PCI_CFG_STATUS 0x006 +# define PCI_STS_INT (1 << 3) +# define PCI_STS_CAPS (1 << 4) +#define PCI_CFG_REVERSION 0x008 +#define PCI_CFG_BAR 0x010 +# define PCI_BAR_IO 0x1 +# define PCI_BAR_1M 0x2 +# define PCI_BAR_64BIT 0x4 +#define PCI_CFG_CAP_PTR 0x034 + +#define PCI_ID_ANY 0xffff +#define PCI_DEV_CLASS_OTHER 0xff + +#define PCI_CAP_PM 0x01 + +#define PCI_CAP_MSI 0x05 +# define PCI_MSI_MCR 0x02 +# define PCI_MSI_MCR_SIZE 2 +# define PCI_MSI_MCR_EN (1 << 0) +# define PCI_MSI_MCR_64 (1 << 7) +# define PCI_MSI_MAR 0x04 +# define PCI_MSI_MAR_SIZE 4 +# define PCI_MSI_MDR 0x08 +# define PCI_MSI_MDR_SIZE 2 +# define PCI_MSI_MAR64_HI 0x08 +# define PCI_MSI_MAR64_HI_SIZE 4 +# define PCI_MSI_MDR64 0x0c +# define PCI_MSI_MDR64_SIZE 2 +# define PCI_MSI_APIC_ID_OFFSET 0xc + +#define PCI_CAP_MSIX 0x11 +# define PCI_MSIX_MCR 0x02 +# define PCI_MSIX_MCR_SIZE 2 +# define PCI_MSIX_MCR_EN (1 << 15) +# define PCI_MSIX_MCR_FMASK 0x4000 +# define PCI_MSIX_MCR_TBL_MASK 0x03ff +# define PCI_MSIX_TBL 0x04 +# define PCI_MSIX_TBL_SIZE 4 +# define PCI_MSIX_PBA 0x08 +# define PCI_MSIX_PBA_SIZE 4 +# define PCI_MSIX_BIR_MASK 0x07 +# define PCI_MSIX_TBL_ENTRY_SIZE 0x10 +# define PCI_MSIX_TBL_LO_ADDR 0x0 +# define PCI_MSIX_TBL_HI_ADDR 0x4 +# define PCI_MSIX_TBL_MSG_DATA 0x8 +# define PCI_MSIX_TBL_VEC_CTL 0xc +# define PCI_MSIX_APIC_ID_OFFSET 0xc + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* The PCIE driver interface */ + +struct pcie_bus_s; +struct pcie_dev_type_s; +struct pcie_dev_s; + +/* Bus related operations */ + +struct pcie_bus_ops_s +{ + CODE int (*pcie_enumerate)(FAR struct pcie_bus_s *bus, + FAR struct pcie_dev_type_s **types); + + CODE int (*pci_cfg_write)(FAR struct pcie_dev_s *dev, uintptr_t addr, + FAR const void *buffer, unsigned int size); + + CODE int (*pci_cfg_read)(FAR struct pcie_dev_s *dev, uintptr_t addr, + FAR void *buffer, unsigned int size); + + CODE int (*pci_map_bar)(FAR struct pcie_dev_s *dev, uint32_t addr, + unsigned long length); + + CODE int (*pci_map_bar64)(FAR struct pcie_dev_s *dev, uint64_t addr, + unsigned long length); + + CODE int (*pci_msi_register)(FAR struct pcie_dev_s *dev, + uint16_t vector); + + CODE int (*pci_msix_register)(FAR struct pcie_dev_s *dev, + uint32_t vector, uint32_t index); +}; + +/* PCIE bus private data. */ + +struct pcie_bus_s +{ + FAR const struct pcie_bus_ops_s *ops; /* operations */ +}; + +/* PCIE device type, defines by vendor ID and device ID */ + +struct pcie_dev_type_s +{ + uint16_t vendor; /* Device vendor ID */ + uint16_t device; /* Device ID */ + uint32_t class_rev; /* Device reversion */ + const char *name; /* Human readable name */ + + /* Call back function when a device is probed */ + + CODE int (*probe)(FAR struct pcie_bus_s *bus, + FAR struct pcie_dev_type_s *type, uint16_t bdf); +}; + +/* PCIE device private data. */ + +struct pcie_dev_s +{ + FAR struct pcie_bus_s *bus; + FAR struct pcie_dev_type_s *type; + uint16_t bdf; +}; + +/**************************************************************************** + * Public Functions Prototypes + ****************************************************************************/ + +#undef EXTERN +#if defined(__cplusplus) +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +/**************************************************************************** + * Name: pcie_initialize + * + * Description: + * Initialize the PCI-E bus and enumerate the devices with give devices + * type array + * + * Input Parameters: + * bus - An PCIE bus + * types - A array of PCIE device types + * num - Number of device types + * + * Returned Value: + * OK if the driver was successfully register; A negated errno value is + * returned on any failure. + * + ****************************************************************************/ + +int pcie_initialize(FAR struct pcie_bus_s *bus); + +/**************************************************************************** + * Name: pci_enable_device + * + * Description: + * Enable device with MMIO + * + * Input Parameters: + * dev - device + * + * Return value: + * -EINVAL: error + * OK: OK + * + ****************************************************************************/ + +int pci_enable_device(FAR struct pcie_dev_s *dev); + +/**************************************************************************** + * Name: pci_find_cap + * + * Description: + * Search through the PCI-e device capability list to find given capability. + * + * Input Parameters: + * dev - Device + * cap - Bitmask of capability + * + * Returned Value: + * -1: Capability not supported + * other: the offset in PCI configuration space to the capability structure + * + ****************************************************************************/ + +int pci_find_cap(FAR struct pcie_dev_s *dev, uint16_t cap); + +/**************************************************************************** + * Name: pci_map_bar + * + * Description: + * Map address in a 32 bits bar in the flat memory address space + * + * Input Parameters: + * dev - Device private data + * bar - Bar number + * length - Map length, multiple of PAGE_SIZE + * ret - Bar Contentif not NULL + * + * Returned Value: + * 0: success, <0: A negated errno + * + ****************************************************************************/ + +int pci_map_bar(FAR struct pcie_dev_s *dev, uint32_t bar, + unsigned long length, uint32_t *ret); + +/**************************************************************************** + * Name: pci_map_bar64 + * + * Description: + * Map address in a 64 bits bar in the flat memory address space + * + * Input Parameters: + * dev - Device private data + * bar - Bar number + * length - Map length, multiple of PAGE_SIZE + * ret - Bar Content if not NULL + * + * Returned Value: + * 0: success, <0: A negated errno + * + ****************************************************************************/ + +int pci_map_bar64(FAR struct pcie_dev_s *dev, uint32_t bar, + unsigned long length, uint64_t *ret); + +/**************************************************************************** + * Name: pci_get_bar + * + * Description: + * Get a 32 bits bar + * + * Input Parameters: + * dev - Device private data + * bar - Bar number + * ret - Bar Content + * + * Returned Value: + * 0: success, <0: A negated errno + * + ****************************************************************************/ + +int pci_get_bar(FAR struct pcie_dev_s *dev, uint32_t bar, + uint32_t *ret); + +/**************************************************************************** + * Name: pci_get_bar64 + * + * Description: + * Get a 64 bits bar + * + * Input Parameters: + * dev - Device private data + * bar - Bar number + * ret - Bar Content + * + * Returned Value: + * 0: success, <0: A negated errno + * + ****************************************************************************/ + +int pci_get_bar64(FAR struct pcie_dev_s *dev, uint32_t bar, + uint64_t *ret); + +/**************************************************************************** + * Name: pci_set_bar + * + * Description: + * Set a 32 bits bar + * + * Input Parameters: + * dev - Device private data + * bar - Bar number + * val - Bar Content + * + * Returned Value: + * 0: success, <0: A negated errno + * + ****************************************************************************/ + +int pci_set_bar(FAR struct pcie_dev_s *dev, uint32_t bar, + uint32_t val); + +/**************************************************************************** + * Name: pci_set_bar64 + * + * Description: + * Set a 64 bits bar + * + * Input Parameters: + * dev - Device private data + * bar - Bar number + * val - Bar Content + * + * Returned Value: + * 0: success, <0: A negated errno + * + ****************************************************************************/ + +int pci_set_bar64(FAR struct pcie_dev_s *dev, uint32_t bar, + uint64_t val); + +#undef EXTERN +#if defined(__cplusplus) +} +#endif +#endif +#endif /* __INCLUDE_NUTTX_I2C_I2C_MASTER_H */ diff --git a/include/nuttx/virt/qemu_pci.h b/include/nuttx/virt/qemu_pci.h new file mode 100644 index 0000000000..f8e38f9241 --- /dev/null +++ b/include/nuttx/virt/qemu_pci.h @@ -0,0 +1,53 @@ +/**************************************************************************** + * include/nuttx/serial/uart_mcs99xx.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_VIRT_QEMU_PCI_TEST_H +#define __INCLUDE_NUTTX_VIRT_QEMU_PCI_TEST_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +#ifdef __cplusplus +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +#ifdef CONFIG_VIRT_QEMU_PCI_TEST +extern struct pcie_dev_type_s pcie_type_qemu_pci_test; +#endif /* CONFIG_VIRT_QEMU_PCI_TEST */ + +#undef EXTERN +#ifdef __cplusplus +} +#endif + +#endif /* __INCLUDE_NUTTX_VIRT_QEMU_PCI_TEST_H */