Various fixes for PCI work

Squashed commits:

1. Porting prior PCI work in place of jailhouse code

At this point the PCI enumeration works for x86_64 including over
pci-pci bridges.

Running QEMU with this configuration we see the bridge and the
device on the bridge.  It also detected the qemu test device

qemu-system-x86_64 \
  -cpu host,+pcid,+x2apic,+tsc-deadline,+xsave,+rdrand \
  --enable-kvm -smp 1 -m 2G -cdrom boot.iso --nographic -no-reboot \
  -device pci-testdev \
  -device pci-bridge,id=bridge0,chassis_nr=2 \
  -device e1000,bus=bridge0,addr=0x3

qemu_pci_init: Initializing PCI Bus
pci_probe_device: [00:00.0] Found 8086:1237, class/revision 06000002
pci_probe_device: [00:01.1] Found 8086:7010, class/revision 01018000
pci_probe_device: [00:01.2] Found ffff:ffff, class/revision ffffffff
pci_probe_device: [00:01.3] Found 8086:7113, class/revision 06800003
pci_probe_device: [00:01.4] Found ffff:ffff, class/revision ffffffff
pci_probe_device: [00:01.5] Found ffff:ffff, class/revision ffffffff
pci_probe_device: [00:01.6] Found ffff:ffff, class/revision ffffffff
pci_probe_device: [00:01.7] Found ffff:ffff, class/revision ffffffff
pci_probe_device: [00:02.0] Found 1234:1111, class/revision 03000002
pci_probe_device: [00:03.0] Found 8086:100e, class/revision 02000003
pci_probe_device: [00:04.0] Found 1b36:0005, class/revision 00ff0000
pci_probe_device: [00:04.0] Probing
pci_check_pci_bridge: [00:05.0] Found Bridge
pci_probe_device: [01:03.0] Found 8086:100e, class/revision 02000003
pci_probe_device: [00:05.0] Found 1b36:0001, class/revision 06040000

2. Remove unused CONFIG_PCI_MAX_BDF option

3. Add a workaround for Jailhouse pci scanning

4. Extend BAR parsing and handle PIO and MMIO for pci-testdev

Signed-off-by: Brennan Ashton <bashton@brennanashton.com>

5. PCI: Add initial support for QEMU 'edu' test device

Signed-off-by: Brennan Ashton <bashton@brennanashton.com>

6. Bring up PCI later in boot process

Signed-off-by: Brennan Ashton <bashton@brennanashton.com>

7. Add ISR and DMA support to QEMU edu test pci device

Signed-off-by: Brennan Ashton <bashton@brennanashton.com>

8. Fix bad function prototype definition in qemu_edu

9. intel64:  Add a pci test configuration and instructions

Signed-off-by: Brennan Ashton <bashton@brennanashton.com>

10. PCI: Fix issue in identification of 64bit bar

Signed-off-by: Brennan Ashton <bashton@brennanashton.com>
This commit is contained in:
Brennan Ashton 2020-05-07 02:59:29 -07:00 committed by Xiang Xiao
parent 18f97bf2f8
commit 69ed5bb67d
27 changed files with 2247 additions and 1542 deletions

View File

@ -71,6 +71,14 @@ Use control-a x to terminate the emulation.
P.S. Make sure that you CPU supports the mandatory features. Look at Real machine
section for more information.
For testing the PCI bus and driver layers. This QEMU configuration can be used
with the pcitest NuttX configuration::
qemu-system-x86_64 -cpu host,+pcid,+x2apic,+tsc-deadline,+xsave,+rdrand --enable-kvm -smp 1 -m 2G -cdrom boot.iso --nographic -s -no-reboot -device edu -device pci-testdev
This will enable the QEMU pci-test and edu PCI test devices which test PIO, MMIO, IRQ, and DMA
functions. Additionally it will show detailed information about the enumeration of the PCI bus.
Bochs
=====

31
Kconfig
View File

@ -2196,41 +2196,40 @@ config DEBUG_IPC_INFO
endif # DEBUG_IPC
=======
config DEBUG_PCIE
bool "PCI-E Debug Features"
config DEBUG_PCI
bool "PCI Debug Features"
default n
depends on PCIE
depends on PCI
---help---
Enable PCIE driver debug features.
Enable PCI driver debug features.
Support for this debug option is architecture-specific and may not
be available for some MCUs.
if DEBUG_PCIE
if DEBUG_PCI
config DEBUG_PCIE_ERROR
bool "PCI-E Error Output"
config DEBUG_PCI_ERROR
bool "PCI Error Output"
default n
depends on DEBUG_ERROR
---help---
Enable PCI-E driver error output to SYSLOG.
Enable PCI driver error output to SYSLOG.
config DEBUG_PCIE_WARN
bool "PCI-E Warnings Output"
config DEBUG_PCI_WARN
bool "PCI Warnings Output"
default n
depends on DEBUG_WARN
---help---
Enable PCI-E driver warning output to SYSLOG.
Enable PCI driver warning output to SYSLOG.
config DEBUG_PCIE_INFO
bool "PCI-E Informational Output"
config DEBUG_PCI_INFO
bool "PCI Informational Output"
default n
depends on DEBUG_INFO
---help---
Enable PCI-E driver informational output to SYSLOG.
Enable PCI driver informational output to SYSLOG.
endif # DEBUG_PCIE
endif # DEBUG_PCI
endif # DEBUG_FEATURES
config ARCH_HAVE_STACKCHECK

View File

@ -3,10 +3,10 @@
# see the file kconfig-language.txt in the NuttX tools repository.
#
#
config QEMU_PCIE
bool "Initialize and enumerate PCI-E Bus"
config QEMU_PCI
bool "Initialize and enumerate PCI Bus"
default n
select PCIE
select PCI
---help---
Enables initialization and scaning of standard x86-64 pcie bus.
Enables initialization and scanning of standard x86-64 pci bus.

View File

@ -0,0 +1,76 @@
#
# This file is autogenerated: PLEASE DO NOT EDIT IT.
#
# You can use "make menuconfig" to make any modifications to the installed .config file.
# You can then do "make savedefconfig" to generate a new defconfig file that includes your
# modifications.
#
CONFIG_16550_ADDRWIDTH=16
CONFIG_16550_UART0=y
CONFIG_16550_UART0_BASE=0x3f8
CONFIG_16550_UART0_CLOCK=1843200
CONFIG_16550_UART0_IRQ=36
CONFIG_16550_UART0_RXBUFSIZE=16
CONFIG_16550_UART0_SERIAL_CONSOLE=y
CONFIG_16550_UART0_TXBUFSIZE=16
CONFIG_16550_UART=y
CONFIG_ARCH="x86_64"
CONFIG_ARCH_BOARD="qemu-intel64"
CONFIG_ARCH_BOARD_INTEL64_QEMU=y
CONFIG_ARCH_CHIP="intel64"
CONFIG_ARCH_INTEL64_CORE_FREQ_KHZ=2600000
CONFIG_ARCH_SIZET_LONG=y
CONFIG_ARCH_X86_64=y
CONFIG_BOARD_LOOPSPERMSEC=999
CONFIG_BOOT_RUNFROMEXTSRAM=y
CONFIG_BUILTIN=y
CONFIG_CLOCK_MONOTONIC=y
CONFIG_CONSOLE_SYSLOG=y
CONFIG_DEBUG_ERROR=y
CONFIG_DEBUG_FEATURES=y
CONFIG_DEBUG_INFO=y
CONFIG_DEBUG_PCI=y
CONFIG_DEBUG_PCI_ERROR=y
CONFIG_DEBUG_PCI_INFO=y
CONFIG_DEBUG_PCI_WARN=y
CONFIG_DEBUG_WARN=y
CONFIG_EXAMPLES_HELLO=y
CONFIG_EXAMPLES_HELLO_STACKSIZE=4194304
CONFIG_FS_PROCFS=y
CONFIG_IDLETHREAD_STACKSIZE=4194304
CONFIG_LIBM=y
CONFIG_MAX_TASKS=64
CONFIG_NFILE_DESCRIPTORS=32
CONFIG_NSH_ARCHINIT=y
CONFIG_NSH_BUILTIN_APPS=y
CONFIG_NSH_DISABLE_IFCONFIG=y
CONFIG_NSH_DISABLE_IFUPDOWN=y
CONFIG_NSH_READLINE=y
CONFIG_PREALLOC_CHILDSTATUS=16
CONFIG_PRIORITY_INHERITANCE=y
CONFIG_PTHREAD_MUTEX_TYPES=y
CONFIG_PTHREAD_STACK_DEFAULT=4194304
CONFIG_PTHREAD_STACK_MIN=4194304
CONFIG_QEMU_PCI=y
CONFIG_RAM_SIZE=268435456
CONFIG_SCHED_CHILD_STATUS=y
CONFIG_SCHED_HAVE_PARENT=y
CONFIG_SCHED_IRQMONITOR=y
CONFIG_SCHED_TICKLESS=y
CONFIG_SCHED_TICKLESS_ALARM=y
CONFIG_SCHED_TICKLESS_LIMIT_MAX_SLEEP=y
CONFIG_SCHED_WAITPID=y
CONFIG_SDCLONE_DISABLE=y
CONFIG_SIG_DEFAULT=y
CONFIG_START_DAY=3
CONFIG_START_MONTH=3
CONFIG_START_YEAR=2011
CONFIG_SYSTEM_CLE=y
CONFIG_SYSTEM_NSH=y
CONFIG_SYSTEM_TIME64=y
CONFIG_USEC_PER_TICK=1
CONFIG_USERMAIN_STACKSIZE=4194304
CONFIG_USER_ENTRYPOINT="nsh_main"
CONFIG_VIRT=y
CONFIG_VIRT_QEMU_EDU=y
CONFIG_VIRT_QEMU_PCI_TEST=y

