drivers/pci/pci_qemu_test: update qemu pci test code

Change the qmeu pci test code for new pci driver framework

Signed-off-by: yangshuyong <yangshuyong@xiaomi.com>
Signed-off-by: Bowen Wang <wangbowen6@xiaomi.com>
Signed-off-by: lipengfei28 <lipengfei28@xiaomi.com>
This commit is contained in:
yangshuyong 2024-03-05 14:29:32 +08:00 committed by Xiang Xiao
parent 7cbb7d36d8
commit 9c07b369e9
8 changed files with 435 additions and 296 deletions

View File

@ -18,4 +18,11 @@ config PCI_ASSIGN_ALL_BUSES
---help--- ---help---
Assign resources to all buses. This is required for some Assign resources to all buses. This is required for some
platforms that have multiple PCI buses. platforms that have multiple PCI buses.
endif
config PCI_QEMU_TEST
bool "Driver for QEMU PCI test device"
default n
---help---
Driver for QEMU PCI test device
endif # PCI

View File

@ -19,12 +19,15 @@
############################################################################ ############################################################################
ifeq ($(CONFIG_PCI),y) ifeq ($(CONFIG_PCI),y)
CSRCS += pci.c pci_ecam.c pci_drivers.c
CSRCS += pci.c pci_ecam.c ifeq ($(CONFIG_PCI_QEMU_TEST),y)
CSRCS += pci_qemu_test.c
endif
# Include PCI device driver build support # Include PCI device driver build support
DEPPATH += --dep-path pci DEPPATH += --dep-path pci
VPATH += :pci VPATH += :pci
CFLAGS += ${INCDIR_PREFIX}$(TOPDIR)$(DELIM)drivers$(DELIM)pci CFLAGS += ${INCDIR_PREFIX}$(TOPDIR)$(DELIM)drivers$(DELIM)pci
endif endif # CONFIG_PCI

58
drivers/pci/pci_drivers.c Normal file
View File

@ -0,0 +1,58 @@
/****************************************************************************
* drivers/pci/pci_drivers.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 <debug.h>
#include <nuttx/pci/pci.h>
#include <nuttx/pci/pci_qemu_test.h>
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: pci_register_drivers
*
* Description:
* Register all the pci drivers to pci bus
*
****************************************************************************/
int pci_register_drivers(void)
{
int ret;
/* Initialization pci qemu test driver */
#ifdef CONFIG_PCI_QEMU_TEST
ret = pci_register_qemu_test_driver();
if (ret < 0)
{
pcierr("pci_register_qemu_test_driver failed, ret=%d\n", ret);
}
#endif
UNUSED(ret);
return ret;
}

350
drivers/pci/pci_qemu_test.c Normal file
View File

