Commit 3085a159 authored by A. Hahn's avatar A. Hahn

pcie_wb: added pmc/pci support

parent 99640fa5
......@@ -27,7 +27,9 @@
#error "unknown machine byte order (endian)"
#endif
static unsigned int debug = 0;
static unsigned int debug = 0; /* module parameter, enable debug prints */
static unsigned int debug_irq = 0; /* module parameter, enable debug prints in irq handler*/
static unsigned int pmcintx = 0; /* module parameter, force INTx interrupt for PCI/PMC card */
#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,28)
......@@ -48,8 +50,18 @@ static void compat_pci_clear_master(struct pci_dev *dev)
static void pcie_int_enable(struct pcie_wb_dev *dev, int on)
{
int enable = on && !dev->msi;
iowrite32((enable?0x20000000UL:0) + 0x10000000UL, dev->pci_res[0].addr + CONTROL_REGISTER_HIGH);
int enable;
/* enable/disable interrupts for pmc device */
if(dev->pci_dev->device == PMC_WB_DEVICE_ID){
iowrite32((on ? 0x20000000UL:0) + 0x10000000UL, dev->pci_res[0].addr + CONTROL_REGISTER_HIGH);
ioread32(dev->pci_res[0].addr + CONTROL_REGISTER_HIGH); /*dummy read to be sure that write was executed*/
}
/* enable/disable interrupts for pcie device */
else{
enable = on && !dev->msi;
iowrite32((enable ? 0x20000000UL:0) + 0x10000000UL, dev->pci_res[0].addr + CONTROL_REGISTER_HIGH);
}
}
static void wb_cycle(struct wishbone* wb, int on)
......@@ -133,15 +145,15 @@ static void wb_write(struct wishbone* wb, wb_addr_t addr, wb_data_t data)
switch (dev->width) {
case 4:
if (unlikely(debug)) printk(KERN_ALERT PCIE_WB ": iowrite32(0x%x, 0x%x)\n", data, addr & ~3);
if (unlikely(debug)) printk(KERN_DEBUG PCIE_WB ": iowrite32 A:0x%08x, D:0x%08x\n", addr & ~3, data);
iowrite32(data, window + (addr & WINDOW_LOW));
break;
case 2:
if (unlikely(debug)) printk(KERN_ALERT PCIE_WB ": iowrite16(0x%x, 0x%x)\n", data >> dev->shift, (addr & ~3) + dev->low_addr);
if (unlikely(debug)) printk(KERN_DEBUG PCIE_WB ": iowrite16 A:0x%08x, D:0x%08x\n", (addr & ~3) + dev->low_addr, data >> dev->shift);
iowrite16(data >> dev->shift, window + (addr & WINDOW_LOW) + dev->low_addr);
break;
case 1:
if (unlikely(debug)) printk(KERN_ALERT PCIE_WB ": iowrite8(0x%x, 0x%x)\n", data >> dev->shift, (addr & ~3) + dev->low_addr);
if (unlikely(debug)) printk(KERN_DEBUG PCIE_WB ": iowrite8 A:0x%08x, D:0x%08x\n", (addr & ~3) + dev->low_addr, data >> dev->shift);
iowrite8 (data >> dev->shift, window + (addr & WINDOW_LOW) + dev->low_addr);
break;
}
......@@ -261,8 +273,22 @@ static const struct wishbone_operations wb_ops = {
static irqreturn_t irq_handler(int irq, void *dev_id)
{
struct pcie_wb_dev *dev = dev_id;
unsigned char* wb_conf;
uint32_t wb_cfg_data;
/* if card is PMC with IntX interrupts */
/* it is likely that irq line is shared */
if (!(dev->msi) && (dev->pci_dev->device == PMC_WB_DEVICE_ID)){
/* check that pmc card has requested IRQ */
/* if it has not then exit */
wb_conf = dev->pci_res[2].addr;
wb_cfg_data = ioread32(wb_conf + WB_CONF_ISR_REG);
if (!(wb_cfg_data & WB_CONF_IRQ_STATUS_MASK)){
return IRQ_NONE;
}
}
pcie_int_enable(dev, 0);
pcie_int_enable(dev, 0); /* disable IRQ on Etherbone layer - Etherbone */
wishbone_slave_ready(&dev->wb);
return IRQ_HANDLED;
......@@ -275,7 +301,7 @@ static int setup_bar(struct pci_dev* pdev, struct pcie_wb_resource* res, int bar
res->size = res->end - res->start + 1;
if (debug)
printk(KERN_ALERT PCIE_WB "/BAR%d 0x%lx - 0x%lx\n", bar, res->start, res->end);
printk(KERN_INFO PCIE_WB "/BAR%d 0x%lx - 0x%lx\n", bar, res->start, res->end);
if ((pci_resource_flags(pdev, 0) & IORESOURCE_MEM) == 0) {
printk(KERN_ALERT PCIE_WB "/BAR%d is not a memory resource\n", bar);
......@@ -311,12 +337,21 @@ static int probe(struct pci_dev *pdev, const struct pci_device_id *id)
* reading IRQ
* register char dev
*/
u8 revision;
struct pcie_wb_dev *dev;
unsigned char* control;
unsigned char* wb_conf;
if(unlikely(debug)){
printk(KERN_INFO PCIE_WB ":-----------------------------\n");
printk(KERN_INFO PCIE_WB ": PCI Device info\n");
printk(KERN_INFO PCIE_WB ": vendor : %04x\n", pdev->vendor);
printk(KERN_INFO PCIE_WB ": device : %04x\n", pdev->device);
printk(KERN_INFO PCIE_WB ": PCIe capable : %04x\n", pdev->pcie_cap);
printk(KERN_INFO PCIE_WB ": irq number : %d\n" , pdev->irq);
printk(KERN_INFO PCIE_WB ":-----------------------------\n");
}
pci_read_config_byte(pdev, PCI_REVISION_ID, &revision);
if (revision != 0x01) {
if (pdev->revision != 0x01) {
printk(KERN_ALERT PCIE_WB ": revision ID wrong!\n");
goto fail_out;
}
......@@ -342,29 +377,80 @@ static int probe(struct pci_dev *pdev, const struct pci_device_id *id)
dev->low_addr = 0;
dev->width = 4;
dev->shift = 0;
pci_set_drvdata(pdev, dev);
/* check which device is being installed: PMC or PCIe and setup bars accordingly */
if (pdev->device == PMC_WB_DEVICE_ID) {
printk(KERN_INFO PCIE_WB ": Requesting BARs for PMC Device : %04x:%04x\n", pdev->vendor, pdev->device);
/* BAR1 - etherbone configuration space */
if (setup_bar(pdev, &dev->pci_res[0], 1) < 0) goto fail_free;
/* BAR2 - wishbone */
if (setup_bar(pdev, &dev->pci_res[1], 2) < 0) goto fail_bar0;
/* BAR0 - PCI/WB bridge configuration space */
if (setup_bar(pdev, &dev->pci_res[2], 0) < 0) goto fail_bar1;
}
else{
printk(KERN_INFO PCIE_WB ": Requesting BARs for PCIe Device : %04x:%04x\n", pdev->vendor, pdev->device);
/* BAR0 - etherbone configuration space */
if (setup_bar(pdev, &dev->pci_res[0], 0) < 0) goto fail_free;
/* BAR1 - wishbone */
if (setup_bar(pdev, &dev->pci_res[1], 1) < 0) goto fail_bar0;
}
/* Initialize device registers */
control = dev->pci_res[0].addr;
iowrite32(0, control + WINDOW_OFFSET_LOW);
iowrite32(0, control + CONTROL_REGISTER_HIGH);
/* Configure interrupts*/
/* configure if device is PCIe and wants MSI */
if(pdev->device != PMC_WB_DEVICE_ID){
if(pdev->pcie_cap && dev->msi){
pci_set_master(pdev); /* enable bus mastering => needed for MSI */
/* enable message signaled interrupts */
if (pci_enable_msi(pdev) != 0) {
/* resort to legacy interrupts */
printk(KERN_ALERT PCIE_WB ": could not enable MSI interrupting (using legacy)\n");
/* resort to legacy interrupts if MSI enable failed*/
printk(KERN_ALERT PCIE_WB ": Could not enable MSI interrupting (using legacy)\n");
dev->msi = 0;
pci_clear_master(pdev);
pci_intx(pdev, 1); /* enable legacy INTx interrupts for PCIe device*/
printk(KERN_INFO PCIE_WB ": Enabled legacy interrupts for PCIe Device : %04x:%04x\n", pdev->vendor, pdev->device);
}
if (dev->msi) {
else{
/* disable legacy interrupts when using MSI */
printk(KERN_INFO PCIE_WB ": Enabled MSI, disabling INTx interrupts for PCIe Device : %04x:%04x\n", pdev->vendor, pdev->device);
pci_intx(pdev, 0);
}
}
}
else{
/* configure interrupts for pmc device */
wb_conf = dev->pci_res[2].addr;
/* Enable INTx interrupts on PMC device */
if(pmcintx){
iowrite32(1, wb_conf + WB_CONF_ICR_REG); /* enable wishbone interrupts to the PCI core */
iowrite32(0x10, control + PMC_IRQ_CONTROL); /* enable INTx interrupts to wishbone */
dev->msi = 0;
pci_intx(pdev, 1); /* enable INTx interrupts on PMC device */
printk(KERN_INFO PCIE_WB ": Enabled INTx interrupts for PMC Device : %04x:%04x\n", pdev->vendor, pdev->device);
}
else{ /* enable MSI */
pci_set_master(pdev); /* enable bus mastering => needed for MSI */
/* enable message signaled interrupts */
if (pci_enable_msi(pdev) != 0) {
printk(KERN_ALERT PCIE_WB ": Could not enable MSI interrupting on PMC device\n");
dev->msi = 0;
pci_clear_master(pdev);
}
else{
iowrite32(0x1, control + PMC_IRQ_CONTROL); /* enable MSI interrupts to wishbone */
}
}
}
if (wishbone_register(&dev->wb) < 0) {
printk(KERN_ALERT PCIE_WB ": could not register wishbone bus\n");
......@@ -376,7 +462,7 @@ static int probe(struct pci_dev *pdev, const struct pci_device_id *id)
goto fail_reg;
}
/* Enable classic interrupts */
/* Enable interrupts from wishbone */
pcie_int_enable(dev, 1);
return 0;
......@@ -385,12 +471,12 @@ fail_reg:
wishbone_unregister(&dev->wb);
fail_msi:
if (dev->msi) {
pci_intx(pdev, 1);
pci_intx(pdev,0 );
pci_disable_msi(pdev);
}
/*fail_master:*/
pci_clear_master(pdev);
/*fail_bar1:*/
fail_bar1:
destroy_bar(&dev->pci_res[1]);
fail_bar0:
destroy_bar(&dev->pci_res[0]);
......@@ -406,24 +492,42 @@ static void remove(struct pci_dev *pdev)
{
struct pcie_wb_dev *dev;
dev = pci_get_drvdata(pdev);
if(unlikely(debug)){
printk(KERN_INFO PCIE_WB ":-------------------------\n");
printk(KERN_INFO PCIE_WB ": Removing PCI Device : \n");
printk(KERN_INFO PCIE_WB ": vendor : %04x\n", pdev->vendor);
printk(KERN_INFO PCIE_WB ": device : %04x\n", pdev->device);
printk(KERN_INFO PCIE_WB ": PCIe capable : %04x\n", pdev->pcie_cap);
printk(KERN_INFO PCIE_WB ": irq number : %d\n", pdev->irq);
printk(KERN_INFO PCIE_WB ":-------------------------\n");
}
dev = pci_get_drvdata(pdev);
/* disable/remove interrupts*/
pcie_int_enable(dev, 0);
free_irq(dev->pci_dev->irq, dev);
wishbone_unregister(&dev->wb);
if (dev->msi) {
pci_intx(pdev, 1);
if(dev->msi){
pci_disable_msi(pdev);
}
pci_clear_master(pdev);
}else{
pci_intx(pdev, 0);
}
if(pdev->device == PMC_WB_DEVICE_ID){
destroy_bar(&dev->pci_res[2]);
}
destroy_bar(&dev->pci_res[1]);
destroy_bar(&dev->pci_res[0]);
kfree(dev);
printk(KERN_INFO PCIE_WB ": Removed Device %04x:%04x\n", pdev->vendor, pdev->device);
pci_disable_device(pdev);
}
static struct pci_device_id ids[] = {
{ PCI_DEVICE(PCIE_WB_VENDOR_ID, PCIE_WB_DEVICE_ID), },
{ PCI_DEVICE(PCIE_WB_VENDOR_ID, PMC_WB_DEVICE_ID ), },
{ 0, }
};
MODULE_DEVICE_TABLE(pci, ids);
......@@ -445,10 +549,17 @@ static void __exit pcie_wb_exit(void)
pci_unregister_driver(&pcie_wb_driver);
}
MODULE_AUTHOR("Stefan Rauch <s.rauch@gsi.de>");
MODULE_AUTHOR("Stefan Rauch <s.rauch@gsi.de> Dusan Slavinec <dusan.slavinec@cosylab.com>");
MODULE_DESCRIPTION("GSI Altera-Wishbone bridge driver");
module_param(debug, int, 0644);
MODULE_PARM_DESC(debug, "Enable debugging information");
module_param(debug_irq, int, 0644);
MODULE_PARM_DESC(debug_irq, "Enable debugging information in interrupt handler");
module_param(pmcintx, int, 0644);
MODULE_PARM_DESC(pmcintx, "Force INTx interrupt for PMC card");
MODULE_LICENSE("GPL");
MODULE_VERSION(PCIE_WB_VERSION);
......
......@@ -7,7 +7,8 @@
#define PCIE_WB_VERSION "0.1"
#define PCIE_WB_VENDOR_ID 0x10dc
#define PCIE_WB_DEVICE_ID 0x019a
#define PCIE_WB_DEVICE_ID 0x019a /* PCIe FTRNs (PEXARIA, EXPLODER, AMC, SCU, ...) */
#define PMC_WB_DEVICE_ID 0xc570 /* PCI FTRN (PMC) */
#define CONTROL_REGISTER_HIGH 0
#define CONTROL_REGISTER_LOW 4
......@@ -17,6 +18,7 @@
#define WINDOW_OFFSET_LOW 20
#define SDWB_ADDRESS_HIGH 24
#define SDWB_ADDRESS_LOW 28
#define PMC_IRQ_CONTROL 32
#define MASTER_CTL_HIGH 64
#define MASTER_CTL_LOW 68
......@@ -28,6 +30,15 @@
#define WINDOW_HIGH 0xFFFF0000UL
#define WINDOW_LOW 0x0000FFFCUL
/* PCI core control and status registers in BAR0 */
#define PCI_STATUS_REG 0x04
#define PCI_CONF_IRQ 0x3C
#define WB_CONF_IRQ_STATUS_MASK 0x00000001
#define WB_CONF_INT_ACK_REG 0x1E8 /* PCI core WB interrupt Acknowledge register */
#define WB_CONF_ICR_REG 0x1EC /* PCI core WB interrupt Control register */
#define WB_CONF_ISR_REG 0x1F0 /* PCI core WB interrupt Status register */
/* One per BAR */
struct pcie_wb_resource {
unsigned long start; /* start addr of BAR */
......@@ -39,7 +50,7 @@ struct pcie_wb_resource {
/* One per physical card */
struct pcie_wb_dev {
struct pci_dev* pci_dev;
struct pcie_wb_resource pci_res[2];
struct pcie_wb_resource pci_res[3];
int msi;
struct wishbone wb;
......
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