View File

@ -66,8 +66,6 @@ extern "C"
* Public Function Prototypes
****************************************************************************/
void qemu_pcie_init(void);
#undef EXTERN
#if defined(__cplusplus)
}

View File

@ -26,8 +26,8 @@ ifeq ($(CONFIG_BOARDCTL),y)
CSRCS += qemu_appinit.c
endif
ifeq ($(CONFIG_QEMU_PCIE),y)
CSRCS += qemu_pcie.c
ifeq ($(CONFIG_QEMU_PCI),y)
CSRCS += qemu_pci.c
endif
include $(TOPDIR)/boards/Board.mk

View File

@ -66,12 +66,6 @@ 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

View File

@ -57,5 +57,10 @@ int qemu_bringup(void)
}
#endif
#ifdef CONFIG_QEMU_PCI
/* Initialization of system */
qemu_pci_init();
#endif
return ret;
}

View File

@ -48,6 +48,8 @@
* Public Function Prototypes
****************************************************************************/
void qemu_pci_init(void);
int qemu_bringup(void);
#endif /* __ASSEMBLY__ */

View File

@ -0,0 +1,226 @@
/****************************************************************************
* boards/x86_64/intel64/qemu-intel64/src/qemu_pci.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 <nuttx/config.h>
#include <assert.h>
#include <debug.h>
#include <nuttx/pci/pci.h>
#include "up_arch.h"
#include "up_internal.h"
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
#define PCI_CFG_ADDR 0xcf8
#define PCI_DATA_ADDR 0xcfc
#define PCI_CFG_EN (1 << 31)
/****************************************************************************
* Private Functions Definitions
****************************************************************************/
static void qemu_pci_cfg_write(FAR struct pci_dev_s *dev, int reg,
uint32_t val, int width);
static uint32_t qemu_pci_cfg_read(FAR struct pci_dev_s *dev, int reg,
int width);
static int qemu_pci_map_bar(uint64_t addr, uint64_t len);
static uint32_t qemu_pci_io_read(FAR const volatile void *addr, int width);
static void qemu_pci_io_write(FAR const volatile void *addr, uint32_t val,
int width);
/****************************************************************************
* Public Data
****************************************************************************/
struct pci_bus_ops_s qemu_pci_bus_ops =
{
.pci_cfg_write = qemu_pci_cfg_write,
.pci_cfg_read = qemu_pci_cfg_read,
.pci_map_bar = qemu_pci_map_bar,
.pci_io_read = qemu_pci_io_read,
.pci_io_write = qemu_pci_io_write,
};
struct pci_bus_s qemu_pci_bus =
{
.ops = &qemu_pci_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
* reg - 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 void qemu_pci_cfg_write(FAR struct pci_dev_s *dev, int reg,
uint32_t val, int width)
{
uint8_t offset_mask = (4 - width);
outl(PCI_CFG_EN | (dev->bdf << 8) | reg, PCI_CFG_ADDR);
switch (width)
{
case 1:
outb(val, PCI_DATA_ADDR + (reg & offset_mask));
return;
case 2:
outw(val, PCI_DATA_ADDR + (reg & offset_mask));
return;
case 4:
outl(val, PCI_DATA_ADDR);
return;
default:
pcierr("Invalid cfg write width %d\n", width);
}
}
/****************************************************************************
* 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 uint32_t qemu_pci_cfg_read(FAR struct pci_dev_s *dev, int reg,
int width)
{
uint32_t ret;
uint8_t offset_mask = 4 - width;
outl(PCI_CFG_EN | (dev->bdf << 8) | reg, PCI_CFG_ADDR);
switch (width)
{
case 1:
ret = inb(PCI_DATA_ADDR + (reg & offset_mask));
return ret;
case 2:
ret = inw(PCI_DATA_ADDR + (reg & offset_mask));
return ret;
case 4:
ret = inl(PCI_DATA_ADDR);
return ret;
default:
pcierr("Invalid cfg read width %d\n", width);
}
return 0;
}
static uint32_t qemu_pci_io_read(FAR const volatile void *addr, int width)
{
uint16_t portaddr = (uint16_t)(intptr_t)addr;
switch (width)
{
case 1:
return (uint32_t)inb(portaddr);
case 2:
return (uint32_t)inw(portaddr);
case 4:
return (uint32_t)inl(portaddr);
default:
pcierr("Invalid read width %d\n", width);
DEBUGPANIC();
}
return 0;
}
static void qemu_pci_io_write(FAR const volatile void *addr, uint32_t val,
int width)
{
uint16_t portaddr = (uint16_t)(intptr_t)addr;
switch (width)
{
case 1:
outb((uint8_t)val, portaddr);
return;
case 2:
outw((uint8_t)val, portaddr);
return;
case 4:
outl((uint8_t)val, portaddr);
return;
default:
pcierr("Invalid write width %d\n", width);
DEBUGPANIC();
}
}
static int qemu_pci_map_bar(uint64_t addr, uint64_t len)
{
up_map_region((void *)(uintptr_t)addr, len,
X86_PAGE_WR | X86_PAGE_PRESENT | X86_PAGE_NOCACHE | X86_PAGE_GLOBAL);
return OK;
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: qemu_pci_init
*
* Description:
* Initialize the PCI-E bus *
*
****************************************************************************/
void qemu_pci_init(void)
{
pciinfo("Initializing PCI Bus\n");
pci_initialize(&qemu_pci_bus);
}

View File

@ -1,398 +0,0 @@
/****************************************************************************
* 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 <jan.kiszka@siemens.com>
*
* 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 <nuttx/config.h>
#include <assert.h>
#include <nuttx/pcie/pcie.h>
#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);
}

View File

@ -1,240 +0,0 @@
/****************************************************************************
* 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 <jan.kiszka@siemens.com>
*
* 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 <nuttx/config.h>
#include <assert.h>
#include <nuttx/pcie/pcie.h>
#include <nuttx/board.h>
#include <arch/board/board.h>
#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 */

View File

@ -59,5 +59,5 @@ source "drivers/usrsock/Kconfig"
source "drivers/dma/Kconfig"
source "drivers/devicetree/Kconfig"
source "drivers/reset/Kconfig"
source "drivers/pcie/Kconfig"
source "drivers/pci/Kconfig"
source "drivers/virt/Kconfig"

View File

@ -77,7 +77,7 @@ include rc/Make.defs
include segger/Make.defs
include usrsock/Make.defs
include reset/Make.defs
include pcie/Make.defs
include pci/Make.defs
include virt/Make.defs
ifeq ($(CONFIG_SPECIFIC_DRIVERS),y)

11
drivers/pci/Kconfig Normal file
View File

@ -0,0 +1,11 @@
#
# For a description of the syntax of this configuration file,
# see the file kconfig-language.txt in the NuttX tools repository.
#
menuconfig PCI
bool "Support for PCI Bus"
default n
---help---
Enables support for the PCI bus.
Backend bust be provided by per-arch or per-board implementation.

View File

@ -1,5 +1,5 @@
############################################################################
# drivers/pcie/Make.defs
# drivers/pci/Make.defs
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
@ -20,13 +20,13 @@
# Don't build anything if there is no CAN support
ifeq ($(CONFIG_PCIE),y)
ifeq ($(CONFIG_PCI),y)
CSRCS += pcie_root.c
CSRCS += pci.c
# Include PCIE device driver build support
# Include PCI device driver build support
DEPPATH += --dep-path pcie
VPATH += :pcie
CFLAGS += ${shell $(INCDIR) $(INCDIROPT) "$(CC)" $(TOPDIR)$(DELIM)drivers$(DELIM)pcie}
DEPPATH += --dep-path pci
VPATH += :pci
CFLAGS += ${shell $(INCDIR) $(INCDIROPT) "$(CC)" $(TOPDIR)$(DELIM)drivers$(DELIM)pci}
endif

792
drivers/pci/pci.c Normal file
View File

@ -0,0 +1,792 @@
/****************************************************************************
* nuttx/drivers/pci/pci.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 <nuttx/config.h>
#include <assert.h>
#include <errno.h>
#include <debug.h>
#include <nuttx/pci/pci.h>
#include <nuttx/virt/qemu_pci.h>
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
/* For now hard code jailhouse as a flag. In the future we can determine this
* by looking at the CPUID base for "Jailhouse\0\0\0"
*/
#define JAILHOUSE_ENABLED 1
#define PCI_BDF(bus, slot, func) (((uint32_t)bus << 8) | \
((uint32_t)slot << 3) | \
func)
/****************************************************************************
* Private Functions Definitions
****************************************************************************/
static void pci_probe_device(FAR struct pci_bus_s *root_bus,
uint8_t bus_idx, uint8_t slot_idx, uint8_t func,
FAR struct pci_dev_type_s **types);
static uint8_t pci_check_pci_bridge(FAR struct pci_bus_s *root_bus,
uint8_t bus_idx, uint8_t slot_idx,
uint8_t dev_func);
static void pci_scan_device(FAR struct pci_bus_s *root_bus,
uint8_t bus_idx, uint8_t slot_idx,
FAR struct pci_dev_type_s **types);
static void pci_scan_bus(FAR struct pci_bus_s *root_bus,
uint8_t bus_idx,
FAR struct pci_dev_type_s **types);
static void pci_set_cmd_bit(FAR struct pci_dev_s *dev, uint16_t bitmask);
static void pci_clear_cmd_bit(FAR struct pci_dev_s *dev, uint16_t bitmask);
/****************************************************************************
* Public Data
****************************************************************************/
struct pci_dev_type_s *pci_device_types[] =
{
#ifdef CONFIG_VIRT_QEMU_PCI_TEST
&pci_type_qemu_pci_test,
#endif /* CONFIG_VIRT_QEMU_PCI_TEST */
#ifdef CONFIG_VIRT_QEMU_EDU
&pci_type_qemu_edu,
#endif /* CONFIG_VIRT_QEMU_EDU */
NULL,
};
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: pci_probe
*
* Description:
* Checks if the specified device is supported and if so calls probe on it
*
* Input Parameters:
* root_bus - The root bus device that lets us address the whole tree
* bus - Bus ID
* slot - Device Slot
* func - Device Function
* types - List of pointers to devices types recognized, NULL terminated
*
****************************************************************************/
static void pci_probe_device(FAR struct pci_bus_s *root_bus,
uint8_t bus_idx, uint8_t slot_idx, uint8_t func,
FAR struct pci_dev_type_s **types)
{
struct pci_dev_s tmp_dev;
uint16_t vid;
uint16_t id;
uint32_t class_rev;
tmp_dev.bus = root_bus;
tmp_dev.bdf = PCI_BDF(bus_idx, slot_idx, func);
vid = root_bus->ops->pci_cfg_read(&tmp_dev, PCI_CONFIG_VENDOR, 2);
id = root_bus->ops->pci_cfg_read(&tmp_dev, PCI_CONFIG_DEVICE, 2);
/* This is reading rev prog_if subclass and class */
class_rev = root_bus->ops->pci_cfg_read(&tmp_dev, PCI_CONFIG_REV_ID, 4);
pci_dev_dump(&tmp_dev);
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 == class_rev)
{
pciinfo("Found: %s\n", types[i]->name);
if (types[i]->probe)
{
pciinfo("[%02x:%02x.%x] Probing\n",
bus_idx, slot_idx, func);
types[i]->probe(root_bus, types[i], tmp_dev.bdf);
}
else
{
pcierr("[%02x:%02x.%x] Error: Invalid \
device probe function\n",
bus_idx, slot_idx, func);
}
break;
}
}
}
}
}
/****************************************************************************
* Name: pci_check_pci_bridge
*
* Description:
* Checks if the specified device is PCI bridge and return the sub-bridge
* idx if found. Otherwise return 0.
*
* Input Parameters:
* root_bus - The root bus device that lets us address the whole tree
* bus - Bus ID
* slot - Device Slot
* func - Device Function
*
****************************************************************************/
static uint8_t pci_check_pci_bridge(FAR struct pci_bus_s *root_bus,
uint8_t bus_idx, uint8_t slot_idx,
uint8_t dev_func)
{
struct pci_dev_s tmp_dev;
uint8_t base_class;
uint8_t sub_class;
uint8_t secondary_bus;
tmp_dev.bus = root_bus;
tmp_dev.bdf = PCI_BDF(bus_idx, slot_idx, dev_func);
/* Check if this is a PCI-PCI bridge device */
base_class = root_bus->ops->pci_cfg_read(&tmp_dev, PCI_CONFIG_CLASS, 1);
sub_class = root_bus->ops->pci_cfg_read(&tmp_dev, PCI_CONFIG_SUBCLASS, 1);
if ((base_class == PCI_CLASS_BASE_BRG_DEV) && \
(sub_class == PCI_CLASS_SUB_PCI_BRG))
{
/* This is a bridge device we need to determin the bus idx and
* enumerate it just like we do the root.
*/
pciinfo("[%02x:%02x.%x] Found Bridge\n",
bus_idx, slot_idx, dev_func);
secondary_bus = root_bus->ops->pci_cfg_read(
&tmp_dev, PCI_CONFIG_SEC_BUS, 1);
return secondary_bus;
}
return 0;
}
/****************************************************************************
* Name: pci_scan_device
*
* Description:
* Checks if the specified device is a bus and iterates over it or
* if it is a real device initializes it if recognized.
*
* Input Parameters:
* root_bus - The root bus device that lets us address the whole tree
* bus - Bus ID
* slot - Device Slot
* types - List of pointers to devices types recognized, NULL terminated
*
****************************************************************************/
static void pci_scan_device(FAR struct pci_bus_s *root_bus,
uint8_t bus_idx, uint8_t slot_idx,
FAR struct pci_dev_type_s **types)
{
struct pci_dev_s tmp_dev;
uint8_t dev_func = 0;
uint16_t vid;
uint8_t sec_bus;
uint8_t multi_function;
tmp_dev.bus = root_bus;
tmp_dev.bdf = PCI_BDF(bus_idx, slot_idx, dev_func);
vid = root_bus->ops->pci_cfg_read(&tmp_dev, PCI_CONFIG_VENDOR, 2);
if (vid == 0xffff)
return;
/* Check if this is a PCI-PCI bridge device */
sec_bus = pci_check_pci_bridge(root_bus, bus_idx, slot_idx, dev_func);
if (sec_bus)
pci_scan_bus(root_bus, sec_bus, types);
multi_function = root_bus->ops->pci_cfg_read(
&tmp_dev, PCI_CONFIG_HEADER_TYPE, 1) & PCI_HEADER_MASK_MULTI;
/* Jailhouse breaks the PCI spec by allowing you to pass individual
* functions of a multi-function device. In this case we need to
* scan each of the functions not just function 0.
*/
if (multi_function || JAILHOUSE_ENABLED)
{
/* This is a multi-function device that we need to iterate over */
for (dev_func = 0; dev_func < 8; dev_func++)
{
tmp_dev.bdf = PCI_BDF(bus_idx, slot_idx, dev_func);
vid = root_bus->ops->pci_cfg_read(&tmp_dev, PCI_CONFIG_VENDOR, 2);
if (vid != 0xffff)
{
sec_bus = pci_check_pci_bridge(
root_bus, bus_idx, slot_idx, dev_func);
if (sec_bus)
{
pci_scan_bus(root_bus, sec_bus, types);
continue;
}
pci_probe_device(root_bus, bus_idx, slot_idx, dev_func, types);
}
}
}
else
{
pci_probe_device(root_bus, bus_idx, slot_idx, dev_func, types);
}
}
/****************************************************************************
* Name: pci_scan_bus
*
* Description:
* Iterates over all slots on bus looking for devices and buses to
* enumerate.
*
* Input Parameters:
* root_bus - The root bus device that lets us address the whole tree
* bus - Bus ID
* types - List of pointers to devices types recognized, NULL terminated
*
****************************************************************************/
static void pci_scan_bus(FAR struct pci_bus_s *root_bus,
uint8_t bus_idx,
FAR struct pci_dev_type_s **types)
{
uint8_t slot_idx;
for (slot_idx = 0; slot_idx < 32; slot_idx++)
{
pci_scan_device(root_bus, bus_idx, slot_idx, types);
}
return;
}
/****************************************************************************
* Name: pci_set_cmd_bit
*
* Description:
* This sets an individual bit in the command register for a device.
*
* Input Parameters:
* dev - device
* bit - Bit to set
*
****************************************************************************/
static void pci_set_cmd_bit(FAR struct pci_dev_s *dev, uint16_t bitmask)
{
uint16_t cmd;
cmd = dev->bus->ops->pci_cfg_read(dev, PCI_CONFIG_COMMAND, 2);
dev->bus->ops->pci_cfg_write(dev, PCI_CONFIG_COMMAND,
(cmd | bitmask), 2);
}
/****************************************************************************
* Name: pci_clear_cmd_bit
*
* Description:
* This clears an individual bit in the command register for a device.
*
* Input Parameters:
* dev - device
* bit - Bit to set
*
****************************************************************************/
static void pci_clear_cmd_bit(FAR struct pci_dev_s *dev, uint16_t bitmask)
{
uint16_t cmd;
cmd = dev->bus->ops->pci_cfg_read(dev, PCI_CONFIG_COMMAND, 2);
dev->bus->ops->pci_cfg_write(dev, PCI_CONFIG_COMMAND,
(cmd & ~bitmask), 2);
}
/****************************************************************************
* 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
* types - List of pointers to devices types recognized, NULL terminated
*
* Returned Value:
* 0: success, <0: A negated errno
*
****************************************************************************/
int pci_enumerate(FAR struct pci_bus_s *bus,
FAR struct pci_dev_type_s **types)
{
if (!bus)
return -EINVAL;
if (!types)
return -EINVAL;
pci_scan_bus(bus, 0, types);
return OK;
}
/****************************************************************************
* Name: pci_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 pci_initialize(FAR struct pci_bus_s *bus)
{
return pci_enumerate(bus, pci_device_types);
}
/****************************************************************************
* Name: pci_enable_io
*
* Description:
* Enable MMIO or IOPORT
*
* Input Parameters:
* dev - device
* space - which resource is being enabled
* PCI_SYS_RES_IOPORT for io port address decoding or
* PCI_SYS_RES_MEM for memory
*
* Return value:
* -EINVAL: error
* OK: OK
*
****************************************************************************/
int pci_enable_io(FAR struct pci_dev_s *dev, int res)
{
switch (res)
{
case PCI_SYS_RES_IOPORT:
pci_set_cmd_bit(dev, PCI_CMD_IO_SPACE);
return OK;
case PCI_SYS_RES_MEM:
pci_set_cmd_bit(dev, PCI_CMD_MEM_SPACE);
return OK;
}
return -EINVAL;
}
/****************************************************************************
* Name: pci_disable_io
*
* Description:
* Enable MMIO or IOPORT
*
* Input Parameters:
* dev - device
* space - which resource is being disabled
* PCI_SYS_RES_IOPORT for io port address decoding or
* PCI_SYS_RES_MEM for memory
*
* Return value:
* -EINVAL: error
* OK: OK
*
****************************************************************************/
int pci_disable_io(FAR struct pci_dev_s *dev, int res)
{
switch (res)
{
case PCI_SYS_RES_IOPORT:
pci_clear_cmd_bit(dev, PCI_CMD_IO_SPACE);
return OK;
case PCI_SYS_RES_MEM:
pci_clear_cmd_bit(dev, PCI_CMD_MEM_SPACE);
return OK;
}
return -EINVAL;
}
/****************************************************************************
* Name: pci_enable_bus_master
*
* Description:
* Enable bus mastering for device so it can perform PCI accesses
*
* Input Parameters:
* dev - device
*
* Return value:
* -EINVAL: error
* OK: OK
*
****************************************************************************/
int pci_enable_bus_master(FAR struct pci_dev_s *dev)
{
pci_set_cmd_bit(dev, PCI_CMD_BUS_MSTR);
return OK;
}
/****************************************************************************
* Name: pci_disable_bus_master
*
* Description:
* Disable bus mastering for device
*
* Input Parameters:
* dev - device
*
* Return value:
* -EINVAL: error
* OK: OK
*
****************************************************************************/
int pci_disable_bus_master(FAR struct pci_dev_s *dev)
{
pci_clear_cmd_bit(dev, PCI_CMD_BUS_MSTR);
return OK;
}
/****************************************************************************
* Name: pci_bar_valid
*
* Description:
* Determine in if the address in the BAR is valid
*
* Input Parameters:
* dev - device
* bar_id - bar number
*
* Return value:
* -EINVAL: error
* OK: OK
*
****************************************************************************/
int pci_bar_valid(FAR struct pci_dev_s *dev, uint8_t bar_id)
{
uint32_t bar = dev->bus->ops->pci_cfg_read(dev,
PCI_HEADER_NORM_BAR0 + (bar_id * 4), 4);
if (bar == PCI_BAR_INVALID)
{
return -EINVAL;
}
return OK;
}
/****************************************************************************
* Name: pci_bar_is_64
*
* Description:
* Determine in if the bar address is 64 bit. If it is the address includes
* the address in the next bar location.
*
* Input Parameters:
* dev - device
* bar_id - bar number
*
* Return value:
* true: 64bit address
*
****************************************************************************/
bool pci_bar_is_64(FAR struct pci_dev_s *dev, uint8_t bar_id)
{
uint32_t bar = dev->bus->ops->pci_cfg_read(dev,
PCI_HEADER_NORM_BAR0 + (bar_id * 4), 4);
/* Check that it is memory and not io port */
if ((bar & PCI_BAR_LAYOUT_MASK) != PCI_BAR_LAYOUT_MEM)
return false;
if (((bar & PCI_BAR_TYPE_MASK) >> PCI_BAR_TYPE_OFFSET) == PCI_BAR_TYPE_64)
{
return true;
}
return false;
}
/****************************************************************************
* Name: pci_bar_size
*
* Description:
* Determine the size of the address space required by the BAR
*
* Input Parameters:
* dev - device
* bar_id - bar number
*
* Return value:
* Size of address space
*
****************************************************************************/
uint64_t pci_bar_size(FAR struct pci_dev_s *dev, uint8_t bar_id)
{
uint32_t bar;
uint32_t size;
uint64_t full_size;
uint8_t bar_offset = PCI_HEADER_NORM_BAR0 + (bar_id * 4);
const struct pci_bus_ops_s *dev_ops = dev->bus->ops;
bar = dev_ops->pci_cfg_read(dev, bar_offset, 4);
/* Write all 1 to the BAR. We are looking for which bits will change */
dev_ops->pci_cfg_write(dev, bar_offset, 0xffffffff, 4);
full_size = dev_ops->pci_cfg_read(dev, bar_offset, 4);
/* Resore BAR to original values */
dev_ops->pci_cfg_write(dev, bar_offset, bar, 4);
if (full_size == 0)
{
/* This is not a valid bar */
return 0;
}
if ((bar & PCI_BAR_LAYOUT_MASK) == PCI_BAR_LAYOUT_MEM)
{
full_size &= PCI_BAR_MEM_BASE_MASK;
}
else
{
full_size &= PCI_BAR_IO_BASE_MASK;
}
/* If it is 64 bit address check the next bar as well */
if (pci_bar_is_64(dev, bar_id))
{
bar_offset += 4;
bar = dev_ops->pci_cfg_read(dev, bar_offset, 4);
dev_ops->pci_cfg_write(dev, bar_offset, 0xffffffff, 4);
size = dev_ops->pci_cfg_read(dev, bar_offset, 4);
dev_ops->pci_cfg_write(dev, bar_offset, bar, 4);
full_size |= ((uint64_t)size << 32);
}
else
{
full_size |= (uint64_t)(0xffffffff) << 32;
}
return ~full_size + 1;
}
/****************************************************************************
* Name: pci_bar_addr
*
* Description:
* Determine the size of the address space required by the BAR
*
* Input Parameters:
* dev - device
* bar_id - bar number
*
* Return value:
* full bar address
*
****************************************************************************/
uint64_t pci_bar_addr(FAR struct pci_dev_s *dev, uint8_t bar_id)
{
uint64_t addr;
uint8_t bar_offset = PCI_HEADER_NORM_BAR0 + (bar_id * 4);
const struct pci_bus_ops_s *dev_ops = dev->bus->ops;
addr = dev_ops->pci_cfg_read(dev, bar_offset, 4);
if ((addr & PCI_BAR_LAYOUT_MASK) == PCI_BAR_LAYOUT_MEM)
{
addr &= PCI_BAR_MEM_BASE_MASK;
}
else
{
addr &= PCI_BAR_IO_BASE_MASK;
}
/* If it is 64 bit address check the next bar as well */
if (pci_bar_is_64(dev, bar_id))
{
bar_offset += 4;
addr |= (uint64_t)(dev_ops->pci_cfg_read(dev, bar_offset, 4)) << 32;
}
return addr;
}
/****************************************************************************
* Name: pci_dev_dump
*
* Description:
* Dump the configuration information for the device
*
* Input Parameters:
* dev - device
*
****************************************************************************/
void pci_dev_dump(FAR struct pci_dev_s *dev)
{
uint8_t bar_id;
uint8_t bar_mem_type = 0;
uint32_t bar;
uint64_t bar_size;
uint64_t bar_addr;
uint8_t cap_id;
uint8_t cap_offset;
const struct pci_bus_ops_s *dev_ops = dev->bus->ops;
uint32_t bdf = dev->bdf;
uint16_t vid = dev_ops->pci_cfg_read(dev, PCI_CONFIG_VENDOR, 2);
uint16_t pid = dev_ops->pci_cfg_read(dev, PCI_CONFIG_DEVICE, 2);
uint8_t header = dev_ops->pci_cfg_read(dev, PCI_CONFIG_HEADER_TYPE, 1);
uint8_t progif = dev_ops->pci_cfg_read(dev, PCI_CONFIG_PROG_IF, 1);
uint8_t subclass = dev_ops->pci_cfg_read(dev, PCI_CONFIG_SUBCLASS, 1);
uint8_t class = dev_ops->pci_cfg_read(dev, PCI_CONFIG_CLASS, 1);
uint8_t int_pin;
uint8_t int_line;
pciinfo("[%02x:%02x.%x] %04x:%04x\n",
bdf >> 8, (bdf & 0xff) >> 3, bdf & 0x7, vid, pid);
pciinfo("\ttype %02x Prog IF %02x Class %02x Subclass %02x\n",
header, progif, class, subclass);
cap_offset = dev_ops->pci_cfg_read(dev, PCI_HEADER_NORM_CAP, 1);
while (cap_offset)
{
cap_id = dev_ops->pci_cfg_read(dev, cap_offset, 1);
if (cap_id > PCI_CAP_ID_END)
{
pcierr("Invalid PCI Capability Found, Skipping. %d\n", cap_id);
DEBUGPANIC();
break;
}
pciinfo("\tCAP %02x\n", cap_id);
cap_offset = dev_ops->pci_cfg_read(dev, cap_offset + 1, 1);
}
if ((header & PCI_HEADER_TYPE_MASK) != PCI_HEADER_NORMAL)
return;
int_pin = dev_ops->pci_cfg_read(dev, PCI_HEADER_NORM_INT_PIN, 1);
int_line = dev_ops->pci_cfg_read(dev, PCI_HEADER_NORM_INT_LINE, 1);
pciinfo("\tINT Pin %02x Line %02x\n", int_pin, int_line);
for (bar_id = 0; bar_id < PCI_BAR_CNT; bar_id++)
{
if (pci_bar_valid(dev, bar_id) != OK)
continue;
bar = dev_ops->pci_cfg_read(dev,
PCI_HEADER_NORM_BAR0 + (bar_id * 4), 4);
bar_size = pci_bar_size(dev, bar_id);
bar_addr = pci_bar_addr(dev, bar_id);
if ((bar & PCI_BAR_LAYOUT_MASK) == PCI_BAR_LAYOUT_MEM)
{
switch ((bar & PCI_BAR_TYPE_MASK) >> PCI_BAR_TYPE_OFFSET)
{
case PCI_BAR_TYPE_64:
bar_mem_type = 64;
break;
case PCI_BAR_TYPE_32:
bar_mem_type = 32;
break;
case PCI_BAR_TYPE_16:
bar_mem_type = 16;
break;
default:
bar_mem_type = 0;
}
pciinfo("\tBAR [%d] MEM %db range %p-%p (%p)\n",
bar_id, bar_mem_type,
bar_addr, bar_addr + bar_size - 1, bar_size);
}
else
{
pciinfo("\tBAR [%d] PIO range %p-%p (%p)\n",
bar_id,
bar_addr, bar_addr + bar_size - 1, bar_size);
}
/* Skip next bar if this one was 64bit */
if (bar_mem_type == 64)
bar_id++;
}
}