@ -0,0 +1,350 @@
/****************************************************************************
* drivers/pci/pci_qemu_test.c
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <assert.h>
#include <errno.h>
#include <debug.h>
#include <stdint.h>
#include <nuttx/pci/pci.h>
#include <nuttx/pci/pci_qemu_test.h>
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
/****************************************************************************
* Private Types
****************************************************************************/
struct pci_qemu_test_hdr_s
{
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_qemu_test_ops_s
{
CODE uint32_t (*read)(FAR struct pci_bus_s *bus, FAR void *addr, int size);
CODE int (*write)(FAR struct pci_bus_s *bus, FAR void *addr, uint32_t val,
int size);
};
/****************************************************************************
* Private Functions Definitions
****************************************************************************/
static uint32_t pci_qemu_test_read_mem(FAR struct pci_bus_s *bus,
FAR void *addr, int size);
static int pci_qemu_test_write_mem(FAR struct pci_bus_s *bus, FAR void *addr,
uint32_t val, int size);
static uint32_t pci_qemu_test_read_io(FAR struct pci_bus_s *bus,
FAR void *addr, int size);
static int pci_qemu_test_write_io(FAR struct pci_bus_s *bus, FAR void *addr,
uint32_t val, int size);
static int pci_qemu_test_probe(FAR struct pci_device_s *dev);
/****************************************************************************
* Private Data
****************************************************************************/
static const struct pci_qemu_test_ops_s g_pci_qemu_test_mem_ops =
{
pci_qemu_test_read_mem, /* read */
pci_qemu_test_write_mem /* write */
};
static const struct pci_qemu_test_ops_s g_pci_qemu_test_io_ops =
{
pci_qemu_test_read_io, /* read */
pci_qemu_test_write_io /* write */
};
static const struct pci_device_id_s g_pci_qemu_test_id_table[] =
{
{ PCI_DEVICE(0x1b36, 0x0005), },
{ }
};
static struct pci_driver_s g_pci_qemu_test_drv =
{
.id_table = g_pci_qemu_test_id_table,
.probe = pci_qemu_test_probe,
};
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: pci_qemu_test_read_mem
*
* Description:
* This function is used to read mem register.
*
****************************************************************************/
static uint32_t pci_qemu_test_read_mem(FAR struct pci_bus_s *bus,
FAR void *addr, int size)
{
if (size == 1)
{
return *(FAR volatile uint8_t *)addr;
}
else if (size == 2)
{
return *(FAR volatile uint16_t *)addr;
}
else if (size == 4)
{
return *(FAR volatile uint32_t *)addr;
}
DEBUGPANIC();
return 0;
}
/****************************************************************************
* Name: pci_qemu_test_write_mem
*
* Description:
* This function is used to write a value to mem register.
*
****************************************************************************/
static int pci_qemu_test_write_mem(FAR struct pci_bus_s *bus, FAR void *addr,
uint32_t val, int size)
{
if (size == 1)
{
*(FAR volatile uint8_t *)addr = (uint8_t)val;
}
else if (size == 2)
{
*(FAR volatile uint16_t *)addr = (uint16_t)val;
}
else if (size == 4)
{
*(FAR volatile uint32_t *)addr = (uint32_t)val;
}
else
{
return -EINVAL;
}
return 0;
}
/****************************************************************************
* Name: pci_qemu_test_read_io
****************************************************************************/
static uint32_t pci_qemu_test_read_io(FAR struct pci_bus_s *bus,
FAR void *addr, int size)
{
uint32_t val;
int ret;
ret = bus->ctrl->ops->read_io(bus, (uintptr_t)addr, size, &val);
if (ret < 0)
{
pcierr("Read io failed, ret=%d\n", ret);
return 0;
}
return val;
}
/****************************************************************************
* Name: pci_qemu_test_write_io
****************************************************************************/
static int pci_qemu_test_write_io(FAR struct pci_bus_s *bus, FAR void *addr,
uint32_t val, int size)
{
return bus->ctrl->ops->write_io(bus, (uintptr_t)addr, size, val);
}
/****************************************************************************
* Name: pci_qemu_test_bar
*
* Description:
* The pci bar test demo in the qemu environment
*
****************************************************************************/
static bool pci_qemu_test_bar(FAR struct pci_device_s *dev,
FAR const struct pci_qemu_test_ops_s *ops,
FAR struct pci_qemu_test_hdr_s *hdr,
uint8_t num)
{
const uint32_t write_limit = 8;
uint32_t write_cnt;
uint32_t offset;
uint32_t count;
uint32_t data;
uint8_t width;
char name[32];
int i;
pciinfo("WRITING Test# %u %p\n", num, &hdr->test);
ops->write(dev->bus, &hdr->test, num, sizeof(num));
/* 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.
*/
name[sizeof(name) - 1] = 0;
for (i = 0; i < sizeof(name); i++)
{
name[i] = (char)ops->read(dev->bus, (FAR char *)&hdr->name + i, 1);
if (name[i] == 0)
{
break;
}
}
pciinfo("Running test: %s\n", name);
count = ops->read(dev->bus, &hdr->count, sizeof(count));
pciinfo("Start Count: %04" PRIu32 "\n", count);
if (count != 0)
{
return false;
}
width = ops->read(dev->bus, &hdr->width, sizeof(width));
pciinfo("Width: %d\n", width);
if (width == 0 || width > 4)
{
return false;
}
data = ops->read(dev->bus, &hdr->data, sizeof(data));
offset = ops->read(dev->bus, &hdr->offset, sizeof(offset));
pciinfo("Data: 0x%04" PRIx32 " Offset: 0x%04" PRIx32 "\n", data, offset);
for (write_cnt = 0; write_cnt < write_limit; write_cnt++)
{
pciinfo("[%" PRIu32 "]Issuing WRITE to %p Data: 0x%04" PRIx32
" Width: %u\n",
write_cnt, (FAR char *)hdr + offset, data, width);
ops->write(dev->bus, (FAR char *)hdr + offset, data, width);
}
count = ops->read(dev->bus, &hdr->count, sizeof(count));
pciinfo("End Count: %04" PRIu32 "\n", count);
if (count == 0)
{
return true;
}
return count == write_cnt;
}
/****************************************************************************
* Name: pci_qemu_test_probe
*
* Description:
* Initialize device
*
****************************************************************************/
static int pci_qemu_test_probe(FAR struct pci_device_s *dev)
{
FAR const struct pci_qemu_test_ops_s *ops;
FAR struct pci_qemu_test_hdr_s *hdr;
unsigned long flags;
uint8_t test_cnt;
int bar;
int ret;
pciinfo("Enter pci test probe.\n");
ret = pci_enable_device(dev);
if (ret < 0)
{
pcierr("Enable device failed, ret=%d\n", ret);
return ret;
}
pci_set_master(dev);
for (bar = 0; bar < PCI_NUM_RESOURCES; bar++)
{
hdr = (FAR struct pci_qemu_test_hdr_s *)pci_map_bar(dev, bar);
if (hdr == NULL)
{
continue;
}
flags = pci_resource_flags(dev, bar);
if ((flags & PCI_RESOURCE_MEM) == PCI_RESOURCE_MEM)
{
ops = &g_pci_qemu_test_mem_ops;
}
else if ((flags & PCI_RESOURCE_IO) == PCI_RESOURCE_IO)
{
ops = &g_pci_qemu_test_io_ops;
}
for (test_cnt = 0; test_cnt < 0xff; test_cnt++)
{
if (!pci_qemu_test_bar(dev, ops, hdr, test_cnt))
{
break;
}
}
}
return 0;
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: pci_register_qemu_test_driver
*
* Description:
* Register a pci driver
*
****************************************************************************/
int pci_register_qemu_test_driver(void)
{
return pci_register_driver(&g_pci_qemu_test_drv);
}

View File

@ -12,13 +12,6 @@ menuconfig VIRT
if VIRT if VIRT
config VIRT_QEMU_PCI_TEST
bool "Driver for QEMU PCI test device"
default n
select PCI
---help---
Driver for QEMU PCI test device
config VIRT_QEMU_EDU config VIRT_QEMU_EDU
bool "Driver for QEMU EDU test device" bool "Driver for QEMU EDU test device"
default n default n

View File

@ -18,10 +18,6 @@
# #
############################################################################ ############################################################################
ifeq ($(CONFIG_VIRT_QEMU_PCI_TEST),y)
CSRCS += qemu_pci_test.c
endif
ifeq ($(CONFIG_VIRT_QEMU_EDU),y) ifeq ($(CONFIG_VIRT_QEMU_EDU),y)
CSRCS += qemu_edu.c CSRCS += qemu_edu.c
endif endif

View File

@ -1,274 +0,0 @@
/*****************************************************************************
* drivers/virt/qemu_pci_test.c
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
*****************************************************************************/
/*****************************************************************************
* Included Files
*****************************************************************************/
#include <nuttx/config.h>
#include <nuttx/arch.h>
#include <debug.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
*****************************************************************************/
/*****************************************************************************
* 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);
static int qemu_pci_test_probe(FAR struct pci_bus_s *bus,
FAR const struct pci_dev_type_s *type,
uint16_t bdf);
/*****************************************************************************
* Public Data
*****************************************************************************/
const struct pci_dev_type_s g_pci_type_qemu_pci_test =
{
.vendor = 0x1b36,
.device = 0x0005,
.class_rev = PCI_ID_ANY,
.name = "Qemu PCI test device",
.probe = qemu_pci_test_probe
};
/*****************************************************************************
* Private Types
*****************************************************************************/
struct pci_test_dev_hdr_s
{
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 g_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)
{
const int write_limit = 8;
uint32_t count;
uint32_t data;
uint32_t offset;
uint8_t width;
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;
}
/*****************************************************************************
* Name: qemu_pci_test_probe
*
* Description:
* Initialize device
*
*****************************************************************************/
static int qemu_pci_test_probe(FAR struct pci_bus_s *bus,
FAR const struct pci_dev_type_s *type,
uint16_t bdf)
{
struct pci_dev_s dev;
struct pci_test_dev_ops_s io_ops;
struct pci_test_dev_ops_s *test_ops;
struct pci_test_dev_hdr_s *test_hdr;
uint8_t bar_id;
uint32_t bar;
uint64_t bar_addr;
uint16_t test_cnt;
/* Get dev */
dev.bus = bus;
dev.type = type;
dev.bdf = bdf;
/* Get io ops */
io_ops.read = bus->ops->pci_io_read;
io_ops.write = bus->ops->pci_io_write;
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)
{
test_ops = &g_mem_ops;
/* If the BAR is MMIO the it must be mapped */
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;
}

View File

@ -1,5 +1,5 @@
/**************************************************************************** /****************************************************************************
* include/nuttx/virt/qemu_pci.h * include/nuttx/pci/pci_qemu_test.h
* *
* Licensed to the Apache Software Foundation (ASF) under one or more * Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with * contributor license agreements. See the NOTICE file distributed with
@ -18,8 +18,8 @@
* *
****************************************************************************/ ****************************************************************************/
#ifndef __INCLUDE_NUTTX_VIRT_QEMU_PCI_H #ifndef __INCLUDE_NUTTX_PCI_PCI_QEMU_TEST_H
#define __INCLUDE_NUTTX_VIRT_QEMU_PCI_H #define __INCLUDE_NUTTX_PCI_PCI_QEMU_TEST_H
/**************************************************************************** /****************************************************************************
* Included Files * Included Files
@ -27,8 +27,6 @@
#include <nuttx/config.h> #include <nuttx/config.h>
#include <stdbool.h>
/**************************************************************************** /****************************************************************************
* Public Function Prototypes * Public Function Prototypes
****************************************************************************/ ****************************************************************************/
@ -41,8 +39,16 @@ extern "C"
#define EXTERN extern #define EXTERN extern
#endif #endif
#ifdef CONFIG_VIRT_QEMU_PCI_TEST /****************************************************************************
extern const struct pci_dev_type_s g_pci_type_qemu_pci_test; * Name: pci_register_qemu_test_driver
*
* Description:
* register a pci driver
*
****************************************************************************/
#ifdef CONFIG_PCI_QEMU_TEST
int pci_register_qemu_test_driver(void);
#endif #endif
#ifdef CONFIG_VIRT_QEMU_EDU #ifdef CONFIG_VIRT_QEMU_EDU
@ -54,4 +60,4 @@ extern const struct pci_dev_type_s g_pci_type_qemu_edu;
} }
#endif #endif
#endif /* __INCLUDE_NUTTX_VIRT_QEMU_PCI_H */ #endif /* __INCLUDE_NUTTX_PCI_PCI_QEMU_TEST_H */