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
This commit is contained in:
commit
0c38011aba
|
@ -15,3 +15,5 @@ subsys_id : don't care
|
|||
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
|
||||
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.
|
|||
* clear_bar: ops to reset the BAR
|
||||
* alloc_addr_space: ops to allocate in PCI controller 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
|
||||
* stop: ops to stop the PCI link
|
||||
|
||||
|
@ -96,7 +96,7 @@ by the PCI endpoint function driver.
|
|||
*) pci_epc_raise_irq()
|
||||
|
||||
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()
|
||||
|
||||
|
|
|
@ -20,6 +20,8 @@ The PCI endpoint test device has the following registers:
|
|||
5) PCI_ENDPOINT_TEST_DST_ADDR
|
||||
6) PCI_ENDPOINT_TEST_SIZE
|
||||
7) PCI_ENDPOINT_TEST_CHECKSUM
|
||||
8) PCI_ENDPOINT_TEST_IRQ_TYPE
|
||||
9) PCI_ENDPOINT_TEST_IRQ_NUMBER
|
||||
|
||||
*) PCI_ENDPOINT_TEST_MAGIC
|
||||
|
||||
|
@ -34,10 +36,10 @@ that the endpoint device must perform.
|
|||
Bitfield Description:
|
||||
Bit 0 : raise legacy IRQ
|
||||
Bit 1 : raise MSI IRQ
|
||||
Bit 2 - 7 : MSI interrupt number
|
||||
Bit 8 : read command (read data from RC buffer)
|
||||
Bit 9 : write command (write data to RC buffer)
|
||||
Bit 10 : copy command (copy data from one RC buffer to another
|
||||
Bit 2 : raise MSI-X IRQ
|
||||
Bit 3 : read command (read data from RC buffer)
|
||||
Bit 4 : write command (write data to RC buffer)
|
||||
Bit 5 : copy command (copy data from one RC buffer to another
|
||||
RC buffer)
|
||||
|
||||
*) PCI_ENDPOINT_TEST_STATUS
|
||||
|
@ -64,3 +66,22 @@ COPY/READ command.
|
|||
|
||||
This register contains the destination address (RC buffer address) for
|
||||
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
|
|||
configurable fields.
|
||||
|
||||
# ls functions/pci_epf_test/func1
|
||||
baseclass_code interrupt_pin revid subsys_vendor_id
|
||||
cache_line_size msi_interrupts subclass_code vendorid
|
||||
deviceid progif_code subsys_id
|
||||
baseclass_code interrupt_pin progif_code subsys_id
|
||||
cache_line_size msi_interrupts revid subsys_vendorid
|
||||
deviceid msix_interrupts subclass_code vendorid
|
||||
|
||||
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
|
||||
|
@ -67,6 +67,7 @@ device, the following commands can be used.
|
|||
# echo 0x104c > functions/pci_epf_test/func1/vendorid
|
||||
# echo 0xb500 > functions/pci_epf_test/func1/deviceid
|
||||
# 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
|
||||
|
||||
|
@ -120,7 +121,9 @@ following commands.
|
|||
|
||||
Interrupt tests
|
||||
|
||||
SET IRQ TYPE TO LEGACY: OKAY
|
||||
LEGACY IRQ: NOT OKAY
|
||||
SET IRQ TYPE TO MSI: OKAY
|
||||
MSI1: OKAY
|
||||
MSI2: OKAY
|
||||
MSI3: OKAY
|
||||
|
@ -153,9 +156,30 @@ following commands.
|
|||
MSI30: NOT OKAY
|
||||
MSI31: 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
|
||||
|
||||
SET IRQ TYPE TO MSI: OKAY
|
||||
READ ( 1 bytes): OKAY
|
||||
READ ( 1024 bytes): OKAY
|
||||
READ ( 1025 bytes): OKAY
|
||||
|
|
|
@ -166,6 +166,7 @@ Code Seq#(hex) Include File Comments
|
|||
'P' all linux/soundcard.h conflict!
|
||||
'P' 60-6F sound/sscape_ioctl.h 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
|
||||
'R' 00-1F linux/random.h conflict!
|
||||
'R' 01 linux/rfkill.h conflict!
|
||||
|
|
|
@ -10,6 +10,7 @@ The PCI driver for the test device performs the following tests
|
|||
*) verifying addresses programmed in BAR
|
||||
*) raise legacy IRQ
|
||||
*) raise MSI IRQ
|
||||
*) raise MSI-X IRQ
|
||||
*) read data
|
||||
*) write data
|
||||
*) copy data
|
||||
|
@ -25,6 +26,11 @@ ioctl
|
|||
PCITEST_LEGACY_IRQ: Tests legacy IRQ
|
||||
PCITEST_MSI: Tests message signalled interrupts. The MSI number
|
||||
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
|
||||
as argument.
|
||||
PCITEST_READ: Perform read tests. The size of the buffer should be passed
|
||||
|
|
|
@ -35,38 +35,45 @@
|
|||
|
||||
#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 IRQ_TYPE_MSI 1
|
||||
#define IRQ_TYPE_MSIX 2
|
||||
|
||||
#define PCI_ENDPOINT_TEST_COMMAND 0x4
|
||||
#define COMMAND_RAISE_LEGACY_IRQ BIT(0)
|
||||
#define COMMAND_RAISE_MSI_IRQ BIT(1)
|
||||
#define MSI_NUMBER_SHIFT 2
|
||||
/* 6 bits for MSI number */
|
||||
#define COMMAND_READ BIT(8)
|
||||
#define COMMAND_WRITE BIT(9)
|
||||
#define COMMAND_COPY BIT(10)
|
||||
#define PCI_ENDPOINT_TEST_MAGIC 0x0
|
||||
|
||||
#define PCI_ENDPOINT_TEST_STATUS 0x8
|
||||
#define STATUS_READ_SUCCESS BIT(0)
|
||||
#define STATUS_READ_FAIL BIT(1)
|
||||
#define STATUS_WRITE_SUCCESS BIT(2)
|
||||
#define STATUS_WRITE_FAIL BIT(3)
|
||||
#define STATUS_COPY_SUCCESS BIT(4)
|
||||
#define STATUS_COPY_FAIL BIT(5)
|
||||
#define STATUS_IRQ_RAISED BIT(6)
|
||||
#define STATUS_SRC_ADDR_INVALID BIT(7)
|
||||
#define STATUS_DST_ADDR_INVALID BIT(8)
|
||||
#define PCI_ENDPOINT_TEST_COMMAND 0x4
|
||||
#define COMMAND_RAISE_LEGACY_IRQ BIT(0)
|
||||
#define COMMAND_RAISE_MSI_IRQ BIT(1)
|
||||
#define COMMAND_RAISE_MSIX_IRQ BIT(2)
|
||||
#define COMMAND_READ BIT(3)
|
||||
#define COMMAND_WRITE BIT(4)
|
||||
#define COMMAND_COPY BIT(5)
|
||||
|
||||
#define PCI_ENDPOINT_TEST_LOWER_SRC_ADDR 0xc
|
||||
#define PCI_ENDPOINT_TEST_STATUS 0x8
|
||||
#define STATUS_READ_SUCCESS BIT(0)
|
||||
#define STATUS_READ_FAIL BIT(1)
|
||||
#define STATUS_WRITE_SUCCESS BIT(2)
|
||||
#define STATUS_WRITE_FAIL BIT(3)
|
||||
#define STATUS_COPY_SUCCESS BIT(4)
|
||||
#define STATUS_COPY_FAIL BIT(5)
|
||||
#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_LOWER_DST_ADDR 0x14
|
||||
#define PCI_ENDPOINT_TEST_UPPER_DST_ADDR 0x18
|
||||
|
||||
#define PCI_ENDPOINT_TEST_SIZE 0x1c
|
||||
#define PCI_ENDPOINT_TEST_CHECKSUM 0x20
|
||||
#define PCI_ENDPOINT_TEST_SIZE 0x1c
|
||||
#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);
|
||||
|
||||
|
@ -77,6 +84,10 @@ static bool no_msi;
|
|||
module_param(no_msi, bool, 0444);
|
||||
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 {
|
||||
BAR_0,
|
||||
BAR_1,
|
||||
|
@ -103,7 +114,7 @@ struct pci_endpoint_test {
|
|||
struct pci_endpoint_test_data {
|
||||
enum pci_barno test_reg_bar;
|
||||
size_t alignment;
|
||||
bool no_msi;
|
||||
int irq_type;
|
||||
};
|
||||
|
||||
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)
|
|||
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,
|
||||
enum pci_barno barno)
|
||||
{
|
||||
|
@ -179,6 +284,9 @@ static bool pci_endpoint_test_legacy_irq(struct pci_endpoint_test *test)
|
|||
{
|
||||
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,
|
||||
COMMAND_RAISE_LEGACY_IRQ);
|
||||
val = wait_for_completion_timeout(&test->irq_raised,
|
||||
|
@ -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,
|
||||
u8 msi_num)
|
||||
u16 msi_num, bool msix)
|
||||
{
|
||||
u32 val;
|
||||
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,
|
||||
msi_num << MSI_NUMBER_SHIFT |
|
||||
COMMAND_RAISE_MSI_IRQ);
|
||||
msix == false ? COMMAND_RAISE_MSI_IRQ :
|
||||
COMMAND_RAISE_MSIX_IRQ);
|
||||
val = wait_for_completion_timeout(&test->irq_raised,
|
||||
msecs_to_jiffies(1000));
|
||||
if (!val)
|
||||
|
@ -230,6 +342,11 @@ static bool pci_endpoint_test_copy(struct pci_endpoint_test *test, size_t size)
|
|||
if (size > SIZE_MAX - alignment)
|
||||
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_phys_addr, GFP_KERNEL);
|
||||
if (!orig_src_addr) {
|
||||
|
@ -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,
|
||||
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,
|
||||
1 << MSI_NUMBER_SHIFT | COMMAND_COPY);
|
||||
COMMAND_COPY);
|
||||
|
||||
wait_for_completion(&test->irq_raised);
|
||||
|
||||
|
@ -318,6 +437,11 @@ static bool pci_endpoint_test_write(struct pci_endpoint_test *test, size_t size)
|
|||
if (size > SIZE_MAX - alignment)
|
||||
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,
|
||||
GFP_KERNEL);
|
||||
if (!orig_addr) {
|
||||
|
@ -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_IRQ_TYPE, irq_type);
|
||||
pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_IRQ_NUMBER, 1);
|
||||
pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_COMMAND,
|
||||
1 << MSI_NUMBER_SHIFT | COMMAND_READ);
|
||||
COMMAND_READ);
|
||||
|
||||
wait_for_completion(&test->irq_raised);
|
||||
|
||||
|
@ -379,6 +505,11 @@ static bool pci_endpoint_test_read(struct pci_endpoint_test *test, size_t size)
|
|||
if (size > SIZE_MAX - alignment)
|
||||
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,
|
||||
GFP_KERNEL);
|
||||
if (!orig_addr) {
|
||||
|
@ -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_IRQ_TYPE, irq_type);
|
||||
pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_IRQ_NUMBER, 1);
|
||||
pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_COMMAND,
|
||||
1 << MSI_NUMBER_SHIFT | COMMAND_WRITE);
|
||||
COMMAND_WRITE);
|
||||
|
||||
wait_for_completion(&test->irq_raised);
|
||||
|
||||
|
@ -417,6 +550,38 @@ static bool pci_endpoint_test_read(struct pci_endpoint_test *test, size_t size)
|
|||
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,
|
||||
unsigned long arg)
|
||||
{
|
||||
|
@ -436,7 +601,8 @@ static long pci_endpoint_test_ioctl(struct file *file, unsigned int cmd,
|
|||
ret = pci_endpoint_test_legacy_irq(test);
|
||||
break;
|
||||
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;
|
||||
case PCITEST_WRITE:
|
||||
ret = pci_endpoint_test_write(test, arg);
|
||||
|
@ -447,6 +613,12 @@ static long pci_endpoint_test_ioctl(struct file *file, unsigned int cmd,
|
|||
case PCITEST_COPY:
|
||||
ret = pci_endpoint_test_copy(test, arg);
|
||||
break;
|
||||
case PCITEST_SET_IRQTYPE:
|
||||
ret = pci_endpoint_test_set_irq(test, arg);
|
||||
break;
|
||||
case PCITEST_GET_IRQTYPE:
|
||||
ret = irq_type;
|
||||
break;
|
||||
}
|
||||
|
||||
ret:
|
||||
|
@ -462,9 +634,7 @@ static const struct file_operations pci_endpoint_test_fops = {
|
|||
static int pci_endpoint_test_probe(struct pci_dev *pdev,
|
||||
const struct pci_device_id *ent)
|
||||
{
|
||||
int i;
|
||||
int err;
|
||||
int irq = 0;
|
||||
int id;
|
||||
char name[20];
|
||||
enum pci_barno bar;
|
||||
|
@ -486,11 +656,14 @@ static int pci_endpoint_test_probe(struct pci_dev *pdev,
|
|||
test->alignment = 0;
|
||||
test->pdev = pdev;
|
||||
|
||||
if (no_msi)
|
||||
irq_type = IRQ_TYPE_LEGACY;
|
||||
|
||||
data = (struct pci_endpoint_test_data *)ent->driver_data;
|
||||
if (data) {
|
||||
test_reg_bar = data->test_reg_bar;
|
||||
test->alignment = data->alignment;
|
||||
no_msi = data->no_msi;
|
||||
irq_type = data->irq_type;
|
||||
}
|
||||
|
||||
init_completion(&test->irq_raised);
|
||||
|
@ -510,28 +683,11 @@ static int pci_endpoint_test_probe(struct pci_dev *pdev,
|
|||
|
||||
pci_set_master(pdev);
|
||||
|
||||
if (!no_msi) {
|
||||
irq = pci_alloc_irq_vectors(pdev, 1, 32, PCI_IRQ_MSI);
|
||||
if (irq < 0)
|
||||
dev_err(dev, "Failed to get MSI interrupts\n");
|
||||
test->num_irqs = irq;
|
||||
}
|
||||
if (!pci_endpoint_test_alloc_irq_vectors(test, irq_type))
|
||||
goto err_disable_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++) {
|
||||
err = devm_request_irq(dev, pci_irq_vector(pdev, i),
|
||||
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);
|
||||
}
|
||||
if (!pci_endpoint_test_request_irq(test))
|
||||
goto err_disable_irq;
|
||||
|
||||
for (bar = BAR_0; bar <= BAR_5; bar++) {
|
||||
if (pci_resource_flags(pdev, bar) & IORESOURCE_MEM) {
|
||||
|
@ -590,12 +746,10 @@ static int pci_endpoint_test_probe(struct pci_dev *pdev,
|
|||
if (test->bar[bar])
|
||||
pci_iounmap(pdev, test->bar[bar]);
|
||||
}
|
||||
pci_endpoint_test_release_irq(test);
|
||||
|
||||
for (i = 0; i < irq; i++)
|
||||
devm_free_irq(&pdev->dev, pci_irq_vector(pdev, i), test);
|
||||
|
||||
err_disable_msi:
|
||||
pci_disable_msi(pdev);
|
||||
err_disable_irq:
|
||||
pci_endpoint_test_free_irq_vectors(test);
|
||||
pci_release_regions(pdev);
|
||||
|
||||
err_disable_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)
|
||||
{
|
||||
int id;
|
||||
int i;
|
||||
enum pci_barno bar;
|
||||
struct pci_endpoint_test *test = pci_get_drvdata(pdev);
|
||||
struct miscdevice *misc_device = &test->miscdev;
|
||||
|
@ -624,9 +777,10 @@ static void pci_endpoint_test_remove(struct pci_dev *pdev)
|
|||
if (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_disable_msi(pdev);
|
||||
|
||||
pci_endpoint_test_release_irq(test);
|
||||
pci_endpoint_test_free_irq_vectors(test);
|
||||
|
||||
pci_release_regions(pdev);
|
||||
pci_disable_device(pdev);
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
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 dra7xx_pcie *dra7xx = to_dra7xx_pcie(pci);
|
||||
|
|
|
@ -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;
|
||||
|
||||
ret = dw_pcie_host_init(pp);
|
||||
|
|
|
@ -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;
|
||||
|
||||
ret = dw_pcie_host_init(pp);
|
||||
|
|
|
@ -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;
|
||||
ret = ks_dw_pcie_host_init(ks_pcie, ks_pcie->msi_intc_np);
|
||||
if (ret) {
|
||||
|
|
|
@ -172,7 +172,6 @@ static int armada8k_add_pcie_port(struct armada8k_pcie *pcie,
|
|||
struct device *dev = &pdev->dev;
|
||||
int ret;
|
||||
|
||||
pp->root_bus_nr = -1;
|
||||
pp->ops = &armada8k_pcie_host_ops;
|
||||
|
||||
pp->irq = platform_get_irq(pdev, 0);
|
||||
|
|
|
@ -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;
|
||||
|
||||
ret = dw_pcie_host_init(pp);
|
||||
|
@ -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,
|
||||
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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
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,
|
||||
struct pci_epf_header *hdr)
|
||||
{
|
||||
|
@ -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)
|
||||
{
|
||||
int val;
|
||||
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 (!(val & MSI_CAP_MSI_EN_MASK))
|
||||
if (!ep->msi_cap)
|
||||
return -EINVAL;
|
||||
|
||||
val = (val & MSI_CAP_MME_MASK) >> MSI_CAP_MME_SHIFT;
|
||||
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 encode_int)
|
||||
static int dw_pcie_ep_set_msi(struct pci_epc *epc, u8 func_no, u8 interrupts)
|
||||
{
|
||||
int val;
|
||||
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);
|
||||
val &= ~MSI_CAP_MMC_MASK;
|
||||
val |= (encode_int << MSI_CAP_MMC_SHIFT) & MSI_CAP_MMC_MASK;
|
||||
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, MSI_MESSAGE_CONTROL, val);
|
||||
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;
|
||||
|
||||
if (!ep->msix_cap)
|
||||
return -EINVAL;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
static int dw_pcie_ep_set_msix(struct pci_epc *epc, u8 func_no, u16 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->msix_cap)
|
||||
return -EINVAL;
|
||||
|
||||
reg = ep->msix_cap + PCI_MSIX_FLAGS;
|
||||
val = dw_pcie_readw_dbi(pci, reg);
|
||||
val &= ~PCI_MSIX_FLAGS_QSIZE;
|
||||
val |= interrupts;
|
||||
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_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);
|
||||
|
||||
|
@ -282,32 +363,52 @@ static const struct pci_epc_ops epc_ops = {
|
|||
.unmap_addr = dw_pcie_ep_unmap_addr,
|
||||
.set_msi = dw_pcie_ep_set_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,
|
||||
.start = dw_pcie_ep_start,
|
||||
.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,
|
||||
u8 interrupt_num)
|
||||
{
|
||||
struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
|
||||
struct pci_epc *epc = ep->epc;
|
||||
u16 msg_ctrl, msg_data;
|
||||
u32 msg_addr_lower, msg_addr_upper;
|
||||
u32 msg_addr_lower, msg_addr_upper, reg;
|
||||
u64 msg_addr;
|
||||
bool has_upper;
|
||||
int ret;
|
||||
|
||||
if (!ep->msi_cap)
|
||||
return -EINVAL;
|
||||
|
||||
/* 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);
|
||||
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) {
|
||||
msg_addr_upper = dw_pcie_readl_dbi(pci, MSI_MESSAGE_ADDR_U32);
|
||||
msg_data = dw_pcie_readw_dbi(pci, MSI_MESSAGE_DATA_64);
|
||||
reg = ep->msi_cap + PCI_MSI_ADDRESS_HI;
|
||||
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 {
|
||||
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;
|
||||
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,
|
|||
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)
|
||||
{
|
||||
struct pci_epc *epc = ep->epc;
|
||||
|
@ -386,15 +545,18 @@ int dw_pcie_ep_init(struct dw_pcie_ep *ep)
|
|||
return -ENOMEM;
|
||||
ep->outbound_addr = addr;
|
||||
|
||||
if (ep->ops->ep_init)
|
||||
ep->ops->ep_init(ep);
|
||||
|
||||
epc = devm_pci_epc_create(dev, &epc_ops);
|
||||
if (IS_ERR(epc)) {
|
||||
dev_err(dev, "Failed to create epc device\n");
|
||||
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);
|
||||
if (ret < 0)
|
||||
epc->max_functions = 1;
|
||||
|
@ -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,
|
||||
epc->mem->page_size);
|
||||
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;
|
||||
}
|
||||
ep->msi_cap = dw_pcie_ep_find_capability(pci, PCI_CAP_ID_MSI);
|
||||
|
||||
epc->features = EPC_FEATURE_NO_LINKUP_NOTIFIER;
|
||||
EPC_FEATURE_SET_BAR(epc->features, BAR_0);
|
||||
ep->msix_cap = dw_pcie_ep_find_capability(pci, PCI_CAP_ID_MSIX);
|
||||
|
||||
ep->epc = epc;
|
||||
epc_set_drvdata(epc, ep);
|
||||
dw_pcie_setup(pci);
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
|
||||
struct pci_epc *epc = ep->epc;
|
||||
enum pci_barno bar;
|
||||
|
||||
for (bar = BAR_0; bar <= BAR_5; 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,
|
||||
enum pci_epc_irq_type type,
|
||||
u8 interrupt_num)
|
||||
u16 interrupt_num)
|
||||
{
|
||||
struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
|
||||
|
||||
switch (type) {
|
||||
case PCI_EPC_IRQ_LEGACY:
|
||||
dev_err(pci->dev, "EP cannot trigger legacy IRQs\n");
|
||||
return -EINVAL;
|
||||
return dw_pcie_ep_raise_legacy_irq(ep, func_no);
|
||||
case PCI_EPC_IRQ_MSI:
|
||||
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:
|
||||
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,
|
|||
return pp->msi_irq;
|
||||
}
|
||||
|
||||
pp->root_bus_nr = -1;
|
||||
pp->ops = &dw_plat_pcie_host_ops;
|
||||
|
||||
ret = dw_pcie_host_init(pp);
|
||||
|
|
|
@ -96,17 +96,6 @@
|
|||
#define PCIE_GET_ATU_INB_UNR_REG_OFFSET(region) \
|
||||
((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_PER_CTRL 32
|
||||
#define MAX_MSI_CTRLS (MAX_MSI_IRQS / MAX_MSI_IRQS_PER_CTRL)
|
||||
|
@ -191,7 +180,7 @@ enum dw_pcie_as_type {
|
|||
struct dw_pcie_ep_ops {
|
||||
void (*ep_init)(struct dw_pcie_ep *ep);
|
||||
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 {
|
||||
|
@ -208,6 +197,8 @@ struct dw_pcie_ep {
|
|||
u32 num_ob_windows;
|
||||
void __iomem *msi_mem;
|
||||
phys_addr_t msi_mem_phys;
|
||||
u8 msi_cap; /* MSI capability offset */
|
||||
u8 msix_cap; /* MSI-X capability offset */
|
||||
};
|
||||
|
||||
struct dw_pcie_ops {
|
||||
|
@ -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);
|
||||
int dw_pcie_ep_init(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,
|
||||
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);
|
||||
#else
|
||||
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)
|
|||
{
|
||||
}
|
||||
|
||||
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,
|
||||
u8 interrupt_num)
|
||||
{
|
||||
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)
|
||||
{
|
||||
}
|
||||
|
|
|
@ -420,7 +420,6 @@ static int histb_pcie_probe(struct platform_device *pdev)
|
|||
phy_init(hipcie->phy);
|
||||
}
|
||||
|
||||
pp->root_bus_nr = -1;
|
||||
pp->ops = &histb_pcie_host_ops;
|
||||
|
||||
platform_set_drvdata(pdev, hipcie);
|
||||
|
|
|
@ -430,6 +430,9 @@ static int kirin_pcie_host_init(struct pcie_port *pp)
|
|||
{
|
||||
kirin_pcie_establish_link(pp);
|
||||
|
||||
if (IS_ENABLED(CONFIG_PCI_MSI))
|
||||
dw_pcie_msi_init(pp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -445,9 +448,34 @@ static const struct dw_pcie_host_ops kirin_pcie_host_ops = {
|
|||
.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,
|
||||
struct platform_device *pdev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = kirin_pcie_add_msi(pci, pdev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
pci->pp.ops = &kirin_pcie_host_ops;
|
||||
|
||||
return dw_pcie_host_init(&pci->pp);
|
||||
|
|
|
@ -1251,7 +1251,6 @@ static int qcom_pcie_probe(struct platform_device *pdev)
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
pp->root_bus_nr = -1;
|
||||
pp->ops = &qcom_pcie_dw_ops;
|
||||
|
||||
if (IS_ENABLED(CONFIG_PCI_MSI)) {
|
||||
|
|
|
@ -210,7 +210,6 @@ static int spear13xx_add_pcie_port(struct spear13xx_pcie *spear13xx_pcie,
|
|||
return ret;
|
||||
}
|
||||
|
||||
pp->root_bus_nr = -1;
|
||||
pp->ops = &spear13xx_pcie_host_ops;
|
||||
|
||||
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,
|
|||
}
|
||||
|
||||
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);
|
||||
|
||||
|
|
|
@ -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,
|
||||
enum pci_epc_irq_type type,
|
||||
u8 interrupt_num)
|
||||
u16 interrupt_num)
|
||||
{
|
||||
struct rockchip_pcie_ep *ep = epc_get_drvdata(epc);
|
||||
|
||||
|
|
|
@ -18,13 +18,16 @@
|
|||
#include <linux/pci-epf.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_MSI_IRQ BIT(1)
|
||||
#define MSI_NUMBER_SHIFT 2
|
||||
#define MSI_NUMBER_MASK (0x3f << MSI_NUMBER_SHIFT)
|
||||
#define COMMAND_READ BIT(8)
|
||||
#define COMMAND_WRITE BIT(9)
|
||||
#define COMMAND_COPY BIT(10)
|
||||
#define COMMAND_RAISE_MSIX_IRQ BIT(2)
|
||||
#define COMMAND_READ BIT(3)
|
||||
#define COMMAND_WRITE BIT(4)
|
||||
#define COMMAND_COPY BIT(5)
|
||||
|
||||
#define STATUS_READ_SUCCESS BIT(0)
|
||||
#define STATUS_READ_FAIL BIT(1)
|
||||
|
@ -45,6 +48,7 @@ struct pci_epf_test {
|
|||
struct pci_epf *epf;
|
||||
enum pci_barno test_reg_bar;
|
||||
bool linkup_notifier;
|
||||
bool msix_available;
|
||||
struct delayed_work cmd_handler;
|
||||
};
|
||||
|
||||
|
@ -56,6 +60,8 @@ struct pci_epf_test_reg {
|
|||
u64 dst_addr;
|
||||
u32 size;
|
||||
u32 checksum;
|
||||
u32 irq_type;
|
||||
u32 irq_number;
|
||||
} __packed;
|
||||
|
||||
static struct pci_epf_header test_header = {
|
||||
|
@ -244,31 +250,42 @@ static int pci_epf_test_write(struct pci_epf_test *epf_test)
|
|||
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 device *dev = &epf->dev;
|
||||
struct pci_epc *epc = epf->epc;
|
||||
enum pci_barno test_reg_bar = epf_test->test_reg_bar;
|
||||
struct pci_epf_test_reg *reg = epf_test->reg[test_reg_bar];
|
||||
|
||||
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);
|
||||
else
|
||||
break;
|
||||
case IRQ_TYPE_MSI:
|
||||
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)
|
||||
{
|
||||
int ret;
|
||||
u8 irq;
|
||||
u8 msi_count;
|
||||
int count;
|
||||
u32 command;
|
||||
struct pci_epf_test *epf_test = container_of(work, struct pci_epf_test,
|
||||
cmd_handler.work);
|
||||
struct pci_epf *epf = epf_test->epf;
|
||||
struct device *dev = &epf->dev;
|
||||
struct pci_epc *epc = epf->epc;
|
||||
enum pci_barno test_reg_bar = epf_test->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)
|
|||
reg->command = 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) {
|
||||
reg->status = STATUS_IRQ_RAISED;
|
||||
|
@ -294,7 +314,8 @@ static void pci_epf_test_cmd_handler(struct work_struct *work)
|
|||
reg->status |= STATUS_WRITE_FAIL;
|
||||
else
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -304,7 +325,8 @@ static void pci_epf_test_cmd_handler(struct work_struct *work)
|
|||
reg->status |= STATUS_READ_SUCCESS;
|
||||
else
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -314,16 +336,28 @@ static void pci_epf_test_cmd_handler(struct work_struct *work)
|
|||
reg->status |= STATUS_COPY_SUCCESS;
|
||||
else
|
||||
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;
|
||||
}
|
||||
|
||||
if (command & COMMAND_RAISE_MSI_IRQ) {
|
||||
msi_count = pci_epc_get_msi(epc, epf->func_no);
|
||||
if (irq > msi_count || msi_count <= 0)
|
||||
count = pci_epc_get_msi(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_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;
|
||||
}
|
||||
|
||||
|
@ -440,6 +474,8 @@ static int pci_epf_test_bind(struct pci_epf *epf)
|
|||
else
|
||||
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);
|
||||
|
||||
ret = pci_epc_write_header(epc, epf->func_no, header);
|
||||
|
@ -457,8 +493,18 @@ static int pci_epf_test_bind(struct pci_epf *epf)
|
|||
return ret;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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)
|
||||
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,
|
|||
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_W_u16(vendorid)
|
||||
|
||||
|
@ -327,6 +349,7 @@ CONFIGFS_ATTR(pci_epf_, subsys_vendor_id);
|
|||
CONFIGFS_ATTR(pci_epf_, subsys_id);
|
||||
CONFIGFS_ATTR(pci_epf_, interrupt_pin);
|
||||
CONFIGFS_ATTR(pci_epf_, msi_interrupts);
|
||||
CONFIGFS_ATTR(pci_epf_, msix_interrupts);
|
||||
|
||||
static struct configfs_attribute *pci_epf_attrs[] = {
|
||||
&pci_epf_attr_vendorid,
|
||||
|
@ -340,6 +363,7 @@ static struct configfs_attribute *pci_epf_attrs[] = {
|
|||
&pci_epf_attr_subsys_id,
|
||||
&pci_epf_attr_interrupt_pin,
|
||||
&pci_epf_attr_msi_interrupts,
|
||||
&pci_epf_attr_msix_interrupts,
|
||||
NULL,
|
||||
};
|
||||
|
||||
|
|
|
@ -131,13 +131,13 @@ EXPORT_SYMBOL_GPL(pci_epc_start);
|
|||
* pci_epc_raise_irq() - interrupt the host system
|
||||
* @epc: the EPC device which has to interrupt the host
|
||||
* @func_no: the endpoint function number in the EPC device
|
||||
* @type: specify the type of interrupt; legacy or MSI
|
||||
* @interrupt_num: the MSI interrupt number
|
||||
* @type: specify the type of interrupt; legacy, MSI or MSI-X
|
||||
* @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,
|
||||
enum pci_epc_irq_type type, u8 interrupt_num)
|
||||
enum pci_epc_irq_type type, u16 interrupt_num)
|
||||
{
|
||||
int ret;
|
||||
unsigned long flags;
|
||||
|
@ -201,7 +201,8 @@ int pci_epc_set_msi(struct pci_epc *epc, u8 func_no, u8 interrupts)
|
|||
u8 encode_int;
|
||||
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;
|
||||
|
||||
if (!epc->ops->set_msi)
|
||||
|
@ -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);
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @epc: the EPC device on which address is allocated
|
||||
|
|
|
@ -17,6 +17,7 @@ enum pci_epc_irq_type {
|
|||
PCI_EPC_IRQ_UNKNOWN,
|
||||
PCI_EPC_IRQ_LEGACY,
|
||||
PCI_EPC_IRQ_MSI,
|
||||
PCI_EPC_IRQ_MSIX,
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -30,7 +31,11 @@ enum pci_epc_irq_type {
|
|||
* capability register
|
||||
* @get_msi: ops to get the number of MSI interrupts allocated by the RC from
|
||||
* 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
|
||||
* @stop: ops to stop the PCI link
|
||||
* @owner: the module owner containing the ops
|
||||
|
@ -48,8 +53,10 @@ struct pci_epc_ops {
|
|||
phys_addr_t addr);
|
||||
int (*set_msi)(struct pci_epc *epc, u8 func_no, u8 interrupts);
|
||||
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,
|
||||
enum pci_epc_irq_type type, u8 interrupt_num);
|
||||
enum pci_epc_irq_type type, u16 interrupt_num);
|
||||
int (*start)(struct pci_epc *epc);
|
||||
void (*stop)(struct pci_epc *epc);
|
||||
struct module *owner;
|
||||
|
@ -95,6 +102,7 @@ struct pci_epc {
|
|||
|
||||
#define EPC_FEATURE_NO_LINKUP_NOTIFIER BIT(0)
|
||||
#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) \
|
||||
(features |= (EPC_FEATURE_BAR_MASK & (bar << 1)))
|
||||
#define EPC_FEATURE_GET_BAR(features) \
|
||||
|
@ -144,8 +152,10 @@ void pci_epc_unmap_addr(struct pci_epc *epc, u8 func_no,
|
|||
phys_addr_t phys_addr);
|
||||
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_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,
|
||||
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);
|
||||
void pci_epc_stop(struct pci_epc *epc);
|
||||
struct pci_epc *pci_epc_get(const char *epc_name);
|
||||
|
|
|
@ -119,6 +119,7 @@ struct pci_epf {
|
|||
struct pci_epf_header *header;
|
||||
struct pci_epf_bar bar[6];
|
||||
u8 msi_interrupts;
|
||||
u16 msix_interrupts;
|
||||
u8 func_no;
|
||||
|
||||
struct pci_epc *epc;
|
||||
|
|
|
@ -16,5 +16,8 @@
|
|||
#define PCITEST_WRITE _IOW('P', 0x4, unsigned long)
|
||||
#define PCITEST_READ _IOW('P', 0x5, 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 */
|
||||
|
|
|
@ -31,12 +31,17 @@
|
|||
#define BILLION 1E9
|
||||
|
||||
static char *result[] = { "NOT OKAY", "OKAY" };
|
||||
static char *irq[] = { "LEGACY", "MSI", "MSI-X" };
|
||||
|
||||
struct pci_test {
|
||||
char *device;
|
||||
char barnum;
|
||||
bool legacyirq;
|
||||
unsigned int msinum;
|
||||
unsigned int msixnum;
|
||||
int irqtype;
|
||||
bool set_irqtype;
|
||||
bool get_irqtype;
|
||||
bool read;
|
||||
bool write;
|
||||
bool copy;
|
||||
|
@ -65,6 +70,24 @@ static int run_test(struct pci_test *test)
|
|||
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) {
|
||||
ret = ioctl(fd, PCITEST_LEGACY_IRQ, 0);
|
||||
fprintf(stdout, "LEGACY IRQ:\t");
|
||||
|
@ -83,6 +106,15 @@ static int run_test(struct pci_test *test)
|
|||
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) {
|
||||
ret = ioctl(fd, PCITEST_WRITE, test->size);
|
||||
fprintf(stdout, "WRITE (%7ld bytes):\t\t", test->size);
|
||||
|
@ -133,7 +165,7 @@ int main(int argc, char **argv)
|
|||
/* set default endpoint device */
|
||||
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) {
|
||||
case 'D':
|
||||
test->device = optarg;
|
||||
|
@ -151,6 +183,20 @@ int main(int argc, char **argv)
|
|||
if (test->msinum < 1 || test->msinum > 32)
|
||||
goto usage;
|
||||
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':
|
||||
test->read = true;
|
||||
continue;
|
||||
|
@ -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-b <bar num> BAR test (bar number between 0..5)\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-r Read buffer test\n"
|
||||
"\t-w Write buffer test\n"
|
||||
|
|
|
@ -16,7 +16,10 @@ echo
|
|||
echo "Interrupt tests"
|
||||
echo
|
||||
|
||||
pcitest -i 0
|
||||
pcitest -l
|
||||
|
||||
pcitest -i 1
|
||||
msi=1
|
||||
|
||||
while [ $msi -lt 33 ]
|
||||
|
@ -26,9 +29,21 @@ do
|
|||
done
|
||||
echo
|
||||
|
||||
pcitest -i 2
|
||||
msix=1
|
||||
|
||||
while [ $msix -lt 2049 ]
|
||||
do
|
||||
pcitest -x $msix
|
||||
msix=`expr $msix + 1`
|
||||
done
|
||||
echo
|
||||
|
||||
echo "Read Tests"
|
||||
echo
|
||||
|
||||
pcitest -i 1
|
||||
|
||||
pcitest -r -s 1
|
||||
pcitest -r -s 1024
|
||||
pcitest -r -s 1025
|
||||
|
|
Loading…
Reference in New Issue