View File

@ -1,20 +0,0 @@
#
# 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

View File

@ -1,446 +0,0 @@
/****************************************************************************
* 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 <nuttx/config.h>
#include <assert.h>
#include <errno.h>
#include <debug.h>
#include <nuttx/pcie/pcie.h>
#include <nuttx/virt/qemu_pci.h>
/****************************************************************************
* 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;
}

View File

@ -15,8 +15,15 @@ if VIRT
config VIRT_QEMU_PCI_TEST
bool "Driver for QEMU PCI test device"
default n
select PCIE
select PCI
---help---
Driver for QEMU PCI test device
config VIRT_QEMU_EDU
bool "Driver for QEMU EDU test device"
default n
select PCI
---help---
Driver for QEMU EDU test device
endif # VIRT

View File

@ -1,5 +1,5 @@
############################################################################
# drivers/pcie/Make.defs
# drivers/pci/Make.defs
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
@ -26,6 +26,12 @@ CSRCS += qemu_pci_test.c
endif
ifeq ($(CONFIG_VIRT_QEMU_EDU),y)
CSRCS += qemu_edu.c
endif
# Include virt device driver build support
#
ifeq ($(CONFIG_VIRT),y)

448
drivers/virt/qemu_edu.c Normal file
View File

@ -0,0 +1,448 @@
/*****************************************************************************
* 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 <nuttx/config.h>
#include <nuttx/arch.h>
#include <nuttx/irq.h>
#include <nuttx/kmalloc.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <math.h>
#include <unistd.h>
#include <errno.h>
#include <sched.h>
#include <nuttx/pci/pci.h>
#include <nuttx/virt/qemu_pci.h>
/*****************************************************************************
* 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
/*****************************************************************************
* 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);
/*****************************************************************************
* Private Data
*****************************************************************************/
/*****************************************************************************
* 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,
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,
struct qemu_edu_priv_s *drv_priv)
{
uintptr_t base_addr = drv_priv->base_addr;
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 = 0x40000;
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, void *context, FAR void *arg)
{
struct qemu_edu_priv_s *drv_priv = (struct qemu_edu_priv_s *)arg;
uintptr_t base_addr = drv_priv->base_addr;
uint32_t status = qemu_edu_read_reg32(base_addr + EDU_REG_INT_STATUS);
qemu_edu_write_reg32(base_addr + EDU_REG_INT_ACK, ~0U);
switch (status)
{
case 0x1: /* Factorial triggered */
drv_priv->test_result = qemu_edu_read_reg32(base_addr + EDU_REG_FAC);
pciinfo("Computed factorial: %d\n",
drv_priv->test_result);
break;
case 0x100: /* DMA triggered */
pciinfo("DMA transfer complete\n");
break;
default: /* Generic write */
drv_priv->test_result = status;
pciinfo("Received value: 0x%08x\n", status);
}
sem_post(&drv_priv->isr_done);
return OK;
}
/*****************************************************************************
* Public Functions
*****************************************************************************/
/*****************************************************************************
* Name: qemu_edu_probe
*
* Description:
* Initialize device
*****************************************************************************/
int qemu_edu_probe(FAR struct pci_bus_s *bus,
FAR struct pci_dev_type_s *type, uint16_t bdf)
{
uint32_t bar;
uintptr_t bar_addr;
struct pci_dev_s dev =
{
.bus = bus,
.type = type,
.bdf = bdf,
};
uint8_t irq;
struct qemu_edu_priv_s drv_priv;
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;
}
/*****************************************************************************
* Public Data
*****************************************************************************/
struct pci_dev_type_s pci_type_qemu_edu =
{
.vendor = 0x1234,
.device = 0x11e8,
.class_rev = PCI_ID_ANY,
.name = "Qemu PCI EDU device",
.probe = qemu_edu_probe
};

View File

@ -33,30 +33,133 @@
#include <errno.h>
#include <sched.h>
#include <nuttx/pcie/pcie.h>
#include <nuttx/pci/pci.h>
#include <nuttx/virt/qemu_pci.h>
/*****************************************************************************
* Pre-processor Definitions
*****************************************************************************/
/*****************************************************************************
* Private Functions Definitions
*****************************************************************************/
static uint32_t mem_read(FAR const volatile void *addr, int width);
static void mem_write(FAR const volatile void *addr, uint32_t val, int width);
/*****************************************************************************
* 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. */
uint8_t test; /* write-only, starts a given test number */
uint8_t width; /* read-only, type and width of access for a test */
uint8_t pad0[2];
uint32_t offset; /* read-only, offset in this BAR for a given test */
uint32_t data; /* read-only, data to use for a given test */
uint32_t count; /* for debugging. number of writes detected. */
uint8_t name[]; /* for debugging. 0-terminated ASCII string. */
};
/* Structure the read and write helpers */
struct pci_test_dev_ops_s
{
uint32_t (*read)(FAR const volatile void *addr, int width);
void (*write)(FAR const volatile void *addr, uint32_t val, int width);
};
/*****************************************************************************
* Private Data
*****************************************************************************/
static struct pci_test_dev_ops_s mem_ops =
{
.read = mem_read,
.write = mem_write
};
/*****************************************************************************
* Private Functions
*****************************************************************************/
static uint32_t mem_read(FAR const volatile void *addr, int unused)
{
return *(volatile uint32_t *)addr;
}
static void mem_write(FAR const volatile void *addr, uint32_t val, int unused)
{
*(volatile uint32_t *)addr = val;
}
static bool qemu_pci_test_bar(FAR struct pci_test_dev_ops_s *test_ops,
FAR struct pci_test_dev_hdr_s *test_hdr,
uint16_t test_num)
{
uint32_t count;
uint32_t data;
uint32_t offset;
uint8_t width;
const int write_limit = 8;
int write_cnt;
int i;
char testname[32];
pciinfo("WRITING Test# %d %p\n", test_num, &test_hdr->test);
test_ops->write(&test_hdr->test, test_num, 1);
/* Reading of the string is a little ugly to handle the case where
* we must use the port access methods. For memory map we would
* be able to just read directly.
*/
testname[sizeof(testname) - 1] = 0;
for (i = 0; i < sizeof(testname); i++)
{
testname[i] = (char)test_ops->read((void *)&test_hdr->name + i, 1);
if (testname[i] == 0)
break;
}
pciinfo("Running test: %s\n", testname);
count = test_ops->read(&test_hdr->count, 4);
pciinfo("COUNT: %04x\n", count);
if (count != 0)
return false;
width = test_ops->read(&test_hdr->width, 1);
pciinfo("Width: %d\n", width);
if (width == 0 || width > 4)
return false;
data = test_ops->read(&test_hdr->data, 4);
pciinfo("Data: %04x\n", data);
offset = test_ops->read(&test_hdr->offset, 4);
pciinfo("Offset: %04x\n", offset);
for (write_cnt = 0; write_cnt < write_limit; write_cnt++)
{
pciinfo("Issuing WRITE to %p %x %d\n",
(void *)test_hdr + offset,
data, width);
test_ops->write((void *)test_hdr + offset, data, width);
}
count = test_ops->read(&test_hdr->count, 4);
pciinfo("COUNT: %04x\n", count);
if (!count)
return true;
return (int)count == write_cnt;
}
/*****************************************************************************
* Public Functions
*****************************************************************************/
@ -68,49 +171,73 @@ struct pci_test_dev_hdr_s
* Initialize device
*****************************************************************************/
int qemu_pci_test_probe(FAR struct pcie_bus_s *bus,
FAR struct pcie_dev_type_s *type, uint16_t bdf)
int qemu_pci_test_probe(FAR struct pci_bus_s *bus,
FAR struct pci_dev_type_s *type, uint16_t bdf)
{
uint32_t bar[2];
struct pcie_dev_s dev =
uint8_t bar_id;
uint32_t bar;
uint64_t bar_addr;
struct pci_test_dev_hdr_s *test_hdr;
struct pci_dev_s dev =
{
.bus = bus,
.type = type,
.bdf = bdf,
};
pci_enable_device(&dev);
for (int ii = 0; ii < 2; ii++)
struct pci_test_dev_ops_s io_ops =
{
pci_get_bar(&dev, ii, bar + ii);
.read = bus->ops->pci_io_read,
.write = bus->ops->pci_io_write
};
if ((bar[ii] & PCI_BAR_IO) != PCI_BAR_IO)
struct pci_test_dev_ops_s *test_ops;
uint16_t test_cnt;
pci_enable_bus_master(&dev);
pciinfo("Enabled bus mastering\n");
pci_enable_io(&dev, PCI_SYS_RES_MEM);
pci_enable_io(&dev, PCI_SYS_RES_IOPORT);
pciinfo("Enabled i/o port and memory resources\n");
for (bar_id = 0; bar_id < PCI_BAR_CNT; bar_id++)
{
/* Need to query the BAR for IO vs MEM
* Also handle if the bar is 64bit address
*/
if (pci_bar_valid(&dev, bar_id) != OK)
continue;
bar = bus->ops->pci_cfg_read(&dev,
PCI_HEADER_NORM_BAR0 + (bar_id * 4), 4);
bar_addr = pci_bar_addr(&dev, bar_id);
test_hdr = (struct pci_test_dev_hdr_s *)bar_addr;
if ((bar & PCI_BAR_LAYOUT_MASK) == PCI_BAR_LAYOUT_MEM)
{
pciinfo("Mapping BAR%d: %x\n", ii, bar[ii]);
test_ops = &mem_ops;
pci_map_bar(&dev, ii, 0x1000, NULL);
/* If the BAR is MMIO the it must be mapped */
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++;
}
bus->ops->pci_map_bar(bar_addr, pci_bar_size(&dev, bar_id));
}
else
{
test_ops = &io_ops;
}
for (test_cnt = 0; test_cnt < 0xffff; test_cnt++)
{
if (!qemu_pci_test_bar(test_ops, test_hdr, test_cnt))
break;
pciinfo("Test Completed BAR [%d] TEST [%d]\n", bar_id, test_cnt);
}
if (pci_bar_is_64(&dev, bar_id))
bar_id++;
}
return OK;
@ -120,7 +247,7 @@ int qemu_pci_test_probe(FAR struct pcie_bus_s *bus,
* Public Data
*****************************************************************************/
struct pcie_dev_type_s pcie_type_qemu_pci_test =
struct pci_dev_type_s pci_type_qemu_pci_test =
{
.vendor = 0x1b36,
.device = 0x0005,

View File

@ -920,19 +920,19 @@
# define ipcinfo _none
#endif
#ifdef CONFIG_DEBUG_PCIE_ERROR
#ifdef CONFIG_DEBUG_PCI_ERROR
# define pcierr _err
#else
# define pcierr _none
#endif
#ifdef CONFIG_DEBUG_PCIE_WARN
#ifdef CONFIG_DEBUG_PCI_WARN
# define pciwarn _warn
#else
# define pciwarn _none
#endif
#ifdef CONFIG_DEBUG_PCIE_INFO
#ifdef CONFIG_DEBUG_PCI_INFO
# define pciinfo _info
#else
# define pciinfo _none

458
include/nuttx/pci/pci.h Normal file
View File

@ -0,0 +1,458 @@
/****************************************************************************
* include/nuttx/pci/pci.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_H
#define __INCLUDE_NUTTX_PCI_PCI_H
#ifdef CONFIG_PCI
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <sys/types.h>
#include <stdint.h>
#include <nuttx/fs/ioctl.h>
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
/* PCI config common registers */
#define PCI_CONFIG_VENDOR 0x00
#define PCI_CONFIG_DEVICE 0x02
#define PCI_CONFIG_COMMAND 0x04
#define PCI_CONFIG_REV_ID 0x08
#define PCI_CONFIG_PROG_IF 0x09
#define PCI_CONFIG_SUBCLASS 0x0A
#define PCI_CONFIG_CLASS 0x0B
#define PCI_CONFIG_CACHE_LINE_SIZE 0x0C
#define PCI_CONFIG_LATENCY_TIMER 0x0D
#define PCI_CONFIG_HEADER_TYPE 0x0E
#define PCI_CONFIG_BIST 0x0F
/* PCI config header types */
#define PCI_HEADER_NORMAL 0x00
#define PCI_HEADER_BRIDGE 0x01
#define PCI_HEADER_CARDBUS 0x02
#define PCI_HEADER_TYPE_MASK 0x3F
#define PCI_HEADER_MASK_MULTI 0x80
/* PCI config registers type 0 (Normal devices) */
#define PCI_HEADER_NORM_BAR0 0x10
#define PCI_HEADER_NORM_BAR1 0x14
#define PCI_HEADER_NORM_BAR2 0x18
#define PCI_HEADER_NORM_BAR3 0x1C
#define PCI_HEADER_NORM_BAR4 0x20
#define PCI_HEADER_NORM_BAR5 0x24
#define PCI_HEADER_NORM_CB_CIS 0x28
#define PCI_HEADER_NORM_SUB_VID 0x2C
#define PCI_HEADER_NORM_SUB_ID 0x2E
#define PCI_HEADER_NORM_EXP_ROM 0x30
#define PCI_HEADER_NORM_CAP 0x34
#define PCI_HEADER_NORM_INT_LINE 0x3C
#define PCI_HEADER_NORM_INT_PIN 0x3D
#define PCI_HEADER_NORM_MIN_GRANT 0x3E
#define PCI_HEADER_NORM_MAX_LAT 0x3E
/* PCI config registers type 1 (PCI-PCI bridge) */
#define PCI_CONFIG_SEC_BUS 0x19
/* PCI config registers type 2 (CardBus) */
/* PCI Base Class Codes */
#define PCI_CLASS_BASE_UNCLASSIFIED 0x00
#define PCI_CLASS_BASE_MASS_STORAGE_CTRL 0x01
#define PCI_CLASS_BASE_NETWORK_CTRL 0x02
#define PCI_CLASS_BASE_DISPLAY_CTRL 0x03
#define PCI_CLASS_BASE_MULTIMEDIA_CTRL 0x04
#define PCI_CLASS_BASE_MEM_CTRL 0x05
#define PCI_CLASS_BASE_BRG_DEV 0x06
#define PCI_CLASS_BASE_SMPL_COM_CTRL 0x07
#define PCI_CLASS_BASE_BSP 0x08
#define PCI_CLASS_BASE_INPUT_DEV_CTRL 0x09
#define PCI_CLASS_BASE_DOCK_STN 0x0A
#define PCI_CLASS_BASE_PROCESSOR 0x0B
#define PCI_CLASS_BASE_SBC 0x0C
#define PCI_CLASS_BASE_WIRELESS_CTRL 0x0D
#define PCI_CLASS_BASE_INTL_CTRL 0x0E
#define PCI_CLASS_BASE_SAT_COM_CTRL 0x0F
#define PCI_CLASS_BASE_ENCRYPT_CTRL 0x10
#define PCI_CLASS_BASE_SPC 0x11
#define PCI_CLASS_BASE_PROC_ACCEL 0x12
#define PCI_CLASS_BASE_NON_ES_INST 0x13
/* Reserved 0x14-0x3F */
#define PCI_CLASS_BASE_CO_PROC 0x40
/* Reserved 0x41-0xFE */
#define PCI_CLASS_BASE_UNASSIGNED 0xFF
/* PCI Sub Class Codes (most missing) */
/* Bridge Class */
#define PCI_CLASS_SUB_HOST_BRG 0x00
#define PCI_CLASS_SUB_ISA_BRG 0x01
#define PCI_CLASS_SUB_EISA_BRG 0x02
#define PCI_CLASS_SUB_MCA_BRG 0x03
#define PCI_CLASS_SUB_PCI_BRG 0x04
#define PCI_CLASS_SUB_PCMCIA_BRG 0x05
#define PCI_CLASS_SUB_NUBUS_BRG 0x06
#define PCI_CLASS_SUB_CARDBUS_BRG 0x07
#define PCI_CLASS_SUB_RACEWAY_BRG 0x08
#define PCI_CLASS_SUB_PCI_TRNSP_BRG 0x09
#define PCI_CLASS_SUB_INFINI_BRG 0x0A
#define PCI_CLASS_SUB_NUBUS_BRG 0x80
#define PCI_ID_ANY 0xffff
/* PCI Command Register Bitmasks */
#define PCI_CMD_IO_SPACE 0x0001
#define PCI_CMD_MEM_SPACE 0x0002
#define PCI_CMD_BUS_MSTR 0x0004
#define PCI_CMD_SPECIAL_CYC 0x0008
#define PCI_CMD_MEM_INV 0x0030
#define PCI_CMD_VGA_PLT 0x0040
#define PCI_CMD_PAR_ERR 0x0080
#define PCI_CMD_SERR 0x0100
#define PCI_CMD_FST_B2B 0x0200
#define PCI_CMD_INT 0x0400
/* PCI BAR Bitmasks */
#define PCI_BAR_LAYOUT_MASK 0x00000001
#define PCI_BAR_TYPE_MASK 0x00000006
#define PCI_BAR_MEM_PF_MASK 0x00000008
#define PCI_BAR_MEM_BASE_MASK 0xfffffff0
#define PCI_BAR_IO_BASE_MASK 0xfffffffc
/* PCI BAR OFFSETS */
#define PCI_BAR_LAYOUT_OFFSET 0
#define PCI_BAR_TYPE_OFFSET 1
#define PCI_BAR_MEM_PF_OFFSET 3
#define PCI_BAR_MEM_BASE_OFFSET 4
#define PCI_BAR_IO_BASE_OFFSET 2
/* PCI BAR */
#define PCI_BAR_CNT 6
#define PCI_BAR_INVALID 0
#define PCI_BAR_LAYOUT_MEM 0
#define PCI_BAR_LAYOUT_IO 1
#define PCI_BAR_TYPE_32 0x00
#define PCI_BAR_TYPE_16 0x01 /* This mode is not used */
#define PCI_BAR_TYPE_64 0x02
/* PCI CAP */
#define PCI_CAP_ID_PM 0x01 /* Power Management */
#define PCI_CAP_ID_AGP 0x02 /* Accelerated Graphics */
#define PCI_CAP_ID_VPD 0x03 /* Vital Product Data */
#define PCI_CAP_ID_SLOT 0x04 /* Slot ID */
#define PCI_CAP_ID_MSI 0x05 /* MSI */
#define PCI_CAP_ID_CHP 0x06 /* CompactPCI Hot-Swap */
#define PCI_CAP_ID_PCIX 0x07 /* PCI-X */
#define PCI_CAP_ID_HT 0x08 /* HyperTransport */
#define PCI_CAP_ID_VNDR 0x09 /* Vendor */
#define PCI_CAP_ID_DBG 0x0A /* Debug */
#define PCI_CAP_ID_CCRC 0x0B /* CompactPCI Central Resource Control */
#define PCI_CAP_ID_HOT 0x0C /* Hot-Plug Controller */
#define PCI_CAP_ID_BRG_VID 0x0D /* Bridge Vendor/Device ID */
#define PCI_CAP_ID_AGP_BRG 0x0E /* AGP PCI-PCI Bridge */
#define PCI_CAP_ID_SEC_DEV 0x0F /* Secure Device */
#define PCI_CAP_ID_PCIE 0x10 /* PCIe */
#define PCI_CAP_ID_MSIX 0x11 /* MSI-X */
#define PCI_CAP_ID_SATA 0x12 /* SATA */
#define PCI_CAP_ID_ADVF 0x13 /* Advanced Features */
#define PCI_CAP_ID_END PCI_CAP_ID_ADVF
/* Resource types used by PCI devices */
#define PCI_SYS_RES_IOPORT 0x00
#define PCI_SYS_RES_MEM 0x01
/****************************************************************************
* Public Types
****************************************************************************/
/* The PCI driver interface */
struct pci_bus_s;
struct pci_dev_type_s;
struct pci_dev_s;
/* Bus related operations */
struct pci_bus_ops_s
{
CODE void (*pci_cfg_write)(FAR struct pci_dev_s *dev, int reg,
uint32_t val, int width);
CODE uint32_t (*pci_cfg_read)(FAR struct pci_dev_s *dev, int reg,
int width);
CODE int (*pci_map_bar)(uint64_t addr, uint64_t len);
CODE uint32_t (*pci_io_read)(FAR const volatile void *addr, int width);
CODE void (*pci_io_write)(FAR const volatile void *addr, uint32_t val,
int width);
};
/* PCI bus private data. */
struct pci_bus_s
{
FAR const struct pci_bus_ops_s *ops; /* operations */
};
/* PCI device type, defines by vendor ID and device ID */
struct pci_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 pci_bus_s *bus,
FAR struct pci_dev_type_s *type, uint16_t bdf);
};
/* PCI device private data. */
struct pci_dev_s
{
FAR struct pci_bus_s *bus;
FAR struct pci_dev_type_s *type;
uint32_t bdf;
};
/****************************************************************************
* Public Functions Prototypes
****************************************************************************/
#undef EXTERN
#if defined(__cplusplus)
#define EXTERN extern "C"
extern "C"
{
#else
#define EXTERN extern
#endif
/****************************************************************************
* Name: pci_initialize
*
* Description:
* Initialize the PCI bus and enumerate the devices with give devices
* type array
*
* Input Parameters:
* bus - An PCI bus
* types - A array of PCI device types
*
* Returned Value:
* OK if the driver was successfully register; A negated errno value is
* returned on any failure.
*
****************************************************************************/
int pci_initialize(FAR struct pci_bus_s *bus);
/****************************************************************************
* Name: pci_enable_io
*
* Description:
* Enable MMIO or IOPORT
*
* Input Parameters:
* dev - device
* space - which resource is being enabled
* PCI_SYS_RES_IOPORT for io port address decoding or
* PCI_RES_MEM for memory
*
* Return value:
* -EINVAL: error
* OK: OK
*
****************************************************************************/
int pci_enable_io(FAR struct pci_dev_s *dev, int res);
/****************************************************************************
* Name: pci_disable_io
*
* Description:
* Enable MMIO or IOPORT
*
* Input Parameters:
* dev - device
* space - which resource is being disabled
* PCI_SYS_RES_IOPORT for io port address decoding or
* PCI_SYS_RES_MEM for memory
*
* Return value:
* -EINVAL: error
* OK: OK
*
****************************************************************************/
int pci_disable_io(FAR struct pci_dev_s *dev, int res);
/****************************************************************************
* Name: pci_enable_bus_master
*
* Description:
* Enable bus mastering for device so it can perform PCI accesses
*
* Input Parameters:
* dev - device
*
* Return value:
* -EINVAL: error
* OK: OK
*
****************************************************************************/
int pci_enable_bus_master(FAR struct pci_dev_s *dev);
/****************************************************************************
* Name: pci_disable_bus_master
*
* Description:
* Disable bus mastering for device
*
* Input Parameters:
* dev - device
*
* Return value:
* -EINVAL: error
* OK: OK
*
****************************************************************************/
int pci_disable_bus_master(FAR struct pci_dev_s *dev);
/****************************************************************************
* Name: pci_bar_valid
*
* Description:
* Determine in if the address in the BAR is valid
*
* Input Parameters:
* dev - device
* bar_id - bar number
*
* Return value:
* -EINVAL: error
* OK: OK
*
****************************************************************************/
int pci_bar_valid(FAR struct pci_dev_s *dev, uint8_t bar_id);
/****************************************************************************
* Name: pci_bar_is_64
*
* Description:
* Determine in if the bar address is 64 bit. If it is the address includes
* the address in the next bar location.
*
* Input Parameters:
* dev - device
* bar_id - bar number
*
* Return value:
* true: 64bit address
*
****************************************************************************/
bool pci_bar_is_64(FAR struct pci_dev_s *dev, uint8_t bar_id);
/****************************************************************************
* Name: pci_bar_size
*
* Description:
* Determine the size of the address space required by the BAR
*
* Input Parameters:
* dev - device
* bar_id - bar number
*
* Return value:
* Size of address space
*
****************************************************************************/
uint64_t pci_bar_size(FAR struct pci_dev_s *dev, uint8_t bar_id);
/****************************************************************************
* Name: pci_bar_addr
*
* Description:
* Determine the size of the address space required by the BAR
*
* Input Parameters:
* dev - device
* bar_id - bar number
*
* Return value:
* full bar address
*
****************************************************************************/
uint64_t pci_bar_addr(FAR struct pci_dev_s *dev, uint8_t bar_id);
/****************************************************************************
* Name: pci_dev_dump
*
* Description:
* Dump the configuration information for the device
*
* Input Parameters:
* dev - device
*
****************************************************************************/
void pci_dev_dump(FAR struct pci_dev_s *dev);
#undef EXTERN
#if defined(__cplusplus)
}
#endif
#endif
#endif /* __INCLUDE_NUTTX_PCI_PCI_H */

View File

@ -1,352 +0,0 @@
/****************************************************************************
* 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 <nuttx/config.h>
#include <sys/types.h>
#include <stdint.h>
#include <nuttx/fs/ioctl.h>
/****************************************************************************
* 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 */

View File

@ -42,9 +42,13 @@ extern "C"
#endif
#ifdef CONFIG_VIRT_QEMU_PCI_TEST
extern struct pcie_dev_type_s pcie_type_qemu_pci_test;
extern struct pci_dev_type_s pci_type_qemu_pci_test;
#endif /* CONFIG_VIRT_QEMU_PCI_TEST */
#ifdef CONFIG_VIRT_QEMU_EDU
extern struct pci_dev_type_s pci_type_qemu_edu;
#endif /* CONFIG_VIRT_QEMU_EDU */
#undef EXTERN
#ifdef __cplusplus
}