aboutsummaryrefslogtreecommitdiff
path: root/arch/powerpc/platforms/cell/axon_msi.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/powerpc/platforms/cell/axon_msi.c')
-rw-r--r--arch/powerpc/platforms/cell/axon_msi.c210
1 files changed, 146 insertions, 64 deletions
diff --git a/arch/powerpc/platforms/cell/axon_msi.c b/arch/powerpc/platforms/cell/axon_msi.c
index d95e71dee91..85825b5401e 100644
--- a/arch/powerpc/platforms/cell/axon_msi.c
+++ b/arch/powerpc/platforms/cell/axon_msi.c
@@ -13,7 +13,10 @@
#include <linux/kernel.h>
#include <linux/pci.h>
#include <linux/msi.h>
+#include <linux/export.h>
#include <linux/of_platform.h>
+#include <linux/debugfs.h>
+#include <linux/slab.h>
#include <asm/dcr.h>
#include <asm/machdep.h>
@@ -64,55 +67,92 @@
struct axon_msic {
- struct irq_host *irq_host;
+ struct irq_domain *irq_domain;
__le32 *fifo_virt;
dma_addr_t fifo_phys;
dcr_host_t dcr_host;
u32 read_offset;
+#ifdef DEBUG
+ u32 __iomem *trigger;
+#endif
};
+#ifdef DEBUG
+void axon_msi_debug_setup(struct device_node *dn, struct axon_msic *msic);
+#else
+static inline void axon_msi_debug_setup(struct device_node *dn,
+ struct axon_msic *msic) { }
+#endif
+
+
static void msic_dcr_write(struct axon_msic *msic, unsigned int dcr_n, u32 val)
{
- pr_debug("axon_msi: dcr_write(0x%x, 0x%x)\n", val, dcr_n);
+ pr_devel("axon_msi: dcr_write(0x%x, 0x%x)\n", val, dcr_n);
dcr_write(msic->dcr_host, dcr_n, val);
}
static void axon_msi_cascade(unsigned int irq, struct irq_desc *desc)
{
- struct axon_msic *msic = get_irq_data(irq);
+ struct irq_chip *chip = irq_desc_get_chip(desc);
+ struct axon_msic *msic = irq_get_handler_data(irq);
u32 write_offset, msi;
int idx;
+ int retry = 0;
write_offset = dcr_read(msic->dcr_host, MSIC_WRITE_OFFSET_REG);
- pr_debug("axon_msi: original write_offset 0x%x\n", write_offset);
+ pr_devel("axon_msi: original write_offset 0x%x\n", write_offset);
/* write_offset doesn't wrap properly, so we have to mask it */
write_offset &= MSIC_FIFO_SIZE_MASK;
- while (msic->read_offset != write_offset) {
+ while (msic->read_offset != write_offset && retry < 100) {
idx = msic->read_offset / sizeof(__le32);
msi = le32_to_cpu(msic->fifo_virt[idx]);
msi &= 0xFFFF;
- pr_debug("axon_msi: woff %x roff %x msi %x\n",
+ pr_devel("axon_msi: woff %x roff %x msi %x\n",
write_offset, msic->read_offset, msi);
+ if (msi < nr_irqs && irq_get_chip_data(msi) == msic) {
+ generic_handle_irq(msi);
+ msic->fifo_virt[idx] = cpu_to_le32(0xffffffff);
+ } else {
+ /*
+ * Reading the MSIC_WRITE_OFFSET_REG does not
+ * reliably flush the outstanding DMA to the
+ * FIFO buffer. Here we were reading stale
+ * data, so we need to retry.
+ */
+ udelay(1);
+ retry++;
+ pr_devel("axon_msi: invalid irq 0x%x!\n", msi);
+ continue;
+ }
+
+ if (retry) {
+ pr_devel("axon_msi: late irq 0x%x, retry %d\n",
+ msi, retry);
+ retry = 0;
+ }
+
msic->read_offset += MSIC_FIFO_ENTRY_SIZE;
msic->read_offset &= MSIC_FIFO_SIZE_MASK;
+ }
- if (msi < NR_IRQS && irq_map[msi].host == msic->irq_host)
- generic_handle_irq(msi);
- else
- pr_debug("axon_msi: invalid irq 0x%x!\n", msi);
+ if (retry) {
+ printk(KERN_WARNING "axon_msi: irq timed out\n");
+
+ msic->read_offset += MSIC_FIFO_ENTRY_SIZE;
+ msic->read_offset &= MSIC_FIFO_SIZE_MASK;
}
- desc->chip->eoi(irq);
+ chip->irq_eoi(&desc->irq_data);
}
static struct axon_msic *find_msi_translator(struct pci_dev *dev)
{
- struct irq_host *irq_host;
+ struct irq_domain *irq_domain;
struct device_node *dn, *tmp;
const phandle *ph;
struct axon_msic *msic = NULL;
@@ -123,7 +163,7 @@ static struct axon_msic *find_msi_translator(struct pci_dev *dev)
return NULL;
}
- for (; dn; tmp = of_get_parent(dn), of_node_put(dn), dn = tmp) {
+ for (; dn; dn = of_get_next_parent(dn)) {
ph = of_get_property(dn, "msi-translator", NULL);
if (ph)
break;
@@ -144,14 +184,14 @@ static struct axon_msic *find_msi_translator(struct pci_dev *dev)
goto out_error;
}
- irq_host = irq_find_host(dn);
- if (!irq_host) {
- dev_dbg(&dev->dev, "axon_msi: no irq_host found for node %s\n",
+ irq_domain = irq_find_host(dn);
+ if (!irq_domain) {
+ dev_dbg(&dev->dev, "axon_msi: no irq_domain found for node %s\n",
dn->full_name);
goto out_error;
}
- msic = irq_host->host_data;
+ msic = irq_domain->host_data;
out_error:
of_node_put(dn);
@@ -169,7 +209,7 @@ static int axon_msi_check_device(struct pci_dev *dev, int nvec, int type)
static int setup_msi_msg_address(struct pci_dev *dev, struct msi_msg *msg)
{
- struct device_node *dn, *tmp;
+ struct device_node *dn;
struct msi_desc *entry;
int len;
const u32 *prop;
@@ -182,7 +222,7 @@ static int setup_msi_msg_address(struct pci_dev *dev, struct msi_msg *msg)
entry = list_first_entry(&dev->msi_list, struct msi_desc, list);
- for (; dn; tmp = of_get_parent(dn), of_node_put(dn), dn = tmp) {
+ for (; dn; dn = of_get_next_parent(dn)) {
if (entry->msi_attrib.is_64) {
prop = of_get_property(dn, "msi-address-64", &len);
if (prop)
@@ -236,11 +276,8 @@ static int axon_msi_setup_msi_irqs(struct pci_dev *dev, int nvec, int type)
if (rc)
return rc;
- /* We rely on being able to stash a virq in a u16 */
- BUILD_BUG_ON(NR_IRQS > 65536);
-
list_for_each_entry(entry, &dev->msi_list, list) {
- virq = irq_create_direct_mapping(msic->irq_host);
+ virq = irq_create_direct_mapping(msic->irq_domain);
if (virq == NO_IRQ) {
dev_warn(&dev->dev,
"axon_msi: virq allocation failed!\n");
@@ -248,7 +285,7 @@ static int axon_msi_setup_msi_irqs(struct pci_dev *dev, int nvec, int type)
}
dev_dbg(&dev->dev, "axon_msi: allocated virq 0x%x\n", virq);
- set_irq_msi(virq, entry);
+ irq_set_msi_desc(virq, entry);
msg.data = virq;
write_msi_msg(virq, &msg);
}
@@ -266,53 +303,51 @@ static void axon_msi_teardown_msi_irqs(struct pci_dev *dev)
if (entry->irq == NO_IRQ)
continue;
- set_irq_msi(entry->irq, NULL);
+ irq_set_msi_desc(entry->irq, NULL);
irq_dispose_mapping(entry->irq);
}
}
static struct irq_chip msic_irq_chip = {
- .mask = mask_msi_irq,
- .unmask = unmask_msi_irq,
- .shutdown = unmask_msi_irq,
- .typename = "AXON-MSI",
+ .irq_mask = mask_msi_irq,
+ .irq_unmask = unmask_msi_irq,
+ .irq_shutdown = mask_msi_irq,
+ .name = "AXON-MSI",
};
-static int msic_host_map(struct irq_host *h, unsigned int virq,
+static int msic_host_map(struct irq_domain *h, unsigned int virq,
irq_hw_number_t hw)
{
- set_irq_chip_and_handler(virq, &msic_irq_chip, handle_simple_irq);
+ irq_set_chip_data(virq, h->host_data);
+ irq_set_chip_and_handler(virq, &msic_irq_chip, handle_simple_irq);
return 0;
}
-static struct irq_host_ops msic_host_ops = {
+static const struct irq_domain_ops msic_host_ops = {
.map = msic_host_map,
};
-static int axon_msi_shutdown(struct of_device *device)
+static void axon_msi_shutdown(struct platform_device *device)
{
- struct axon_msic *msic = device->dev.platform_data;
+ struct axon_msic *msic = dev_get_drvdata(&device->dev);
u32 tmp;
- pr_debug("axon_msi: disabling %s\n",
- msic->irq_host->of_node->full_name);
+ pr_devel("axon_msi: disabling %s\n",
+ msic->irq_domain->of_node->full_name);
tmp = dcr_read(msic->dcr_host, MSIC_CTRL_REG);
tmp &= ~MSIC_CTRL_ENABLE & ~MSIC_CTRL_IRQ_ENABLE;
msic_dcr_write(msic, MSIC_CTRL_REG, tmp);
-
- return 0;
}
-static int axon_msi_probe(struct of_device *device,
- const struct of_device_id *device_id)
+static int axon_msi_probe(struct platform_device *device)
{
- struct device_node *dn = device->node;
+ struct device_node *dn = device->dev.of_node;
struct axon_msic *msic;
unsigned int virq;
int dcr_base, dcr_len;
- pr_debug("axon_msi: setting up dn %s\n", dn->full_name);
+ pr_devel("axon_msi: setting up dn %s\n", dn->full_name);
msic = kzalloc(sizeof(struct axon_msic), GFP_KERNEL);
if (!msic) {
@@ -328,7 +363,7 @@ static int axon_msi_probe(struct of_device *device,
printk(KERN_ERR
"axon_msi: couldn't parse dcr properties on %s\n",
dn->full_name);
- goto out;
+ goto out_free_msic;
}
msic->dcr_host = dcr_map(dn, dcr_base, dcr_len);
@@ -346,26 +381,25 @@ static int axon_msi_probe(struct of_device *device,
goto out_free_msic;
}
- msic->irq_host = irq_alloc_host(of_node_get(dn), IRQ_HOST_MAP_NOMAP,
- NR_IRQS, &msic_host_ops, 0);
- if (!msic->irq_host) {
- printk(KERN_ERR "axon_msi: couldn't allocate irq_host for %s\n",
+ virq = irq_of_parse_and_map(dn, 0);
+ if (virq == NO_IRQ) {
+ printk(KERN_ERR "axon_msi: irq parse and map failed for %s\n",
dn->full_name);
goto out_free_fifo;
}
+ memset(msic->fifo_virt, 0xff, MSIC_FIFO_SIZE_BYTES);
- msic->irq_host->host_data = msic;
-
- virq = irq_of_parse_and_map(dn, 0);
- if (virq == NO_IRQ) {
- printk(KERN_ERR "axon_msi: irq parse and map failed for %s\n",
+ /* We rely on being able to stash a virq in a u16, so limit irqs to < 65536 */
+ msic->irq_domain = irq_domain_add_nomap(dn, 65536, &msic_host_ops, msic);
+ if (!msic->irq_domain) {
+ printk(KERN_ERR "axon_msi: couldn't allocate irq_domain for %s\n",
dn->full_name);
- goto out_free_host;
+ goto out_free_fifo;
}
- set_irq_data(virq, msic);
- set_irq_chained_handler(virq, axon_msi_cascade);
- pr_debug("axon_msi: irq 0x%x setup for axon_msi\n", virq);
+ irq_set_handler_data(virq, msic);
+ irq_set_chained_handler(virq, axon_msi_cascade);
+ pr_devel("axon_msi: irq 0x%x setup for axon_msi\n", virq);
/* Enable the MSIC hardware */
msic_dcr_write(msic, MSIC_BASE_ADDR_HI_REG, msic->fifo_phys >> 32);
@@ -375,18 +409,21 @@ static int axon_msi_probe(struct of_device *device,
MSIC_CTRL_IRQ_ENABLE | MSIC_CTRL_ENABLE |
MSIC_CTRL_FIFO_SIZE);
- device->dev.platform_data = msic;
+ msic->read_offset = dcr_read(msic->dcr_host, MSIC_WRITE_OFFSET_REG)
+ & MSIC_FIFO_SIZE_MASK;
+
+ dev_set_drvdata(&device->dev, msic);
ppc_md.setup_msi_irqs = axon_msi_setup_msi_irqs;
ppc_md.teardown_msi_irqs = axon_msi_teardown_msi_irqs;
ppc_md.msi_check_device = axon_msi_check_device;
+ axon_msi_debug_setup(dn, msic);
+
printk(KERN_DEBUG "axon_msi: setup MSIC on %s\n", dn->full_name);
return 0;
-out_free_host:
- kfree(msic->irq_host);
out_free_fifo:
dma_free_coherent(&device->dev, MSIC_FIFO_SIZE_BYTES, msic->fifo_virt,
msic->fifo_phys);
@@ -404,17 +441,62 @@ static const struct of_device_id axon_msi_device_id[] = {
{}
};
-static struct of_platform_driver axon_msi_driver = {
- .match_table = axon_msi_device_id,
+static struct platform_driver axon_msi_driver = {
.probe = axon_msi_probe,
.shutdown = axon_msi_shutdown,
- .driver = {
- .name = "axon-msi"
+ .driver = {
+ .name = "axon-msi",
+ .owner = THIS_MODULE,
+ .of_match_table = axon_msi_device_id,
},
};
static int __init axon_msi_init(void)
{
- return of_register_platform_driver(&axon_msi_driver);
+ return platform_driver_register(&axon_msi_driver);
}
subsys_initcall(axon_msi_init);
+
+
+#ifdef DEBUG
+static int msic_set(void *data, u64 val)
+{
+ struct axon_msic *msic = data;
+ out_le32(msic->trigger, val);
+ return 0;
+}
+
+static int msic_get(void *data, u64 *val)
+{
+ *val = 0;
+ return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(fops_msic, msic_get, msic_set, "%llu\n");
+
+void axon_msi_debug_setup(struct device_node *dn, struct axon_msic *msic)
+{
+ char name[8];
+ u64 addr;
+
+ addr = of_translate_address(dn, of_get_property(dn, "reg", NULL));
+ if (addr == OF_BAD_ADDR) {
+ pr_devel("axon_msi: couldn't translate reg property\n");
+ return;
+ }
+
+ msic->trigger = ioremap(addr, 0x4);
+ if (!msic->trigger) {
+ pr_devel("axon_msi: ioremap failed\n");
+ return;
+ }
+
+ snprintf(name, sizeof(name), "msic_%d", of_node_to_nid(dn));
+
+ if (!debugfs_create_file(name, 0600, powerpc_debugfs_root,
+ msic, &fops_msic)) {
+ pr_devel("axon_msi: debugfs_create_file failed!\n");
+ return;
+ }
+}
+#endif /* DEBUG */