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:
parent
18f97bf2f8
commit
69ed5bb67d
@ -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
31
Kconfig
@ -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
|
||||
|
@ -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.
|
||||
|
76
boards/x86_64/intel64/qemu-intel64/configs/pcitest/defconfig
Normal file
76
boards/x86_64/intel64/qemu-intel64/configs/pcitest/defconfig
Normal 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
|
@ -66,8 +66,6 @@ extern "C"
|
||||
* Public Function Prototypes
|
||||
****************************************************************************/
|
||||
|
||||
void qemu_pcie_init(void);
|
||||
|
||||
#undef EXTERN
|
||||
#if defined(__cplusplus)
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -57,5 +57,10 @@ int qemu_bringup(void)
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_QEMU_PCI
|
||||
/* Initialization of system */
|
||||
|
||||
qemu_pci_init();
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
@ -48,6 +48,8 @@
|
||||
* Public Function Prototypes
|
||||
****************************************************************************/
|
||||
|
||||
void qemu_pci_init(void);
|
||||
|
||||
int qemu_bringup(void);
|
||||
|
||||
#endif /* __ASSEMBLY__ */
|
||||
|
226
boards/x86_64/intel64/qemu-intel64/src/qemu_pci.c
Normal file
226
boards/x86_64/intel64/qemu-intel64/src/qemu_pci.c
Normal 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);
|
||||
}
|
@ -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);
|
||||
}
|
@ -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 */
|
@ -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"
|
||||
|
@ -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
11
drivers/pci/Kconfig
Normal 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.
|
@ -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
792
drivers/pci/pci.c
Normal 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++;
|
||||
}
|
||||
}
|
@ -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
|
@ -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;
|
||||
}
|
@ -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
|
||||
|
@ -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
448
drivers/virt/qemu_edu.c
Normal 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
|
||||
};
|
@ -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,
|
||||
|
@ -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
458
include/nuttx/pci/pci.h
Normal 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 */
|
@ -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 */
|
@ -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
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user