Commit 0c38011a authored by Bjorn Helgaas's avatar Bjorn Helgaas

Merge branch 'remotes/lorenzo/pci/dwc'

  - Add Kirin MSI support (Xiaowei Song)

  - Drop unnecessary root_bus_nr setting from exynos, imx6, keystone,
    armada8k, artpec6, designware-plat, histb, qcom, spear13xx (Shawn Guo)

  - Move link notification settings from DesignWare core to individual
    drivers (Gustavo Pimentel)

  - Add endpoint library MSI-X interfaces (Gustavo Pimentel)

  - Correct signature of endpoint library IRQ interfaces (Gustavo Pimentel)

  - Add DesignWare endpoint library MSI-X callbacks (Gustavo Pimentel)

  - Add endpoint library MSI-X test support (Gustavo Pimentel)

* remotes/lorenzo/pci/dwc:
  PCI: endpoint: Add MSI set maximum restriction
  tools: PCI: Add MSI-X support
  pci_endpoint_test: Add 2 ioctl commands
  pci-epf-test/pci_endpoint_test: Add MSI-X support
  pci-epf-test/pci_endpoint_test: Use irq_type module parameter
  pci-epf-test/pci_endpoint_test: Cleanup PCI_ENDPOINT_TEST memspace
  PCI: dwc: Add legacy interrupt callback handler
  PCI: dwc: Rework MSI callbacks handler
  PCI: dwc: Add MSI-X callbacks handler
  PCI: Update xxx_pcie_ep_raise_irq() and pci_epc_raise_irq() signatures
  PCI: endpoint: Add MSI-X interfaces
  PCI: dwc: Fix EP link notification implementation
  PCI: spear13xx: Drop unnecessary root_bus_nr setting
  PCI: qcom: Drop unnecessary root_bus_nr setting
  PCI: histb: Drop unnecessary root_bus_nr setting
  PCI: designware-plat: Drop unnecessary root_bus_nr setting
  PCI: artpec6: Drop unnecessary root_bus_nr setting
  PCI: armada8k: Drop unnecessary root_bus_nr setting
  PCI: keystone: Drop unnecessary root_bus_nr setting
  PCI: imx6: Drop unnecessary root_bus_nr setting
  PCI: exynos: Drop unnecessary root_bus_nr setting
  PCI: kirin: Add MSI support
parents 37f0e311 15c972df
...@@ -15,3 +15,5 @@ subsys_id : don't care ...@@ -15,3 +15,5 @@ subsys_id : don't care
interrupt_pin : Should be 1 - INTA, 2 - INTB, 3 - INTC, 4 -INTD interrupt_pin : Should be 1 - INTA, 2 - INTB, 3 - INTC, 4 -INTD
msi_interrupts : Should be 1 to 32 depending on the number of MSI interrupts msi_interrupts : Should be 1 to 32 depending on the number of MSI interrupts
to test to test
msix_interrupts : Should be 1 to 2048 depending on the number of MSI-X
interrupts to test
...@@ -44,7 +44,7 @@ by the PCI controller driver. ...@@ -44,7 +44,7 @@ by the PCI controller driver.
* clear_bar: ops to reset the BAR * clear_bar: ops to reset the BAR
* alloc_addr_space: ops to allocate in PCI controller address space * alloc_addr_space: ops to allocate in PCI controller address space
* free_addr_space: ops to free the allocated address space * free_addr_space: ops to free the allocated address space
* raise_irq: ops to raise a legacy or MSI interrupt * raise_irq: ops to raise a legacy, MSI or MSI-X interrupt
* start: ops to start the PCI link * start: ops to start the PCI link
* stop: ops to stop the PCI link * stop: ops to stop the PCI link
...@@ -96,7 +96,7 @@ by the PCI endpoint function driver. ...@@ -96,7 +96,7 @@ by the PCI endpoint function driver.
*) pci_epc_raise_irq() *) pci_epc_raise_irq()
The PCI endpoint function driver should use pci_epc_raise_irq() to raise The PCI endpoint function driver should use pci_epc_raise_irq() to raise
Legacy Interrupt or MSI Interrupt. Legacy Interrupt, MSI or MSI-X Interrupt.
*) pci_epc_mem_alloc_addr() *) pci_epc_mem_alloc_addr()
......
...@@ -20,6 +20,8 @@ The PCI endpoint test device has the following registers: ...@@ -20,6 +20,8 @@ The PCI endpoint test device has the following registers:
5) PCI_ENDPOINT_TEST_DST_ADDR 5) PCI_ENDPOINT_TEST_DST_ADDR
6) PCI_ENDPOINT_TEST_SIZE 6) PCI_ENDPOINT_TEST_SIZE
7) PCI_ENDPOINT_TEST_CHECKSUM 7) PCI_ENDPOINT_TEST_CHECKSUM
8) PCI_ENDPOINT_TEST_IRQ_TYPE
9) PCI_ENDPOINT_TEST_IRQ_NUMBER
*) PCI_ENDPOINT_TEST_MAGIC *) PCI_ENDPOINT_TEST_MAGIC
...@@ -34,10 +36,10 @@ that the endpoint device must perform. ...@@ -34,10 +36,10 @@ that the endpoint device must perform.
Bitfield Description: Bitfield Description:
Bit 0 : raise legacy IRQ Bit 0 : raise legacy IRQ
Bit 1 : raise MSI IRQ Bit 1 : raise MSI IRQ
Bit 2 - 7 : MSI interrupt number Bit 2 : raise MSI-X IRQ
Bit 8 : read command (read data from RC buffer) Bit 3 : read command (read data from RC buffer)
Bit 9 : write command (write data to RC buffer) Bit 4 : write command (write data to RC buffer)
Bit 10 : copy command (copy data from one RC buffer to another Bit 5 : copy command (copy data from one RC buffer to another
RC buffer) RC buffer)
*) PCI_ENDPOINT_TEST_STATUS *) PCI_ENDPOINT_TEST_STATUS
...@@ -64,3 +66,22 @@ COPY/READ command. ...@@ -64,3 +66,22 @@ COPY/READ command.
This register contains the destination address (RC buffer address) for This register contains the destination address (RC buffer address) for
the COPY/WRITE command. the COPY/WRITE command.
*) PCI_ENDPOINT_TEST_IRQ_TYPE
This register contains the interrupt type (Legacy/MSI) triggered
for the READ/WRITE/COPY and raise IRQ (Legacy/MSI) commands.
Possible types:
- Legacy : 0
- MSI : 1
- MSI-X : 2
*) PCI_ENDPOINT_TEST_IRQ_NUMBER
This register contains the triggered ID interrupt.
Admissible values:
- Legacy : 0
- MSI : [1 .. 32]
- MSI-X : [1 .. 2048]
...@@ -45,9 +45,9 @@ The PCI endpoint framework populates the directory with the following ...@@ -45,9 +45,9 @@ The PCI endpoint framework populates the directory with the following
configurable fields. configurable fields.
# ls functions/pci_epf_test/func1 # ls functions/pci_epf_test/func1
baseclass_code interrupt_pin revid subsys_vendor_id baseclass_code interrupt_pin progif_code subsys_id
cache_line_size msi_interrupts subclass_code vendorid cache_line_size msi_interrupts revid subsys_vendorid
deviceid progif_code subsys_id deviceid msix_interrupts subclass_code vendorid
The PCI endpoint function driver populates these entries with default values The PCI endpoint function driver populates these entries with default values
when the device is bound to the driver. The pci-epf-test driver populates when the device is bound to the driver. The pci-epf-test driver populates
...@@ -67,6 +67,7 @@ device, the following commands can be used. ...@@ -67,6 +67,7 @@ device, the following commands can be used.
# echo 0x104c > functions/pci_epf_test/func1/vendorid # echo 0x104c > functions/pci_epf_test/func1/vendorid
# echo 0xb500 > functions/pci_epf_test/func1/deviceid # echo 0xb500 > functions/pci_epf_test/func1/deviceid
# echo 16 > functions/pci_epf_test/func1/msi_interrupts # echo 16 > functions/pci_epf_test/func1/msi_interrupts
# echo 8 > functions/pci_epf_test/func1/msix_interrupts
1.5 Binding pci-epf-test Device to EP Controller 1.5 Binding pci-epf-test Device to EP Controller
...@@ -120,7 +121,9 @@ following commands. ...@@ -120,7 +121,9 @@ following commands.
Interrupt tests Interrupt tests
SET IRQ TYPE TO LEGACY: OKAY
LEGACY IRQ: NOT OKAY LEGACY IRQ: NOT OKAY
SET IRQ TYPE TO MSI: OKAY
MSI1: OKAY MSI1: OKAY
MSI2: OKAY MSI2: OKAY
MSI3: OKAY MSI3: OKAY
...@@ -153,9 +156,30 @@ following commands. ...@@ -153,9 +156,30 @@ following commands.
MSI30: NOT OKAY MSI30: NOT OKAY
MSI31: NOT OKAY MSI31: NOT OKAY
MSI32: NOT OKAY MSI32: NOT OKAY
SET IRQ TYPE TO MSI-X: OKAY
MSI-X1: OKAY
MSI-X2: OKAY
MSI-X3: OKAY
MSI-X4: OKAY
MSI-X5: OKAY
MSI-X6: OKAY
MSI-X7: OKAY
MSI-X8: OKAY
MSI-X9: NOT OKAY
MSI-X10: NOT OKAY
MSI-X11: NOT OKAY
MSI-X12: NOT OKAY
MSI-X13: NOT OKAY
MSI-X14: NOT OKAY
MSI-X15: NOT OKAY
MSI-X16: NOT OKAY
[...]
MSI-X2047: NOT OKAY
MSI-X2048: NOT OKAY
Read Tests Read Tests
SET IRQ TYPE TO MSI: OKAY
READ ( 1 bytes): OKAY READ ( 1 bytes): OKAY
READ ( 1024 bytes): OKAY READ ( 1024 bytes): OKAY
READ ( 1025 bytes): OKAY READ ( 1025 bytes): OKAY
......
...@@ -166,6 +166,7 @@ Code Seq#(hex) Include File Comments ...@@ -166,6 +166,7 @@ Code Seq#(hex) Include File Comments
'P' all linux/soundcard.h conflict! 'P' all linux/soundcard.h conflict!
'P' 60-6F sound/sscape_ioctl.h conflict! 'P' 60-6F sound/sscape_ioctl.h conflict!
'P' 00-0F drivers/usb/class/usblp.c conflict! 'P' 00-0F drivers/usb/class/usblp.c conflict!
'P' 01-09 drivers/misc/pci_endpoint_test.c conflict!
'Q' all linux/soundcard.h 'Q' all linux/soundcard.h
'R' 00-1F linux/random.h conflict! 'R' 00-1F linux/random.h conflict!
'R' 01 linux/rfkill.h conflict! 'R' 01 linux/rfkill.h conflict!
......
...@@ -10,6 +10,7 @@ The PCI driver for the test device performs the following tests ...@@ -10,6 +10,7 @@ The PCI driver for the test device performs the following tests
*) verifying addresses programmed in BAR *) verifying addresses programmed in BAR
*) raise legacy IRQ *) raise legacy IRQ
*) raise MSI IRQ *) raise MSI IRQ
*) raise MSI-X IRQ
*) read data *) read data
*) write data *) write data
*) copy data *) copy data
...@@ -25,6 +26,11 @@ ioctl ...@@ -25,6 +26,11 @@ ioctl
PCITEST_LEGACY_IRQ: Tests legacy IRQ PCITEST_LEGACY_IRQ: Tests legacy IRQ
PCITEST_MSI: Tests message signalled interrupts. The MSI number PCITEST_MSI: Tests message signalled interrupts. The MSI number
to be tested should be passed as argument. to be tested should be passed as argument.
PCITEST_MSIX: Tests message signalled interrupts. The MSI-X number
to be tested should be passed as argument.
PCITEST_SET_IRQTYPE: Changes driver IRQ type configuration. The IRQ type
should be passed as argument (0: Legacy, 1:MSI, 2:MSI-X).
PCITEST_GET_IRQTYPE: Gets driver IRQ type configuration.
PCITEST_WRITE: Perform write tests. The size of the buffer should be passed PCITEST_WRITE: Perform write tests. The size of the buffer should be passed
as argument. as argument.
PCITEST_READ: Perform read tests. The size of the buffer should be passed PCITEST_READ: Perform read tests. The size of the buffer should be passed
......
...@@ -35,38 +35,45 @@ ...@@ -35,38 +35,45 @@
#include <uapi/linux/pcitest.h> #include <uapi/linux/pcitest.h>
#define DRV_MODULE_NAME "pci-endpoint-test" #define DRV_MODULE_NAME "pci-endpoint-test"
#define PCI_ENDPOINT_TEST_MAGIC 0x0 #define IRQ_TYPE_UNDEFINED -1
#define IRQ_TYPE_LEGACY 0
#define PCI_ENDPOINT_TEST_COMMAND 0x4 #define IRQ_TYPE_MSI 1
#define COMMAND_RAISE_LEGACY_IRQ BIT(0) #define IRQ_TYPE_MSIX 2
#define COMMAND_RAISE_MSI_IRQ BIT(1)
#define MSI_NUMBER_SHIFT 2 #define PCI_ENDPOINT_TEST_MAGIC 0x0
/* 6 bits for MSI number */
#define COMMAND_READ BIT(8) #define PCI_ENDPOINT_TEST_COMMAND 0x4
#define COMMAND_WRITE BIT(9) #define COMMAND_RAISE_LEGACY_IRQ BIT(0)
#define COMMAND_COPY BIT(10) #define COMMAND_RAISE_MSI_IRQ BIT(1)
#define COMMAND_RAISE_MSIX_IRQ BIT(2)
#define PCI_ENDPOINT_TEST_STATUS 0x8 #define COMMAND_READ BIT(3)
#define STATUS_READ_SUCCESS BIT(0) #define COMMAND_WRITE BIT(4)
#define STATUS_READ_FAIL BIT(1) #define COMMAND_COPY BIT(5)
#define STATUS_WRITE_SUCCESS BIT(2)
#define STATUS_WRITE_FAIL BIT(3) #define PCI_ENDPOINT_TEST_STATUS 0x8
#define STATUS_COPY_SUCCESS BIT(4) #define STATUS_READ_SUCCESS BIT(0)
#define STATUS_COPY_FAIL BIT(5) #define STATUS_READ_FAIL BIT(1)
#define STATUS_IRQ_RAISED BIT(6) #define STATUS_WRITE_SUCCESS BIT(2)
#define STATUS_SRC_ADDR_INVALID BIT(7) #define STATUS_WRITE_FAIL BIT(3)
#define STATUS_DST_ADDR_INVALID BIT(8) #define STATUS_COPY_SUCCESS BIT(4)
#define STATUS_COPY_FAIL BIT(5)
#define PCI_ENDPOINT_TEST_LOWER_SRC_ADDR 0xc #define STATUS_IRQ_RAISED BIT(6)
#define STATUS_SRC_ADDR_INVALID BIT(7)
#define STATUS_DST_ADDR_INVALID BIT(8)
#define PCI_ENDPOINT_TEST_LOWER_SRC_ADDR 0x0c
#define PCI_ENDPOINT_TEST_UPPER_SRC_ADDR 0x10 #define PCI_ENDPOINT_TEST_UPPER_SRC_ADDR 0x10
#define PCI_ENDPOINT_TEST_LOWER_DST_ADDR 0x14 #define PCI_ENDPOINT_TEST_LOWER_DST_ADDR 0x14
#define PCI_ENDPOINT_TEST_UPPER_DST_ADDR 0x18 #define PCI_ENDPOINT_TEST_UPPER_DST_ADDR 0x18
#define PCI_ENDPOINT_TEST_SIZE 0x1c #define PCI_ENDPOINT_TEST_SIZE 0x1c
#define PCI_ENDPOINT_TEST_CHECKSUM 0x20 #define PCI_ENDPOINT_TEST_CHECKSUM 0x20
#define PCI_ENDPOINT_TEST_IRQ_TYPE 0x24
#define PCI_ENDPOINT_TEST_IRQ_NUMBER 0x28
static DEFINE_IDA(pci_endpoint_test_ida); static DEFINE_IDA(pci_endpoint_test_ida);
...@@ -77,6 +84,10 @@ static bool no_msi; ...@@ -77,6 +84,10 @@ static bool no_msi;
module_param(no_msi, bool, 0444); module_param(no_msi, bool, 0444);
MODULE_PARM_DESC(no_msi, "Disable MSI interrupt in pci_endpoint_test"); MODULE_PARM_DESC(no_msi, "Disable MSI interrupt in pci_endpoint_test");
static int irq_type = IRQ_TYPE_MSI;
module_param(irq_type, int, 0444);
MODULE_PARM_DESC(irq_type, "IRQ mode selection in pci_endpoint_test (0 - Legacy, 1 - MSI, 2 - MSI-X)");
enum pci_barno { enum pci_barno {
BAR_0, BAR_0,
BAR_1, BAR_1,
...@@ -103,7 +114,7 @@ struct pci_endpoint_test { ...@@ -103,7 +114,7 @@ struct pci_endpoint_test {
struct pci_endpoint_test_data { struct pci_endpoint_test_data {
enum pci_barno test_reg_bar; enum pci_barno test_reg_bar;
size_t alignment; size_t alignment;
bool no_msi; int irq_type;
}; };
static inline u32 pci_endpoint_test_readl(struct pci_endpoint_test *test, static inline u32 pci_endpoint_test_readl(struct pci_endpoint_test *test,
...@@ -147,6 +158,100 @@ static irqreturn_t pci_endpoint_test_irqhandler(int irq, void *dev_id) ...@@ -147,6 +158,100 @@ static irqreturn_t pci_endpoint_test_irqhandler(int irq, void *dev_id)
return IRQ_HANDLED; return IRQ_HANDLED;
} }
static void pci_endpoint_test_free_irq_vectors(struct pci_endpoint_test *test)
{
struct pci_dev *pdev = test->pdev;
pci_free_irq_vectors(pdev);
}
static bool pci_endpoint_test_alloc_irq_vectors(struct pci_endpoint_test *test,
int type)
{
int irq = -1;
struct pci_dev *pdev = test->pdev;
struct device *dev = &pdev->dev;
bool res = true;
switch (type) {
case IRQ_TYPE_LEGACY:
irq = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_LEGACY);
if (irq < 0)
dev_err(dev, "Failed to get Legacy interrupt\n");
break;
case IRQ_TYPE_MSI:
irq = pci_alloc_irq_vectors(pdev, 1, 32, PCI_IRQ_MSI);
if (irq < 0)
dev_err(dev, "Failed to get MSI interrupts\n");
break;
case IRQ_TYPE_MSIX:
irq = pci_alloc_irq_vectors(pdev, 1, 2048, PCI_IRQ_MSIX);
if (irq < 0)
dev_err(dev, "Failed to get MSI-X interrupts\n");
break;
default:
dev_err(dev, "Invalid IRQ type selected\n");
}
if (irq < 0) {
irq = 0;
res = false;
}
test->num_irqs = irq;
return res;
}
static void pci_endpoint_test_release_irq(struct pci_endpoint_test *test)
{
int i;
struct pci_dev *pdev = test->pdev;
struct device *dev = &pdev->dev;
for (i = 0; i < test->num_irqs; i++)
devm_free_irq(dev, pci_irq_vector(pdev, i), test);
test->num_irqs = 0;
}
static bool pci_endpoint_test_request_irq(struct pci_endpoint_test *test)
{
int i;
int err;
struct pci_dev *pdev = test->pdev;
struct device *dev = &pdev->dev;
for (i = 0; i < test->num_irqs; i++) {
err = devm_request_irq(dev, pci_irq_vector(pdev, i),
pci_endpoint_test_irqhandler,
IRQF_SHARED, DRV_MODULE_NAME, test);
if (err)
goto fail;
}
return true;
fail:
switch (irq_type) {
case IRQ_TYPE_LEGACY:
dev_err(dev, "Failed to request IRQ %d for Legacy\n",
pci_irq_vector(pdev, i));
break;
case IRQ_TYPE_MSI:
dev_err(dev, "Failed to request IRQ %d for MSI %d\n",
pci_irq_vector(pdev, i),
i + 1);
break;
case IRQ_TYPE_MSIX:
dev_err(dev, "Failed to request IRQ %d for MSI-X %d\n",
pci_irq_vector(pdev, i),
i + 1);
break;
}
return false;
}
static bool pci_endpoint_test_bar(struct pci_endpoint_test *test, static bool pci_endpoint_test_bar(struct pci_endpoint_test *test,
enum pci_barno barno) enum pci_barno barno)
{ {
...@@ -179,6 +284,9 @@ static bool pci_endpoint_test_legacy_irq(struct pci_endpoint_test *test) ...@@ -179,6 +284,9 @@ static bool pci_endpoint_test_legacy_irq(struct pci_endpoint_test *test)
{ {
u32 val; u32 val;
pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_IRQ_TYPE,
IRQ_TYPE_LEGACY);
pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_IRQ_NUMBER, 0);
pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_COMMAND, pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_COMMAND,
COMMAND_RAISE_LEGACY_IRQ); COMMAND_RAISE_LEGACY_IRQ);
val = wait_for_completion_timeout(&test->irq_raised, val = wait_for_completion_timeout(&test->irq_raised,
...@@ -190,14 +298,18 @@ static bool pci_endpoint_test_legacy_irq(struct pci_endpoint_test *test) ...@@ -190,14 +298,18 @@ static bool pci_endpoint_test_legacy_irq(struct pci_endpoint_test *test)
} }
static bool pci_endpoint_test_msi_irq(struct pci_endpoint_test *test, static bool pci_endpoint_test_msi_irq(struct pci_endpoint_test *test,
u8 msi_num) u16 msi_num, bool msix)
{ {
u32 val; u32 val;
struct pci_dev *pdev = test->pdev; struct pci_dev *pdev = test->pdev;
pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_IRQ_TYPE,
msix == false ? IRQ_TYPE_MSI :
IRQ_TYPE_MSIX);
pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_IRQ_NUMBER, msi_num);
pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_COMMAND, pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_COMMAND,
msi_num << MSI_NUMBER_SHIFT | msix == false ? COMMAND_RAISE_MSI_IRQ :
COMMAND_RAISE_MSI_IRQ); COMMAND_RAISE_MSIX_IRQ);
val = wait_for_completion_timeout(&test->irq_raised, val = wait_for_completion_timeout(&test->irq_raised,
msecs_to_jiffies(1000)); msecs_to_jiffies(1000));
if (!val) if (!val)
...@@ -230,6 +342,11 @@ static bool pci_endpoint_test_copy(struct pci_endpoint_test *test, size_t size) ...@@ -230,6 +342,11 @@ static bool pci_endpoint_test_copy(struct pci_endpoint_test *test, size_t size)
if (size > SIZE_MAX - alignment) if (size > SIZE_MAX - alignment)
goto err; goto err;
if (irq_type < IRQ_TYPE_LEGACY || irq_type > IRQ_TYPE_MSIX) {
dev_err(dev, "Invalid IRQ type option\n");
goto err;
}
orig_src_addr = dma_alloc_coherent(dev, size + alignment, orig_src_addr = dma_alloc_coherent(dev, size + alignment,
&orig_src_phys_addr, GFP_KERNEL); &orig_src_phys_addr, GFP_KERNEL);
if (!orig_src_addr) { if (!orig_src_addr) {
...@@ -281,8 +398,10 @@ static bool pci_endpoint_test_copy(struct pci_endpoint_test *test, size_t size) ...@@ -281,8 +398,10 @@ static bool pci_endpoint_test_copy(struct pci_endpoint_test *test, size_t size)
pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_SIZE, pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_SIZE,
size); size);
pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_IRQ_TYPE, irq_type);
pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_IRQ_NUMBER, 1);
pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_COMMAND, pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_COMMAND,
1 << MSI_NUMBER_SHIFT | COMMAND_COPY); COMMAND_COPY);
wait_for_completion(&test->irq_raised); wait_for_completion(&test->irq_raised);
...@@ -318,6 +437,11 @@ static bool pci_endpoint_test_write(struct pci_endpoint_test *test, size_t size) ...@@ -318,6 +437,11 @@ static bool pci_endpoint_test_write(struct pci_endpoint_test *test, size_t size)
if (size > SIZE_MAX - alignment) if (size > SIZE_MAX - alignment)
goto err; goto err;
if (irq_type < IRQ_TYPE_LEGACY || irq_type > IRQ_TYPE_MSIX) {
dev_err(dev, "Invalid IRQ type option\n");
goto err;
}
orig_addr = dma_alloc_coherent(dev, size + alignment, &orig_phys_addr, orig_addr = dma_alloc_coherent(dev, size + alignment, &orig_phys_addr,
GFP_KERNEL); GFP_KERNEL);
if (!orig_addr) { if (!orig_addr) {
...@@ -348,8 +472,10 @@ static bool pci_endpoint_test_write(struct pci_endpoint_test *test, size_t size) ...@@ -348,8 +472,10 @@ static bool pci_endpoint_test_write(struct pci_endpoint_test *test, size_t size)
pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_SIZE, size); pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_SIZE, size);
pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_IRQ_TYPE, irq_type);
pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_IRQ_NUMBER, 1);
pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_COMMAND, pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_COMMAND,
1 << MSI_NUMBER_SHIFT | COMMAND_READ); COMMAND_READ);
wait_for_completion(&test->irq_raised); wait_for_completion(&test->irq_raised);
...@@ -379,6 +505,11 @@ static bool pci_endpoint_test_read(struct pci_endpoint_test *test, size_t size) ...@@ -379,6 +505,11 @@ static bool pci_endpoint_test_read(struct pci_endpoint_test *test, size_t size)
if (size > SIZE_MAX - alignment) if (size > SIZE_MAX - alignment)
goto err; goto err;
if (irq_type < IRQ_TYPE_LEGACY || irq_type > IRQ_TYPE_MSIX) {
dev_err(dev, "Invalid IRQ type option\n");
goto err;
}
orig_addr = dma_alloc_coherent(dev, size + alignment, &orig_phys_addr, orig_addr = dma_alloc_coherent(dev, size + alignment, &orig_phys_addr,
GFP_KERNEL); GFP_KERNEL);
if (!orig_addr) { if (!orig_addr) {
...@@ -403,8 +534,10 @@ static bool pci_endpoint_test_read(struct pci_endpoint_test *test, size_t size) ...@@ -403,8 +534,10 @@ static bool pci_endpoint_test_read(struct pci_endpoint_test *test, size_t size)
pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_SIZE, size); pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_SIZE, size);
pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_IRQ_TYPE, irq_type);
pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_IRQ_NUMBER, 1);
pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_COMMAND, pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_COMMAND,
1 << MSI_NUMBER_SHIFT | COMMAND_WRITE); COMMAND_WRITE);
wait_for_completion(&test->irq_raised); wait_for_completion(&test->irq_raised);
...@@ -417,6 +550,38 @@ static bool pci_endpoint_test_read(struct pci_endpoint_test *test, size_t size) ...@@ -417,6 +550,38 @@ static bool pci_endpoint_test_read(struct pci_endpoint_test *test, size_t size)
return ret; return ret;
} }
static bool pci_endpoint_test_set_irq(struct pci_endpoint_test *test,
int req_irq_type)
{
struct pci_dev *pdev = test->pdev;
struct device *dev = &pdev->dev;
if (req_irq_type < IRQ_TYPE_LEGACY || req_irq_type > IRQ_TYPE_MSIX) {
dev_err(dev, "Invalid IRQ type option\n");
return false;
}
if (irq_type == req_irq_type)
return true;
pci_endpoint_test_release_irq(test);
pci_endpoint_test_free_irq_vectors(test);
if (!pci_endpoint_test_alloc_irq_vectors(test, req_irq_type))
goto err;
if (!pci_endpoint_test_request_irq(test))
goto err;
irq_type = req_irq_type;
return true;
err:
pci_endpoint_test_free_irq_vectors(test);
irq_type = IRQ_TYPE_UNDEFINED;
return false;
}
static long pci_endpoint_test_ioctl(struct file *file, unsigned int cmd, static long pci_endpoint_test_ioctl(struct file *file, unsigned int cmd,
unsigned long arg) unsigned long arg)
{ {
...@@ -436,7 +601,8 @@ static long pci_endpoint_test_ioctl(struct file *file, unsigned int cmd, ...@@ -436,7 +601,8 @@ static long pci_endpoint_test_ioctl(struct file *file, unsigned int cmd,
ret = pci_endpoint_test_legacy_irq(test); ret = pci_endpoint_test_legacy_irq(test);
break; break;
case PCITEST_MSI: case PCITEST_MSI:
ret = pci_endpoint_test_msi_irq(test, arg); case PCITEST_MSIX:
ret = pci_endpoint_test_msi_irq(test, arg, cmd == PCITEST_MSIX);
break; break;
case PCITEST_WRITE: case PCITEST_WRITE:
ret = pci_endpoint_test_write(test, arg); ret = pci_endpoint_test_write(test, arg);
...@@ -447,6 +613,12 @@ static long pci_endpoint_test_ioctl(struct file *file, unsigned int cmd, ...@@ -447,6 +613,12 @@ static long pci_endpoint_test_ioctl(struct file *file, unsigned int cmd,
case PCITEST_COPY: case PCITEST_COPY:
ret = pci_endpoint_test_copy(test, arg); ret = pci_endpoint_test_copy(test, arg);
break; break;
case PCITEST_SET_IRQTYPE:
ret = pci_endpoint_test_set_irq(test, arg);
break;
case PCITEST_GET_IRQTYPE:
ret = irq_type;
break;
} }
ret: ret:
...@@ -462,9 +634,7 @@ static const struct file_operations pci_endpoint_test_fops = { ...@@ -462,9 +634,7 @@ static const struct file_operations pci_endpoint_test_fops = {
static int pci_endpoint_test_probe(struct pci_dev *pdev, static int pci_endpoint_test_probe(struct pci_dev *pdev,
const struct pci_device_id *ent) const struct pci_device_id *ent)
{ {
int i;
int err; int err;
int irq = 0;
int id; int id;
char name[20]; char name[20];
enum pci_barno bar; enum pci_barno bar;
...@@ -486,11 +656,14 @@ static int pci_endpoint_test_probe(struct pci_dev *pdev, ...@@ -486,11 +656,14 @@ static int pci_endpoint_test_probe(struct pci_dev *pdev,
test->alignment = 0; test->alignment = 0;
test->pdev = pdev; test->pdev = pdev;
if (no_msi)
irq_type = IRQ_TYPE_LEGACY;
data = (struct pci_endpoint_test_data *)ent->driver_data; data = (struct pci_endpoint_test_data *)ent->driver_data;
if (data) { if (data) {
test_reg_bar = data->test_reg_bar; test_reg_bar = data->test_reg_bar;
test->alignment = data->alignment; test->alignment = data->alignment;
no_msi = data->no_msi; irq_type = data->irq_type;
} }
init_completion(&test->irq_raised); init_completion(&test->irq_raised);
...@@ -510,28 +683,11 @@ static int pci_endpoint_test_probe(struct pci_dev *pdev, ...@@ -510,28 +683,11 @@ static int pci_endpoint_test_probe(struct pci_dev *pdev,
pci_set_master(pdev); pci_set_master(pdev);
if (!no_msi) { if (!pci_endpoint_test_alloc_irq_vectors(test, irq_type))
irq = pci_alloc_irq_vectors(pdev, 1, 32, PCI_IRQ_MSI); goto err_disable_irq;
if (irq < 0)
dev_err(dev, "Failed to get MSI interrupts\n");
test->num_irqs = irq;
}
err = devm_request_irq(dev, pdev->irq, pci_endpoint_test_irqhandler,
IRQF_SHARED, DRV_MODULE_NAME, test);
if (err) {
dev_err(dev, "Failed to request IRQ %d\n", pdev->irq);
goto err_disable_msi;
}
for (i = 1; i < irq; i++) { if (!pci_endpoint_test_request_irq(test))
err = devm_request_irq(dev, pci_irq_vector(pdev, i), goto err_disable_irq;
pci_endpoint_test_irqhandler,
IRQF_SHARED, DRV_MODULE_NAME, test);
if (err)
dev_err(dev, "failed to request IRQ %d for MSI %d\n",
pci_irq_vector(pdev, i), i + 1);
}
for (bar = BAR_0; bar <= BAR_5; bar++) { for (bar = BAR_0; bar <= BAR_5; bar++) {
if (pci_resource_flags(pdev, bar) & IORESOURCE_MEM) { if (pci_resource_flags(pdev, bar) & IORESOURCE_MEM) {
...@@ -590,12 +746,10 @@ static int pci_endpoint_test_probe(struct pci_dev *pdev, ...@@ -590,12 +746,10 @@ static int pci_endpoint_test_probe(struct pci_dev *pdev,
if (test->bar[bar]) if (test->bar[bar])
pci_iounmap(pdev, test->bar[bar]); pci_iounmap(pdev, test->bar[bar]);
} }
pci_endpoint_test_release_irq(test);
for (i = 0; i < irq; i++) err_disable_irq:
devm_free_irq(&pdev->dev, pci_irq_vector(pdev, i), test); pci_endpoint_test_free_irq_vectors(test);
err_disable_msi:
pci_disable_msi(pdev);
pci_release_regions(pdev); pci_release_regions(pdev);
err_disable_pdev: err_disable_pdev:
...@@ -607,7 +761,6 @@ static int pci_endpoint_test_probe(struct pci_dev *pdev, ...@@ -607,7 +761,6 @@ static int pci_endpoint_test_probe(struct pci_dev *pdev,
static void pci_endpoint_test_remove(struct pci_dev *pdev) static void pci_endpoint_test_remove(struct pci_dev *pdev)
{ {
int id; int id;
int i;
enum pci_barno bar; enum pci_barno bar;
struct pci_endpoint_test *test = pci_get_drvdata(pdev); struct pci_endpoint_test *test = pci_get_drvdata(pdev);
struct miscdevice *misc_device = &test->miscdev; struct miscdevice *misc_device = &test->miscdev;
...@@ -624,9 +777,10 @@ static void pci_endpoint_test_remove(struct pci_dev *pdev) ...@@ -624,9 +777,10 @@ static void pci_endpoint_test_remove(struct pci_dev *pdev)
if (test->bar[bar]) if (test->bar[bar])
pci_iounmap(pdev, test->bar[bar]); pci_iounmap(pdev, test->bar[bar]);
} }
for (i = 0; i < test->num_irqs; i++)
devm_free_irq(&pdev->dev, pci_irq_vector(pdev, i), test); pci_endpoint_test_release_irq(test);
pci_disable_msi(pdev); pci_endpoint_test_free_irq_vectors(test);
pci_release_regions(pdev); pci_release_regions(pdev);
pci_disable_device(pdev); pci_disable_device(pdev);
} }
......
...@@ -370,7 +370,7 @@ static void dra7xx_pcie_raise_msi_irq(struct dra7xx_pcie *dra7xx, ...@@ -370,7 +370,7 @@ static void dra7xx_pcie_raise_msi_irq(struct dra7xx_pcie *dra7xx,
} }
static int dra7xx_pcie_raise_irq(struct dw_pcie_ep *ep, u8 func_no, static int dra7xx_pcie_raise_irq(struct dw_pcie_ep *ep, u8 func_no,
enum pci_epc_irq_type type, u8 interrupt_num) enum pci_epc_irq_type type, u16 interrupt_num)
{ {
struct dw_pcie *pci = to_dw_pcie_from_ep(ep); struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
struct dra7xx_pcie *dra7xx = to_dra7xx_pcie(pci); struct dra7xx_pcie *dra7xx = to_dra7xx_pcie(pci);
......
...@@ -421,7 +421,6 @@ static int __init exynos_add_pcie_port(struct exynos_pcie *ep, ...@@ -421,7 +421,6 @@ static int __init exynos_add_pcie_port(struct exynos_pcie *ep,
} }
} }
pp->root_bus_nr = -1;
pp->ops = &exynos_pcie_host_ops; pp->ops = &exynos_pcie_host_ops;
ret = dw_pcie_host_init(pp); ret = dw_pcie_host_init(pp);
......
...@@ -667,7 +667,6 @@ static int imx6_add_pcie_port(struct imx6_pcie *imx6_pcie, ...@@ -667,7 +667,6 @@ static int imx6_add_pcie_port(struct imx6_pcie *imx6_pcie,
} }
} }
pp->root_bus_nr = -1;
pp->ops = &imx6_pcie_host_ops; pp->ops = &imx6_pcie_host_ops;
ret = dw_pcie_host_init(pp); ret = dw_pcie_host_init(pp);
......
...@@ -347,7 +347,6 @@ static int __init ks_add_pcie_port(struct keystone_pcie *ks_pcie, ...@@ -347,7 +347,6 @@ static int __init ks_add_pcie_port(struct keystone_pcie *ks_pcie,
} }
} }
pp->root_bus_nr = -1;
pp->ops = &keystone_pcie_host_ops; pp->ops = &keystone_pcie_host_ops;
ret = ks_dw_pcie_host_init(ks_pcie, ks_pcie->msi_intc_np); ret = ks_dw_pcie_host_init(ks_pcie, ks_pcie->msi_intc_np);
if (ret) { if (ret) {
......
...@@ -172,7 +172,6 @@ static int armada8k_add_pcie_port(struct armada8k_pcie *pcie, ...@@ -172,7 +172,6 @@ static int armada8k_add_pcie_port(struct armada8k_pcie *pcie,
struct device *dev = &pdev->dev; struct device *dev = &pdev->dev;
int ret; int ret;
pp->root_bus_nr = -1;
pp->ops = &armada8k_pcie_host_ops; pp->ops = &armada8k_pcie_host_ops;
pp->irq = platform_get_irq(pdev, 0); pp->irq = platform_get_irq(pdev, 0);
......
...@@ -399,7 +399,6 @@ static int artpec6_add_pcie_port(struct artpec6_pcie *artpec6_pcie, ...@@ -399,7 +399,6 @@ static int artpec6_add_pcie_port(struct artpec6_pcie *artpec6_pcie,
} }
} }
pp->root_bus_nr = -1;
pp->ops = &artpec6_pcie_host_ops; pp->ops = &artpec6_pcie_host_ops;
ret = dw_pcie_host_init(pp); ret = dw_pcie_host_init(pp);
...@@ -428,7 +427,7 @@ static void artpec6_pcie_ep_init(struct dw_pcie_ep *ep) ...@@ -428,7 +427,7 @@ static void artpec6_pcie_ep_init(struct dw_pcie_ep *ep)
} }
static int artpec6_pcie_raise_irq(struct dw_pcie_ep *ep, u8 func_no, static int artpec6_pcie_raise_irq(struct dw_pcie_ep *ep, u8 func_no,
enum pci_epc_irq_type type, u8 interrupt_num) enum pci_epc_irq_type type, u16 interrupt_num)
{ {
struct dw_pcie *pci = to_dw_pcie_from_ep(ep); struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
......
...@@ -40,6 +40,39 @@ void dw_pcie_ep_reset_bar(struct dw_pcie *pci, enum pci_barno bar) ...@@ -40,6 +40,39 @@ void dw_pcie_ep_reset_bar(struct dw_pcie *pci, enum pci_barno bar)
__dw_pcie_ep_reset_bar(pci, bar, 0); __dw_pcie_ep_reset_bar(pci, bar, 0);
} }
static u8 __dw_pcie_ep_find_next_cap(struct dw_pcie *pci, u8 cap_ptr,
u8 cap)
{
u8 cap_id, next_cap_ptr;
u16 reg;
reg = dw_pcie_readw_dbi(pci, cap_ptr);
next_cap_ptr = (reg & 0xff00) >> 8;
cap_id = (reg & 0x00ff);
if (!next_cap_ptr || cap_id > PCI_CAP_ID_MAX)
return 0;
if (cap_id == cap)
return cap_ptr;
return __dw_pcie_ep_find_next_cap(pci, next_cap_ptr, cap);
}
static u8 dw_pcie_ep_find_capability(struct dw_pcie *pci, u8 cap)
{
u8 next_cap_ptr;
u16 reg;
reg = dw_pcie_readw_dbi(pci, PCI_CAPABILITY_LIST);
next_cap_ptr = (reg & 0x00ff);
if (!next_cap_ptr)
return 0;
return __dw_pcie_ep_find_next_cap(pci, next_cap_ptr, cap);
}
static int dw_pcie_ep_write_header(struct pci_epc *epc, u8 func_no, static int dw_pcie_ep_write_header(struct pci_epc *epc, u8 func_no,
struct pci_epf_header *hdr) struct pci_epf_header *hdr)
{ {
...@@ -213,36 +246,84 @@ static int dw_pcie_ep_map_addr(struct pci_epc *epc, u8 func_no, ...@@ -213,36 +246,84 @@ static int dw_pcie_ep_map_addr(struct pci_epc *epc, u8 func_no,
static int dw_pcie_ep_get_msi(struct pci_epc *epc, u8 func_no) static int dw_pcie_ep_get_msi(struct pci_epc *epc, u8 func_no)
{ {
int val;
struct dw_pcie_ep *ep = epc_get_drvdata(epc); struct dw_pcie_ep *ep = epc_get_drvdata(epc);
struct dw_pcie *pci = to_dw_pcie_from_ep(ep); struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
u32 val, reg;
if (!ep->msi_cap)
return -EINVAL;
reg = ep->msi_cap + PCI_MSI_FLAGS;
val = dw_pcie_readw_dbi(pci, reg);
if (!(val & PCI_MSI_FLAGS_ENABLE))
return -EINVAL;
val = (val & PCI_MSI_FLAGS_QSIZE) >> 4;
return val;
}
static int dw_pcie_ep_set_msi(struct pci_epc *epc, u8 func_no, u8 interrupts)
{
struct dw_pcie_ep *ep = epc_get_drvdata(epc);
struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
u32 val, reg;
if (!ep->msi_cap)
return -EINVAL;
reg = ep->msi_cap + PCI_MSI_FLAGS;
val = dw_pcie_readw_dbi(pci, reg);
val &= ~PCI_MSI_FLAGS_QMASK;
val |= (interrupts << 1) & PCI_MSI_FLAGS_QMASK;
dw_pcie_dbi_ro_wr_en(pci);
dw_pcie_writew_dbi(pci, reg, val);
dw_pcie_dbi_ro_wr_dis(pci);
return 0;
}
static int dw_pcie_ep_get_msix(struct pci_epc *epc, u8 func_no)
{
struct dw_pcie_ep *ep = epc_get_drvdata(epc);
struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
u32 val, reg;
val = dw_pcie_readw_dbi(pci, MSI_MESSAGE_CONTROL); if (!ep->msix_cap)
if (!(val & MSI_CAP_MSI_EN_MASK))
return -EINVAL; return -EINVAL;
val = (val & MSI_CAP_MME_MASK) >> MSI_CAP_MME_SHIFT; reg = ep->msix_cap + PCI_MSIX_FLAGS;
val = dw_pcie_readw_dbi(pci, reg);
if (!(val & PCI_MSIX_FLAGS_ENABLE))
return -EINVAL;
val &= PCI_MSIX_FLAGS_QSIZE;
return val; return val;
} }
static int dw_pcie_ep_set_msi(struct pci_epc *epc, u8 func_no, u8 encode_int) static int dw_pcie_ep_set_msix(struct pci_epc *epc, u8 func_no, u16 interrupts)
{ {
int val;
struct dw_pcie_ep *ep = epc_get_drvdata(epc); struct dw_pcie_ep *ep = epc_get_drvdata(epc);
struct dw_pcie *pci = to_dw_pcie_from_ep(ep); struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
u32 val, reg;
if (!ep->msix_cap)
return -EINVAL;
val = dw_pcie_readw_dbi(pci, MSI_MESSAGE_CONTROL); reg = ep->msix_cap + PCI_MSIX_FLAGS;
val &= ~MSI_CAP_MMC_MASK; val = dw_pcie_readw_dbi(pci, reg);
val |= (encode_int << MSI_CAP_MMC_SHIFT) & MSI_CAP_MMC_MASK; val &= ~PCI_MSIX_FLAGS_QSIZE;
val |= interrupts;
dw_pcie_dbi_ro_wr_en(pci); dw_pcie_dbi_ro_wr_en(pci);
dw_pcie_writew_dbi(pci, MSI_MESSAGE_CONTROL, val); dw_pcie_writew_dbi(pci, reg, val);
dw_pcie_dbi_ro_wr_dis(pci); dw_pcie_dbi_ro_wr_dis(pci);
return 0; return 0;
} }
static int dw_pcie_ep_raise_irq(struct pci_epc *epc, u8 func_no, static int dw_pcie_ep_raise_irq(struct pci_epc *epc, u8 func_no,
enum pci_epc_irq_type type, u8 interrupt_num) enum pci_epc_irq_type type, u16 interrupt_num)
{ {
struct dw_pcie_ep *ep = epc_get_drvdata(epc); struct dw_pcie_ep *ep = epc_get_drvdata(epc);
...@@ -282,32 +363,52 @@ static const struct pci_epc_ops epc_ops = { ...@@ -282,32 +363,52 @@ static const struct pci_epc_ops epc_ops = {
.unmap_addr = dw_pcie_ep_unmap_addr, .unmap_addr = dw_pcie_ep_unmap_addr,
.set_msi = dw_pcie_ep_set_msi, .set_msi = dw_pcie_ep_set_msi,
.get_msi = dw_pcie_ep_get_msi, .get_msi = dw_pcie_ep_get_msi,
.set_msix = dw_pcie_ep_set_msix,
.get_msix = dw_pcie_ep_get_msix,
.raise_irq = dw_pcie_ep_raise_irq, .raise_irq = dw_pcie_ep_raise_irq,
.start = dw_pcie_ep_start, .start = dw_pcie_ep_start,
.stop = dw_pcie_ep_stop, .stop = dw_pcie_ep_stop,
}; };
int dw_pcie_ep_raise_legacy_irq(struct dw_pcie_ep *ep, u8 func_no)
{
struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
struct device *dev = pci->dev;
dev_err(dev, "EP cannot trigger legacy IRQs\n");
return -EINVAL;
}
int dw_pcie_ep_raise_msi_irq(struct dw_pcie_ep *ep, u8 func_no, int dw_pcie_ep_raise_msi_irq(struct dw_pcie_ep *ep, u8 func_no,
u8 interrupt_num) u8 interrupt_num)
{ {
struct dw_pcie *pci = to_dw_pcie_from_ep(ep); struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
struct pci_epc *epc = ep->epc; struct pci_epc *epc = ep->epc;
u16 msg_ctrl, msg_data; u16 msg_ctrl, msg_data;
u32 msg_addr_lower, msg_addr_upper; u32 msg_addr_lower, msg_addr_upper, reg;
u64 msg_addr; u64 msg_addr;
bool has_upper; bool has_upper;
int ret; int ret;
if (!ep->msi_cap)
return -EINVAL;
/* Raise MSI per the PCI Local Bus Specification Revision 3.0, 6.8.1. */ /* Raise MSI per the PCI Local Bus Specification Revision 3.0, 6.8.1. */
msg_ctrl = dw_pcie_readw_dbi(pci, MSI_MESSAGE_CONTROL); reg = ep->msi_cap + PCI_MSI_FLAGS;
msg_ctrl = dw_pcie_readw_dbi(pci, reg);
has_upper = !!(msg_ctrl & PCI_MSI_FLAGS_64BIT); has_upper = !!(msg_ctrl & PCI_MSI_FLAGS_64BIT);
msg_addr_lower = dw_pcie_readl_dbi(pci, MSI_MESSAGE_ADDR_L32); reg = ep->msi_cap + PCI_MSI_ADDRESS_LO;
msg_addr_lower = dw_pcie_readl_dbi(pci, reg);
if (has_upper) { if (has_upper) {
msg_addr_upper = dw_pcie_readl_dbi(pci, MSI_MESSAGE_ADDR_U32); reg = ep->msi_cap + PCI_MSI_ADDRESS_HI;
msg_data = dw_pcie_readw_dbi(pci, MSI_MESSAGE_DATA_64); msg_addr_upper = dw_pcie_readl_dbi(pci, reg);
reg = ep->msi_cap + PCI_MSI_DATA_64;
msg_data = dw_pcie_readw_dbi(pci, reg);
} else { } else {
msg_addr_upper = 0; msg_addr_upper = 0;
msg_data = dw_pcie_readw_dbi(pci, MSI_MESSAGE_DATA_32); reg = ep->msi_cap + PCI_MSI_DATA_32;
msg_data = dw_pcie_readw_dbi(pci, reg);
} }
msg_addr = ((u64) msg_addr_upper) << 32 | msg_addr_lower; msg_addr = ((u64) msg_addr_upper) << 32 | msg_addr_lower;
ret = dw_pcie_ep_map_addr(epc, func_no, ep->msi_mem_phys, msg_addr, ret = dw_pcie_ep_map_addr(epc, func_no, ep->msi_mem_phys, msg_addr,
...@@ -322,6 +423,64 @@ int dw_pcie_ep_raise_msi_irq(struct dw_pcie_ep *ep, u8 func_no, ...@@ -322,6 +423,64 @@ int dw_pcie_ep_raise_msi_irq(struct dw_pcie_ep *ep, u8 func_no,
return 0; return 0;
} }
int dw_pcie_ep_raise_msix_irq(struct dw_pcie_ep *ep, u8 func_no,
u16 interrupt_num)
{
struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
struct pci_epc *epc = ep->epc;
u16 tbl_offset, bir;
u32 bar_addr_upper, bar_addr_lower;
u32 msg_addr_upper, msg_addr_lower;
u32 reg, msg_data, vec_ctrl;
u64 tbl_addr, msg_addr, reg_u64;
void __iomem *msix_tbl;
int ret;
reg = ep->msix_cap + PCI_MSIX_TABLE;
tbl_offset = dw_pcie_readl_dbi(pci, reg);
bir = (tbl_offset & PCI_MSIX_TABLE_BIR);
tbl_offset &= PCI_MSIX_TABLE_OFFSET;
tbl_offset >>= 3;
reg = PCI_BASE_ADDRESS_0 + (4 * bir);
bar_addr_upper = 0;
bar_addr_lower = dw_pcie_readl_dbi(pci, reg);
reg_u64 = (bar_addr_lower & PCI_BASE_ADDRESS_MEM_TYPE_MASK);
if (reg_u64 == PCI_BASE_ADDRESS_MEM_TYPE_64)
bar_addr_upper = dw_pcie_readl_dbi(pci, reg + 4);
tbl_addr = ((u64) bar_addr_upper) << 32 | bar_addr_lower;
tbl_addr += (tbl_offset + ((interrupt_num - 1) * PCI_MSIX_ENTRY_SIZE));
tbl_addr &= PCI_BASE_ADDRESS_MEM_MASK;
msix_tbl = ioremap_nocache(ep->phys_base + tbl_addr,
PCI_MSIX_ENTRY_SIZE);
if (!msix_tbl)
return -EINVAL;
msg_addr_lower = readl(msix_tbl + PCI_MSIX_ENTRY_LOWER_ADDR);
msg_addr_upper = readl(msix_tbl + PCI_MSIX_ENTRY_UPPER_ADDR);
msg_addr = ((u64) msg_addr_upper) << 32 | msg_addr_lower;
msg_data = readl(msix_tbl + PCI_MSIX_ENTRY_DATA);
vec_ctrl = readl(msix_tbl + PCI_MSIX_ENTRY_VECTOR_CTRL);
iounmap(msix_tbl);
if (vec_ctrl & PCI_MSIX_ENTRY_CTRL_MASKBIT)
return -EPERM;
ret = dw_pcie_ep_map_addr(epc, func_no, ep->msi_mem_phys, msg_addr,
epc->mem->page_size);
if (ret)
return ret;
writel(msg_data, ep->msi_mem);
dw_pcie_ep_unmap_addr(epc, func_no, ep->msi_mem_phys);
return 0;
}
void dw_pcie_ep_exit(struct dw_pcie_ep *ep) void dw_pcie_ep_exit(struct dw_pcie_ep *ep)
{ {
struct pci_epc *epc = ep->epc; struct pci_epc *epc = ep->epc;
...@@ -386,15 +545,18 @@ int dw_pcie_ep_init(struct dw_pcie_ep *ep) ...@@ -386,15 +545,18 @@ int dw_pcie_ep_init(struct dw_pcie_ep *ep)
return -ENOMEM; return -ENOMEM;
ep->outbound_addr = addr; ep->outbound_addr = addr;
if (ep->ops->ep_init)
ep->ops->ep_init(ep);
epc = devm_pci_epc_create(dev, &epc_ops); epc = devm_pci_epc_create(dev, &epc_ops);
if (IS_ERR(epc)) { if (IS_ERR(epc)) {
dev_err(dev, "Failed to create epc device\n"); dev_err(dev, "Failed to create epc device\n");
return PTR_ERR(epc); return PTR_ERR(epc);
} }
ep->epc = epc;
epc_set_drvdata(epc, ep);
if (ep->ops->ep_init)
ep->ops->ep_init(ep);
ret = of_property_read_u8(np, "max-functions", &epc->max_functions); ret = of_property_read_u8(np, "max-functions", &epc->max_functions);
if (ret < 0) if (ret < 0)
epc->max_functions = 1; epc->max_functions = 1;
...@@ -409,15 +571,13 @@ int dw_pcie_ep_init(struct dw_pcie_ep *ep) ...@@ -409,15 +571,13 @@ int dw_pcie_ep_init(struct dw_pcie_ep *ep)
ep->msi_mem = pci_epc_mem_alloc_addr(epc, &ep->msi_mem_phys, ep->msi_mem = pci_epc_mem_alloc_addr(epc, &ep->msi_mem_phys,
epc->mem->page_size); epc->mem->page_size);
if (!ep->msi_mem) { if (!ep->msi_mem) {
dev_err(dev, "Failed to reserve memory for MSI\n"); dev_err(dev, "Failed to reserve memory for MSI/MSI-X\n");
return -ENOMEM; return -ENOMEM;
} }
ep->msi_cap = dw_pcie_ep_find_capability(pci, PCI_CAP_ID_MSI);
epc->features = EPC_FEATURE_NO_LINKUP_NOTIFIER; ep->msix_cap = dw_pcie_ep_find_capability(pci, PCI_CAP_ID_MSIX);
EPC_FEATURE_SET_BAR(epc->features, BAR_0);
ep->epc = epc;
epc_set_drvdata(epc, ep);
dw_pcie_setup(pci); dw_pcie_setup(pci);
return 0; return 0;
......
...@@ -70,24 +70,29 @@ static const struct dw_pcie_ops dw_pcie_ops = { ...@@ -70,24 +70,29 @@ static const struct dw_pcie_ops dw_pcie_ops = {
static void dw_plat_pcie_ep_init(struct dw_pcie_ep *ep) static void dw_plat_pcie_ep_init(struct dw_pcie_ep *ep)
{ {
struct dw_pcie *pci = to_dw_pcie_from_ep(ep); struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
struct pci_epc *epc = ep->epc;
enum pci_barno bar; enum pci_barno bar;
for (bar = BAR_0; bar <= BAR_5; bar++) for (bar = BAR_0; bar <= BAR_5; bar++)
dw_pcie_ep_reset_bar(pci, bar); dw_pcie_ep_reset_bar(pci, bar);
epc->features |= EPC_FEATURE_NO_LINKUP_NOTIFIER;
epc->features |= EPC_FEATURE_MSIX_AVAILABLE;
} }
static int dw_plat_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no, static int dw_plat_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no,
enum pci_epc_irq_type type, enum pci_epc_irq_type type,
u8 interrupt_num) u16 interrupt_num)
{ {
struct dw_pcie *pci = to_dw_pcie_from_ep(ep); struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
switch (type) { switch (type) {
case PCI_EPC_IRQ_LEGACY: case PCI_EPC_IRQ_LEGACY:
dev_err(pci->dev, "EP cannot trigger legacy IRQs\n"); return dw_pcie_ep_raise_legacy_irq(ep, func_no);
return -EINVAL;
case PCI_EPC_IRQ_MSI: case PCI_EPC_IRQ_MSI:
return dw_pcie_ep_raise_msi_irq(ep, func_no, interrupt_num); return dw_pcie_ep_raise_msi_irq(ep, func_no, interrupt_num);
case PCI_EPC_IRQ_MSIX:
return dw_pcie_ep_raise_msix_irq(ep, func_no, interrupt_num);
default: default:
dev_err(pci->dev, "UNKNOWN IRQ type\n"); dev_err(pci->dev, "UNKNOWN IRQ type\n");
} }
...@@ -118,7 +123,6 @@ static int dw_plat_add_pcie_port(struct dw_plat_pcie *dw_plat_pcie, ...@@ -118,7 +123,6 @@ static int dw_plat_add_pcie_port(struct dw_plat_pcie *dw_plat_pcie,
return pp->msi_irq; return pp->msi_irq;
} }
pp->root_bus_nr = -1;
pp->ops = &dw_plat_pcie_host_ops; pp->ops = &dw_plat_pcie_host_ops;
ret = dw_pcie_host_init(pp); ret = dw_pcie_host_init(pp);
......
...@@ -96,17 +96,6 @@ ...@@ -96,17 +96,6 @@
#define PCIE_GET_ATU_INB_UNR_REG_OFFSET(region) \ #define PCIE_GET_ATU_INB_UNR_REG_OFFSET(region) \
((0x3 << 20) | ((region) << 9) | (0x1 << 8)) ((0x3 << 20) | ((region) << 9) | (0x1 << 8))
#define MSI_MESSAGE_CONTROL 0x52
#define MSI_CAP_MMC_SHIFT 1
#define MSI_CAP_MMC_MASK (7 << MSI_CAP_MMC_SHIFT)
#define MSI_CAP_MME_SHIFT 4
#define MSI_CAP_MSI_EN_MASK 0x1
#define MSI_CAP_MME_MASK (7 << MSI_CAP_MME_SHIFT)
#define MSI_MESSAGE_ADDR_L32 0x54
#define MSI_MESSAGE_ADDR_U32 0x58
#define MSI_MESSAGE_DATA_32 0x58
#define MSI_MESSAGE_DATA_64 0x5C
#define MAX_MSI_IRQS 256 #define MAX_MSI_IRQS 256
#define MAX_MSI_IRQS_PER_CTRL 32 #define MAX_MSI_IRQS_PER_CTRL 32
#define MAX_MSI_CTRLS (MAX_MSI_IRQS / MAX_MSI_IRQS_PER_CTRL) #define MAX_MSI_CTRLS (MAX_MSI_IRQS / MAX_MSI_IRQS_PER_CTRL)
...@@ -191,7 +180,7 @@ enum dw_pcie_as_type { ...@@ -191,7 +180,7 @@ enum dw_pcie_as_type {
struct dw_pcie_ep_ops { struct dw_pcie_ep_ops {
void (*ep_init)(struct dw_pcie_ep *ep); void (*ep_init)(struct dw_pcie_ep *ep);
int (*raise_irq)(struct dw_pcie_ep *ep, u8 func_no, int (*raise_irq)(struct dw_pcie_ep *ep, u8 func_no,
enum pci_epc_irq_type type, u8 interrupt_num); enum pci_epc_irq_type type, u16 interrupt_num);
}; };
struct dw_pcie_ep { struct dw_pcie_ep {
...@@ -208,6 +197,8 @@ struct dw_pcie_ep { ...@@ -208,6 +197,8 @@ struct dw_pcie_ep {
u32 num_ob_windows; u32 num_ob_windows;
void __iomem *msi_mem; void __iomem *msi_mem;
phys_addr_t msi_mem_phys; phys_addr_t msi_mem_phys;
u8 msi_cap; /* MSI capability offset */
u8 msix_cap; /* MSI-X capability offset */
}; };
struct dw_pcie_ops { struct dw_pcie_ops {
...@@ -357,8 +348,11 @@ static inline int dw_pcie_allocate_domains(struct pcie_port *pp) ...@@ -357,8 +348,11 @@ static inline int dw_pcie_allocate_domains(struct pcie_port *pp)
void dw_pcie_ep_linkup(struct dw_pcie_ep *ep); void dw_pcie_ep_linkup(struct dw_pcie_ep *ep);
int dw_pcie_ep_init(struct dw_pcie_ep *ep); int dw_pcie_ep_init(struct dw_pcie_ep *ep);
void dw_pcie_ep_exit(struct dw_pcie_ep *ep); void dw_pcie_ep_exit(struct dw_pcie_ep *ep);
int dw_pcie_ep_raise_legacy_irq(struct dw_pcie_ep *ep, u8 func_no);
int dw_pcie_ep_raise_msi_irq(struct dw_pcie_ep *ep, u8 func_no, int dw_pcie_ep_raise_msi_irq(struct dw_pcie_ep *ep, u8 func_no,
u8 interrupt_num); u8 interrupt_num);
int dw_pcie_ep_raise_msix_irq(struct dw_pcie_ep *ep, u8 func_no,
u16 interrupt_num);
void dw_pcie_ep_reset_bar(struct dw_pcie *pci, enum pci_barno bar); void dw_pcie_ep_reset_bar(struct dw_pcie *pci, enum pci_barno bar);
#else #else
static inline void dw_pcie_ep_linkup(struct dw_pcie_ep *ep) static inline void dw_pcie_ep_linkup(struct dw_pcie_ep *ep)
...@@ -374,12 +368,23 @@ static inline void dw_pcie_ep_exit(struct dw_pcie_ep *ep) ...@@ -374,12 +368,23 @@ static inline void dw_pcie_ep_exit(struct dw_pcie_ep *ep)
{ {
} }
static inline int dw_pcie_ep_raise_legacy_irq(struct dw_pcie_ep *ep, u8 func_no)
{
return 0;
}
static inline int dw_pcie_ep_raise_msi_irq(struct dw_pcie_ep *ep, u8 func_no, static inline int dw_pcie_ep_raise_msi_irq(struct dw_pcie_ep *ep, u8 func_no,
u8 interrupt_num) u8 interrupt_num)
{ {
return 0; return 0;
} }
static inline int dw_pcie_ep_raise_msix_irq(struct dw_pcie_ep *ep, u8 func_no,
u16 interrupt_num)
{
return 0;
}
static inline void dw_pcie_ep_reset_bar(struct dw_pcie *pci, enum pci_barno bar) static inline void dw_pcie_ep_reset_bar(struct dw_pcie *pci, enum pci_barno bar)
{ {
} }
......
...@@ -420,7 +420,6 @@ static int histb_pcie_probe(struct platform_device *pdev) ...@@ -420,7 +420,6 @@ static int histb_pcie_probe(struct platform_device *pdev)
phy_init(hipcie->phy); phy_init(hipcie->phy);
} }
pp->root_bus_nr = -1;
pp->ops = &histb_pcie_host_ops; pp->ops = &histb_pcie_host_ops;
platform_set_drvdata(pdev, hipcie); platform_set_drvdata(pdev, hipcie);
......
...@@ -430,6 +430,9 @@ static int kirin_pcie_host_init(struct pcie_port *pp) ...@@ -430,6 +430,9 @@ static int kirin_pcie_host_init(struct pcie_port *pp)
{ {
kirin_pcie_establish_link(pp); kirin_pcie_establish_link(pp);
if (IS_ENABLED(CONFIG_PCI_MSI))
dw_pcie_msi_init(pp);
return 0; return 0;
} }
...@@ -445,9 +448,34 @@ static const struct dw_pcie_host_ops kirin_pcie_host_ops = { ...@@ -445,9 +448,34 @@ static const struct dw_pcie_host_ops kirin_pcie_host_ops = {
.host_init = kirin_pcie_host_init, .host_init = kirin_pcie_host_init,
}; };
static int kirin_pcie_add_msi(struct dw_pcie *pci,
struct platform_device *pdev)
{
int irq;
if (IS_ENABLED(CONFIG_PCI_MSI)) {
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
dev_err(&pdev->dev,
"failed to get MSI IRQ (%d)\n", irq);
return irq;
}
pci->pp.msi_irq = irq;
}
return 0;
}
static int __init kirin_add_pcie_port(struct dw_pcie *pci, static int __init kirin_add_pcie_port(struct dw_pcie *pci,
struct platform_device *pdev) struct platform_device *pdev)
{ {
int ret;
ret = kirin_pcie_add_msi(pci, pdev);
if (ret)
return ret;
pci->pp.ops = &kirin_pcie_host_ops; pci->pp.ops = &kirin_pcie_host_ops;
return dw_pcie_host_init(&pci->pp); return dw_pcie_host_init(&pci->pp);
......
...@@ -1251,7 +1251,6 @@ static int qcom_pcie_probe(struct platform_device *pdev) ...@@ -1251,7 +1251,6 @@ static int qcom_pcie_probe(struct platform_device *pdev)
if (ret) if (ret)
return ret; return ret;
pp->root_bus_nr = -1;
pp->ops = &qcom_pcie_dw_ops; pp->ops = &qcom_pcie_dw_ops;
if (IS_ENABLED(CONFIG_PCI_MSI)) { if (IS_ENABLED(CONFIG_PCI_MSI)) {
......
...@@ -210,7 +210,6 @@ static int spear13xx_add_pcie_port(struct spear13xx_pcie *spear13xx_pcie, ...@@ -210,7 +210,6 @@ static int spear13xx_add_pcie_port(struct spear13xx_pcie *spear13xx_pcie,
return ret; return ret;
} }
pp->root_bus_nr = -1;
pp->ops = &spear13xx_pcie_host_ops; pp->ops = &spear13xx_pcie_host_ops;
ret = dw_pcie_host_init(pp); ret = dw_pcie_host_init(pp);
......
...@@ -362,7 +362,8 @@ static int cdns_pcie_ep_send_msi_irq(struct cdns_pcie_ep *ep, u8 fn, ...@@ -362,7 +362,8 @@ static int cdns_pcie_ep_send_msi_irq(struct cdns_pcie_ep *ep, u8 fn,
} }
static int cdns_pcie_ep_raise_irq(struct pci_epc *epc, u8 fn, static int cdns_pcie_ep_raise_irq(struct pci_epc *epc, u8 fn,
enum pci_epc_irq_type type, u8 interrupt_num) enum pci_epc_irq_type type,
u16 interrupt_num)
{ {
struct cdns_pcie_ep *ep = epc_get_drvdata(epc); struct cdns_pcie_ep *ep = epc_get_drvdata(epc);
......
...@@ -472,7 +472,7 @@ static int rockchip_pcie_ep_send_msi_irq(struct rockchip_pcie_ep *ep, u8 fn, ...@@ -472,7 +472,7 @@ static int rockchip_pcie_ep_send_msi_irq(struct rockchip_pcie_ep *ep, u8 fn,
static int rockchip_pcie_ep_raise_irq(struct pci_epc *epc, u8 fn, static int rockchip_pcie_ep_raise_irq(struct pci_epc *epc, u8 fn,
enum pci_epc_irq_type type, enum pci_epc_irq_type type,
u8 interrupt_num) u16 interrupt_num)
{ {
struct rockchip_pcie_ep *ep = epc_get_drvdata(epc); struct rockchip_pcie_ep *ep = epc_get_drvdata(epc);
......
...@@ -18,13 +18,16 @@ ...@@ -18,13 +18,16 @@
#include <linux/pci-epf.h> #include <linux/pci-epf.h>
#include <linux/pci_regs.h> #include <linux/pci_regs.h>
#define IRQ_TYPE_LEGACY 0
#define IRQ_TYPE_MSI 1
#define IRQ_TYPE_MSIX 2
#define COMMAND_RAISE_LEGACY_IRQ BIT(0) #define COMMAND_RAISE_LEGACY_IRQ BIT(0)
#define COMMAND_RAISE_MSI_IRQ BIT(1) #define COMMAND_RAISE_MSI_IRQ BIT(1)
#define MSI_NUMBER_SHIFT 2 #define COMMAND_RAISE_MSIX_IRQ BIT(2)
#define MSI_NUMBER_MASK (0x3f << MSI_NUMBER_SHIFT) #define COMMAND_READ BIT(3)
#define COMMAND_READ BIT(8) #define COMMAND_WRITE BIT(4)
#define COMMAND_WRITE BIT(9) #define COMMAND_COPY BIT(5)
#define COMMAND_COPY BIT(10)
#define STATUS_READ_SUCCESS BIT(0) #define STATUS_READ_SUCCESS BIT(0)
#define STATUS_READ_FAIL BIT(1) #define STATUS_READ_FAIL BIT(1)
...@@ -45,6 +48,7 @@ struct pci_epf_test { ...@@ -45,6 +48,7 @@ struct pci_epf_test {
struct pci_epf *epf; struct pci_epf *epf;
enum pci_barno test_reg_bar; enum pci_barno test_reg_bar;
bool linkup_notifier; bool linkup_notifier;
bool msix_available;
struct delayed_work cmd_handler; struct delayed_work cmd_handler;
}; };
...@@ -56,6 +60,8 @@ struct pci_epf_test_reg { ...@@ -56,6 +60,8 @@ struct pci_epf_test_reg {
u64 dst_addr; u64 dst_addr;
u32 size; u32 size;
u32 checksum; u32 checksum;
u32 irq_type;
u32 irq_number;
} __packed; } __packed;
static struct pci_epf_header test_header = { static struct pci_epf_header test_header = {
...@@ -244,31 +250,42 @@ static int pci_epf_test_write(struct pci_epf_test *epf_test) ...@@ -244,31 +250,42 @@ static int pci_epf_test_write(struct pci_epf_test *epf_test)
return ret; return ret;
} }
static void pci_epf_test_raise_irq(struct pci_epf_test *epf_test, u8 irq) static void pci_epf_test_raise_irq(struct pci_epf_test *epf_test, u8 irq_type,
u16 irq)
{ {
u8 msi_count;
struct pci_epf *epf = epf_test->epf; struct pci_epf *epf = epf_test->epf;
struct device *dev = &epf->dev;
struct pci_epc *epc = epf->epc; struct pci_epc *epc = epf->epc;
enum pci_barno test_reg_bar = epf_test->test_reg_bar; enum pci_barno test_reg_bar = epf_test->test_reg_bar;
struct pci_epf_test_reg *reg = epf_test->reg[test_reg_bar]; struct pci_epf_test_reg *reg = epf_test->reg[test_reg_bar];
reg->status |= STATUS_IRQ_RAISED; reg->status |= STATUS_IRQ_RAISED;
msi_count = pci_epc_get_msi(epc, epf->func_no);
if (irq > msi_count || msi_count <= 0) switch (irq_type) {
case IRQ_TYPE_LEGACY:
pci_epc_raise_irq(epc, epf->func_no, PCI_EPC_IRQ_LEGACY, 0); pci_epc_raise_irq(epc, epf->func_no, PCI_EPC_IRQ_LEGACY, 0);
else break;
case IRQ_TYPE_MSI:
pci_epc_raise_irq(epc, epf->func_no, PCI_EPC_IRQ_MSI, irq); pci_epc_raise_irq(epc, epf->func_no, PCI_EPC_IRQ_MSI, irq);
break;
case IRQ_TYPE_MSIX:
pci_epc_raise_irq(epc, epf->func_no, PCI_EPC_IRQ_MSIX, irq);
break;
default:
dev_err(dev, "Failed to raise IRQ, unknown type\n");
break;
}
} }
static void pci_epf_test_cmd_handler(struct work_struct *work) static void pci_epf_test_cmd_handler(struct work_struct *work)
{ {
int ret; int ret;
u8 irq; int count;
u8 msi_count;
u32 command; u32 command;
struct pci_epf_test *epf_test = container_of(work, struct pci_epf_test, struct pci_epf_test *epf_test = container_of(work, struct pci_epf_test,
cmd_handler.work); cmd_handler.work);
struct pci_epf *epf = epf_test->epf; struct pci_epf *epf = epf_test->epf;
struct device *dev = &epf->dev;
struct pci_epc *epc = epf->epc; struct pci_epc *epc = epf->epc;
enum pci_barno test_reg_bar = epf_test->test_reg_bar; enum pci_barno test_reg_bar = epf_test->test_reg_bar;
struct pci_epf_test_reg *reg = epf_test->reg[test_reg_bar]; struct pci_epf_test_reg *reg = epf_test->reg[test_reg_bar];
...@@ -280,7 +297,10 @@ static void pci_epf_test_cmd_handler(struct work_struct *work) ...@@ -280,7 +297,10 @@ static void pci_epf_test_cmd_handler(struct work_struct *work)
reg->command = 0; reg->command = 0;
reg->status = 0; reg->status = 0;
irq = (command & MSI_NUMBER_MASK) >> MSI_NUMBER_SHIFT; if (reg->irq_type > IRQ_TYPE_MSIX) {
dev_err(dev, "Failed to detect IRQ type\n");
goto reset_handler;
}
if (command & COMMAND_RAISE_LEGACY_IRQ) { if (command & COMMAND_RAISE_LEGACY_IRQ) {
reg->status = STATUS_IRQ_RAISED; reg->status = STATUS_IRQ_RAISED;
...@@ -294,7 +314,8 @@ static void pci_epf_test_cmd_handler(struct work_struct *work) ...@@ -294,7 +314,8 @@ static void pci_epf_test_cmd_handler(struct work_struct *work)
reg->status |= STATUS_WRITE_FAIL; reg->status |= STATUS_WRITE_FAIL;
else else
reg->status |= STATUS_WRITE_SUCCESS; reg->status |= STATUS_WRITE_SUCCESS;
pci_epf_test_raise_irq(epf_test, irq); pci_epf_test_raise_irq(epf_test, reg->irq_type,
reg->irq_number);
goto reset_handler; goto reset_handler;
} }
...@@ -304,7 +325,8 @@ static void pci_epf_test_cmd_handler(struct work_struct *work) ...@@ -304,7 +325,8 @@ static void pci_epf_test_cmd_handler(struct work_struct *work)
reg->status |= STATUS_READ_SUCCESS; reg->status |= STATUS_READ_SUCCESS;
else else
reg->status |= STATUS_READ_FAIL; reg->status |= STATUS_READ_FAIL;
pci_epf_test_raise_irq(epf_test, irq); pci_epf_test_raise_irq(epf_test, reg->irq_type,
reg->irq_number);
goto reset_handler; goto reset_handler;
} }
...@@ -314,16 +336,28 @@ static void pci_epf_test_cmd_handler(struct work_struct *work) ...@@ -314,16 +336,28 @@ static void pci_epf_test_cmd_handler(struct work_struct *work)
reg->status |= STATUS_COPY_SUCCESS; reg->status |= STATUS_COPY_SUCCESS;
else else
reg->status |= STATUS_COPY_FAIL; reg->status |= STATUS_COPY_FAIL;
pci_epf_test_raise_irq(epf_test, irq); pci_epf_test_raise_irq(epf_test, reg->irq_type,
reg->irq_number);
goto reset_handler; goto reset_handler;
} }
if (command & COMMAND_RAISE_MSI_IRQ) { if (command & COMMAND_RAISE_MSI_IRQ) {
msi_count = pci_epc_get_msi(epc, epf->func_no); count = pci_epc_get_msi(epc, epf->func_no);
if (irq > msi_count || msi_count <= 0) if (reg->irq_number > count || count <= 0)
goto reset_handler; goto reset_handler;
reg->status = STATUS_IRQ_RAISED; reg->status = STATUS_IRQ_RAISED;
pci_epc_raise_irq(epc, epf->func_no, PCI_EPC_IRQ_MSI, irq); pci_epc_raise_irq(epc, epf->func_no, PCI_EPC_IRQ_MSI,
reg->irq_number);
goto reset_handler;
}
if (command & COMMAND_RAISE_MSIX_IRQ) {
count = pci_epc_get_msix(epc, epf->func_no);
if (reg->irq_number > count || count <= 0)
goto reset_handler;
reg->status = STATUS_IRQ_RAISED;
pci_epc_raise_irq(epc, epf->func_no, PCI_EPC_IRQ_MSIX,
reg->irq_number);
goto reset_handler; goto reset_handler;
} }
...@@ -440,6 +474,8 @@ static int pci_epf_test_bind(struct pci_epf *epf) ...@@ -440,6 +474,8 @@ static int pci_epf_test_bind(struct pci_epf *epf)
else else
epf_test->linkup_notifier = true; epf_test->linkup_notifier = true;
epf_test->msix_available = epc->features & EPC_FEATURE_MSIX_AVAILABLE;
epf_test->test_reg_bar = EPC_FEATURE_GET_BAR(epc->features); epf_test->test_reg_bar = EPC_FEATURE_GET_BAR(epc->features);
ret = pci_epc_write_header(epc, epf->func_no, header); ret = pci_epc_write_header(epc, epf->func_no, header);
...@@ -457,8 +493,18 @@ static int pci_epf_test_bind(struct pci_epf *epf) ...@@ -457,8 +493,18 @@ static int pci_epf_test_bind(struct pci_epf *epf)
return ret; return ret;
ret = pci_epc_set_msi(epc, epf->func_no, epf->msi_interrupts); ret = pci_epc_set_msi(epc, epf->func_no, epf->msi_interrupts);
if (ret) if (ret) {
dev_err(dev, "MSI configuration failed\n");
return ret; return ret;
}
if (epf_test->msix_available) {
ret = pci_epc_set_msix(epc, epf->func_no, epf->msix_interrupts);
if (ret) {
dev_err(dev, "MSI-X configuration failed\n");
return ret;
}
}
if (!epf_test->linkup_notifier) if (!epf_test->linkup_notifier)
queue_work(kpcitest_workqueue, &epf_test->cmd_handler.work); queue_work(kpcitest_workqueue, &epf_test->cmd_handler.work);
......
...@@ -286,6 +286,28 @@ static ssize_t pci_epf_msi_interrupts_show(struct config_item *item, ...@@ -286,6 +286,28 @@ static ssize_t pci_epf_msi_interrupts_show(struct config_item *item,
to_pci_epf_group(item)->epf->msi_interrupts); to_pci_epf_group(item)->epf->msi_interrupts);
} }
static ssize_t pci_epf_msix_interrupts_store(struct config_item *item,
const char *page, size_t len)
{
u16 val;
int ret;
ret = kstrtou16(page, 0, &val);
if (ret)
return ret;
to_pci_epf_group(item)->epf->msix_interrupts = val;
return len;
}
static ssize_t pci_epf_msix_interrupts_show(struct config_item *item,
char *page)
{
return sprintf(page, "%d\n",
to_pci_epf_group(item)->epf->msix_interrupts);
}
PCI_EPF_HEADER_R(vendorid) PCI_EPF_HEADER_R(vendorid)
PCI_EPF_HEADER_W_u16(vendorid) PCI_EPF_HEADER_W_u16(vendorid)
...@@ -327,6 +349,7 @@ CONFIGFS_ATTR(pci_epf_, subsys_vendor_id); ...@@ -327,6 +349,7 @@ CONFIGFS_ATTR(pci_epf_, subsys_vendor_id);
CONFIGFS_ATTR(pci_epf_, subsys_id); CONFIGFS_ATTR(pci_epf_, subsys_id);
CONFIGFS_ATTR(pci_epf_, interrupt_pin); CONFIGFS_ATTR(pci_epf_, interrupt_pin);
CONFIGFS_ATTR(pci_epf_, msi_interrupts); CONFIGFS_ATTR(pci_epf_, msi_interrupts);
CONFIGFS_ATTR(pci_epf_, msix_interrupts);
static struct configfs_attribute *pci_epf_attrs[] = { static struct configfs_attribute *pci_epf_attrs[] = {
&pci_epf_attr_vendorid, &pci_epf_attr_vendorid,
...@@ -340,6 +363,7 @@ static struct configfs_attribute *pci_epf_attrs[] = { ...@@ -340,6 +363,7 @@ static struct configfs_attribute *pci_epf_attrs[] = {
&pci_epf_attr_subsys_id, &pci_epf_attr_subsys_id,
&pci_epf_attr_interrupt_pin, &pci_epf_attr_interrupt_pin,
&pci_epf_attr_msi_interrupts, &pci_epf_attr_msi_interrupts,
&pci_epf_attr_msix_interrupts,
NULL, NULL,
}; };
......
...@@ -131,13 +131,13 @@ EXPORT_SYMBOL_GPL(pci_epc_start); ...@@ -131,13 +131,13 @@ EXPORT_SYMBOL_GPL(pci_epc_start);
* pci_epc_raise_irq() - interrupt the host system * pci_epc_raise_irq() - interrupt the host system
* @epc: the EPC device which has to interrupt the host * @epc: the EPC device which has to interrupt the host
* @func_no: the endpoint function number in the EPC device * @func_no: the endpoint function number in the EPC device
* @type: specify the type of interrupt; legacy or MSI * @type: specify the type of interrupt; legacy, MSI or MSI-X
* @interrupt_num: the MSI interrupt number * @interrupt_num: the MSI or MSI-X interrupt number
* *
* Invoke to raise an MSI or legacy interrupt * Invoke to raise an legacy, MSI or MSI-X interrupt
*/ */
int pci_epc_raise_irq(struct pci_epc *epc, u8 func_no, int pci_epc_raise_irq(struct pci_epc *epc, u8 func_no,
enum pci_epc_irq_type type, u8 interrupt_num) enum pci_epc_irq_type type, u16 interrupt_num)
{ {
int ret; int ret;
unsigned long flags; unsigned long flags;
...@@ -201,7 +201,8 @@ int pci_epc_set_msi(struct pci_epc *epc, u8 func_no, u8 interrupts) ...@@ -201,7 +201,8 @@ int pci_epc_set_msi(struct pci_epc *epc, u8 func_no, u8 interrupts)
u8 encode_int; u8 encode_int;
unsigned long flags; unsigned long flags;
if (IS_ERR_OR_NULL(epc) || func_no >= epc->max_functions) if (IS_ERR_OR_NULL(epc) || func_no >= epc->max_functions ||
interrupts > 32)
return -EINVAL; return -EINVAL;
if (!epc->ops->set_msi) if (!epc->ops->set_msi)
...@@ -217,6 +218,63 @@ int pci_epc_set_msi(struct pci_epc *epc, u8 func_no, u8 interrupts) ...@@ -217,6 +218,63 @@ int pci_epc_set_msi(struct pci_epc *epc, u8 func_no, u8 interrupts)
} }
EXPORT_SYMBOL_GPL(pci_epc_set_msi); EXPORT_SYMBOL_GPL(pci_epc_set_msi);
/**
* pci_epc_get_msix() - get the number of MSI-X interrupt numbers allocated
* @epc: the EPC device to which MSI-X interrupts was requested
* @func_no: the endpoint function number in the EPC device
*
* Invoke to get the number of MSI-X interrupts allocated by the RC
*/
int pci_epc_get_msix(struct pci_epc *epc, u8 func_no)
{
int interrupt;
unsigned long flags;
if (IS_ERR_OR_NULL(epc) || func_no >= epc->max_functions)
return 0;
if (!epc->ops->get_msix)
return 0;
spin_lock_irqsave(&epc->lock, flags);
interrupt = epc->ops->get_msix(epc, func_no);
spin_unlock_irqrestore(&epc->lock, flags);
if (interrupt < 0)
return 0;
return interrupt + 1;
}
EXPORT_SYMBOL_GPL(pci_epc_get_msix);
/**
* pci_epc_set_msix() - set the number of MSI-X interrupt numbers required
* @epc: the EPC device on which MSI-X has to be configured
* @func_no: the endpoint function number in the EPC device
* @interrupts: number of MSI-X interrupts required by the EPF
*
* Invoke to set the required number of MSI-X interrupts.
*/
int pci_epc_set_msix(struct pci_epc *epc, u8 func_no, u16 interrupts)
{
int ret;
unsigned long flags;
if (IS_ERR_OR_NULL(epc) || func_no >= epc->max_functions ||
interrupts < 1 || interrupts > 2048)
return -EINVAL;
if (!epc->ops->set_msix)
return 0;
spin_lock_irqsave(&epc->lock, flags);
ret = epc->ops->set_msix(epc, func_no, interrupts - 1);
spin_unlock_irqrestore(&epc->lock, flags);
return ret;
}
EXPORT_SYMBOL_GPL(pci_epc_set_msix);
/** /**
* pci_epc_unmap_addr() - unmap CPU address from PCI address * pci_epc_unmap_addr() - unmap CPU address from PCI address
* @epc: the EPC device on which address is allocated * @epc: the EPC device on which address is allocated
......
...@@ -17,6 +17,7 @@ enum pci_epc_irq_type { ...@@ -17,6 +17,7 @@ enum pci_epc_irq_type {
PCI_EPC_IRQ_UNKNOWN, PCI_EPC_IRQ_UNKNOWN,
PCI_EPC_IRQ_LEGACY, PCI_EPC_IRQ_LEGACY,
PCI_EPC_IRQ_MSI, PCI_EPC_IRQ_MSI,
PCI_EPC_IRQ_MSIX,
}; };
/** /**
...@@ -30,7 +31,11 @@ enum pci_epc_irq_type { ...@@ -30,7 +31,11 @@ enum pci_epc_irq_type {
* capability register * capability register
* @get_msi: ops to get the number of MSI interrupts allocated by the RC from * @get_msi: ops to get the number of MSI interrupts allocated by the RC from
* the MSI capability register * the MSI capability register
* @raise_irq: ops to raise a legacy or MSI interrupt * @set_msix: ops to set the requested number of MSI-X interrupts in the
* MSI-X capability register
* @get_msix: ops to get the number of MSI-X interrupts allocated by the RC
* from the MSI-X capability register
* @raise_irq: ops to raise a legacy, MSI or MSI-X interrupt
* @start: ops to start the PCI link * @start: ops to start the PCI link
* @stop: ops to stop the PCI link * @stop: ops to stop the PCI link
* @owner: the module owner containing the ops * @owner: the module owner containing the ops
...@@ -48,8 +53,10 @@ struct pci_epc_ops { ...@@ -48,8 +53,10 @@ struct pci_epc_ops {
phys_addr_t addr); phys_addr_t addr);
int (*set_msi)(struct pci_epc *epc, u8 func_no, u8 interrupts); int (*set_msi)(struct pci_epc *epc, u8 func_no, u8 interrupts);
int (*get_msi)(struct pci_epc *epc, u8 func_no); int (*get_msi)(struct pci_epc *epc, u8 func_no);
int (*set_msix)(struct pci_epc *epc, u8 func_no, u16 interrupts);
int (*get_msix)(struct pci_epc *epc, u8 func_no);
int (*raise_irq)(struct pci_epc *epc, u8 func_no, int (*raise_irq)(struct pci_epc *epc, u8 func_no,
enum pci_epc_irq_type type, u8 interrupt_num); enum pci_epc_irq_type type, u16 interrupt_num);
int (*start)(struct pci_epc *epc); int (*start)(struct pci_epc *epc);
void (*stop)(struct pci_epc *epc); void (*stop)(struct pci_epc *epc);
struct module *owner; struct module *owner;
...@@ -95,6 +102,7 @@ struct pci_epc { ...@@ -95,6 +102,7 @@ struct pci_epc {
#define EPC_FEATURE_NO_LINKUP_NOTIFIER BIT(0) #define EPC_FEATURE_NO_LINKUP_NOTIFIER BIT(0)
#define EPC_FEATURE_BAR_MASK (BIT(1) | BIT(2) | BIT(3)) #define EPC_FEATURE_BAR_MASK (BIT(1) | BIT(2) | BIT(3))
#define EPC_FEATURE_MSIX_AVAILABLE BIT(4)
#define EPC_FEATURE_SET_BAR(features, bar) \ #define EPC_FEATURE_SET_BAR(features, bar) \
(features |= (EPC_FEATURE_BAR_MASK & (bar << 1))) (features |= (EPC_FEATURE_BAR_MASK & (bar << 1)))
#define EPC_FEATURE_GET_BAR(features) \ #define EPC_FEATURE_GET_BAR(features) \
...@@ -144,8 +152,10 @@ void pci_epc_unmap_addr(struct pci_epc *epc, u8 func_no, ...@@ -144,8 +152,10 @@ void pci_epc_unmap_addr(struct pci_epc *epc, u8 func_no,
phys_addr_t phys_addr); phys_addr_t phys_addr);
int pci_epc_set_msi(struct pci_epc *epc, u8 func_no, u8 interrupts); int pci_epc_set_msi(struct pci_epc *epc, u8 func_no, u8 interrupts);
int pci_epc_get_msi(struct pci_epc *epc, u8 func_no); int pci_epc_get_msi(struct pci_epc *epc, u8 func_no);
int pci_epc_set_msix(struct pci_epc *epc, u8 func_no, u16 interrupts);
int pci_epc_get_msix(struct pci_epc *epc, u8 func_no);
int pci_epc_raise_irq(struct pci_epc *epc, u8 func_no, int pci_epc_raise_irq(struct pci_epc *epc, u8 func_no,
enum pci_epc_irq_type type, u8 interrupt_num); enum pci_epc_irq_type type, u16 interrupt_num);
int pci_epc_start(struct pci_epc *epc); int pci_epc_start(struct pci_epc *epc);
void pci_epc_stop(struct pci_epc *epc); void pci_epc_stop(struct pci_epc *epc);
struct pci_epc *pci_epc_get(const char *epc_name); struct pci_epc *pci_epc_get(const char *epc_name);
......
...@@ -119,6 +119,7 @@ struct pci_epf { ...@@ -119,6 +119,7 @@ struct pci_epf {
struct pci_epf_header *header; struct pci_epf_header *header;
struct pci_epf_bar bar[6]; struct pci_epf_bar bar[6];
u8 msi_interrupts; u8 msi_interrupts;
u16 msix_interrupts;
u8 func_no; u8 func_no;
struct pci_epc *epc; struct pci_epc *epc;
......
...@@ -16,5 +16,8 @@ ...@@ -16,5 +16,8 @@
#define PCITEST_WRITE _IOW('P', 0x4, unsigned long) #define PCITEST_WRITE _IOW('P', 0x4, unsigned long)
#define PCITEST_READ _IOW('P', 0x5, unsigned long) #define PCITEST_READ _IOW('P', 0x5, unsigned long)
#define PCITEST_COPY _IOW('P', 0x6, unsigned long) #define PCITEST_COPY _IOW('P', 0x6, unsigned long)
#define PCITEST_MSIX _IOW('P', 0x7, int)
#define PCITEST_SET_IRQTYPE _IOW('P', 0x8, int)
#define PCITEST_GET_IRQTYPE _IO('P', 0x9)
#endif /* __UAPI_LINUX_PCITEST_H */ #endif /* __UAPI_LINUX_PCITEST_H */
...@@ -31,12 +31,17 @@ ...@@ -31,12 +31,17 @@
#define BILLION 1E9 #define BILLION 1E9
static char *result[] = { "NOT OKAY", "OKAY" }; static char *result[] = { "NOT OKAY", "OKAY" };
static char *irq[] = { "LEGACY", "MSI", "MSI-X" };
struct pci_test { struct pci_test {
char *device; char *device;
char barnum; char barnum;
bool legacyirq; bool legacyirq;
unsigned int msinum; unsigned int msinum;
unsigned int msixnum;
int irqtype;
bool set_irqtype;
bool get_irqtype;
bool read; bool read;
bool write; bool write;
bool copy; bool copy;
...@@ -65,6 +70,24 @@ static int run_test(struct pci_test *test) ...@@ -65,6 +70,24 @@ static int run_test(struct pci_test *test)
fprintf(stdout, "%s\n", result[ret]); fprintf(stdout, "%s\n", result[ret]);
} }
if (test->set_irqtype) {
ret = ioctl(fd, PCITEST_SET_IRQTYPE, test->irqtype);
fprintf(stdout, "SET IRQ TYPE TO %s:\t\t", irq[test->irqtype]);
if (ret < 0)
fprintf(stdout, "FAILED\n");
else
fprintf(stdout, "%s\n", result[ret]);
}
if (test->get_irqtype) {
ret = ioctl(fd, PCITEST_GET_IRQTYPE);
fprintf(stdout, "GET IRQ TYPE:\t\t");
if (ret < 0)
fprintf(stdout, "FAILED\n");
else
fprintf(stdout, "%s\n", irq[ret]);
}
if (test->legacyirq) { if (test->legacyirq) {
ret = ioctl(fd, PCITEST_LEGACY_IRQ, 0); ret = ioctl(fd, PCITEST_LEGACY_IRQ, 0);
fprintf(stdout, "LEGACY IRQ:\t"); fprintf(stdout, "LEGACY IRQ:\t");
...@@ -83,6 +106,15 @@ static int run_test(struct pci_test *test) ...@@ -83,6 +106,15 @@ static int run_test(struct pci_test *test)
fprintf(stdout, "%s\n", result[ret]); fprintf(stdout, "%s\n", result[ret]);
} }
if (test->msixnum > 0 && test->msixnum <= 2048) {
ret = ioctl(fd, PCITEST_MSIX, test->msixnum);
fprintf(stdout, "MSI-X%d:\t\t", test->msixnum);
if (ret < 0)
fprintf(stdout, "TEST FAILED\n");
else
fprintf(stdout, "%s\n", result[ret]);
}
if (test->write) { if (test->write) {
ret = ioctl(fd, PCITEST_WRITE, test->size); ret = ioctl(fd, PCITEST_WRITE, test->size);
fprintf(stdout, "WRITE (%7ld bytes):\t\t", test->size); fprintf(stdout, "WRITE (%7ld bytes):\t\t", test->size);
...@@ -133,7 +165,7 @@ int main(int argc, char **argv) ...@@ -133,7 +165,7 @@ int main(int argc, char **argv)
/* set default endpoint device */ /* set default endpoint device */
test->device = "/dev/pci-endpoint-test.0"; test->device = "/dev/pci-endpoint-test.0";
while ((c = getopt(argc, argv, "D:b:m:lrwcs:")) != EOF) while ((c = getopt(argc, argv, "D:b:m:x:i:Ilrwcs:")) != EOF)
switch (c) { switch (c) {
case 'D': case 'D':
test->device = optarg; test->device = optarg;
...@@ -151,6 +183,20 @@ int main(int argc, char **argv) ...@@ -151,6 +183,20 @@ int main(int argc, char **argv)
if (test->msinum < 1 || test->msinum > 32) if (test->msinum < 1 || test->msinum > 32)
goto usage; goto usage;
continue; continue;
case 'x':
test->msixnum = atoi(optarg);
if (test->msixnum < 1 || test->msixnum > 2048)
goto usage;
continue;
case 'i':
test->irqtype = atoi(optarg);
if (test->irqtype < 0 || test->irqtype > 2)
goto usage;
test->set_irqtype = true;
continue;
case 'I':
test->get_irqtype = true;
continue;
case 'r': case 'r':
test->read = true; test->read = true;
continue; continue;
...@@ -173,6 +219,9 @@ int main(int argc, char **argv) ...@@ -173,6 +219,9 @@ int main(int argc, char **argv)
"\t-D <dev> PCI endpoint test device {default: /dev/pci-endpoint-test.0}\n" "\t-D <dev> PCI endpoint test device {default: /dev/pci-endpoint-test.0}\n"
"\t-b <bar num> BAR test (bar number between 0..5)\n" "\t-b <bar num> BAR test (bar number between 0..5)\n"
"\t-m <msi num> MSI test (msi number between 1..32)\n" "\t-m <msi num> MSI test (msi number between 1..32)\n"
"\t-x <msix num> \tMSI-X test (msix number between 1..2048)\n"
"\t-i <irq type> \tSet IRQ type (0 - Legacy, 1 - MSI, 2 - MSI-X)\n"
"\t-I Get current IRQ type configured\n"
"\t-l Legacy IRQ test\n" "\t-l Legacy IRQ test\n"
"\t-r Read buffer test\n" "\t-r Read buffer test\n"
"\t-w Write buffer test\n" "\t-w Write buffer test\n"
......
...@@ -16,7 +16,10 @@ echo ...@@ -16,7 +16,10 @@ echo
echo "Interrupt tests" echo "Interrupt tests"
echo echo
pcitest -i 0
pcitest -l pcitest -l
pcitest -i 1
msi=1 msi=1
while [ $msi -lt 33 ] while [ $msi -lt 33 ]
...@@ -26,9 +29,21 @@ do ...@@ -26,9 +29,21 @@ do
done done
echo echo
pcitest -i 2
msix=1
while [ $msix -lt 2049 ]
do
pcitest -x $msix
msix=`expr $msix + 1`
done
echo
echo "Read Tests" echo "Read Tests"
echo echo
pcitest -i 1
pcitest -r -s 1 pcitest -r -s 1
pcitest -r -s 1024 pcitest -r -s 1024
pcitest -r -s 1025 pcitest -r -s 1025
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment