/arch/powerpc/platforms/cell/axon_msi.c
C | 507 lines | 371 code | 100 blank | 36 comment | 43 complexity | 7907f0e5a1175cd59110e2eff6710462 MD5 | raw file
Possible License(s): LGPL-2.0, AGPL-1.0, GPL-2.0
- /*
- * Copyright 2007, Michael Ellerman, IBM Corporation.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version
- * 2 of the License, or (at your option) any later version.
- */
- #include <linux/interrupt.h>
- #include <linux/irq.h>
- #include <linux/kernel.h>
- #include <linux/pci.h>
- #include <linux/msi.h>
- #include <linux/of_platform.h>
- #include <linux/debugfs.h>
- #include <linux/slab.h>
- #include <asm/dcr.h>
- #include <asm/machdep.h>
- #include <asm/prom.h>
- /*
- * MSIC registers, specified as offsets from dcr_base
- */
- #define MSIC_CTRL_REG 0x0
- /* Base Address registers specify FIFO location in BE memory */
- #define MSIC_BASE_ADDR_HI_REG 0x3
- #define MSIC_BASE_ADDR_LO_REG 0x4
- /* Hold the read/write offsets into the FIFO */
- #define MSIC_READ_OFFSET_REG 0x5
- #define MSIC_WRITE_OFFSET_REG 0x6
- /* MSIC control register flags */
- #define MSIC_CTRL_ENABLE 0x0001
- #define MSIC_CTRL_FIFO_FULL_ENABLE 0x0002
- #define MSIC_CTRL_IRQ_ENABLE 0x0008
- #define MSIC_CTRL_FULL_STOP_ENABLE 0x0010
- /*
- * The MSIC can be configured to use a FIFO of 32KB, 64KB, 128KB or 256KB.
- * Currently we're using a 64KB FIFO size.
- */
- #define MSIC_FIFO_SIZE_SHIFT 16
- #define MSIC_FIFO_SIZE_BYTES (1 << MSIC_FIFO_SIZE_SHIFT)
- /*
- * To configure the FIFO size as (1 << n) bytes, we write (n - 15) into bits
- * 8-9 of the MSIC control reg.
- */
- #define MSIC_CTRL_FIFO_SIZE (((MSIC_FIFO_SIZE_SHIFT - 15) << 8) & 0x300)
- /*
- * We need to mask the read/write offsets to make sure they stay within
- * the bounds of the FIFO. Also they should always be 16-byte aligned.
- */
- #define MSIC_FIFO_SIZE_MASK ((MSIC_FIFO_SIZE_BYTES - 1) & ~0xFu)
- /* Each entry in the FIFO is 16 bytes, the first 4 bytes hold the irq # */
- #define MSIC_FIFO_ENTRY_SIZE 0x10
- struct axon_msic {
- struct irq_host *irq_host;
- __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_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);
- u32 write_offset, msi;
- int idx;
- int retry = 0;
- write_offset = dcr_read(msic->dcr_host, MSIC_WRITE_OFFSET_REG);
- 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 && retry < 100) {
- idx = msic->read_offset / sizeof(__le32);
- msi = le32_to_cpu(msic->fifo_virt[idx]);
- msi &= 0xFFFF;
- pr_devel("axon_msi: woff %x roff %x msi %x\n",
- write_offset, msic->read_offset, msi);
- if (msi < NR_IRQS && irq_map[msi].host == msic->irq_host) {
- 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 (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);
- }
- static struct axon_msic *find_msi_translator(struct pci_dev *dev)
- {
- struct irq_host *irq_host;
- struct device_node *dn, *tmp;
- const phandle *ph;
- struct axon_msic *msic = NULL;
- dn = of_node_get(pci_device_to_OF_node(dev));
- if (!dn) {
- dev_dbg(&dev->dev, "axon_msi: no pci_dn found\n");
- return NULL;
- }
- for (; dn; dn = of_get_next_parent(dn)) {
- ph = of_get_property(dn, "msi-translator", NULL);
- if (ph)
- break;
- }
- if (!ph) {
- dev_dbg(&dev->dev,
- "axon_msi: no msi-translator property found\n");
- goto out_error;
- }
- tmp = dn;
- dn = of_find_node_by_phandle(*ph);
- of_node_put(tmp);
- if (!dn) {
- dev_dbg(&dev->dev,
- "axon_msi: msi-translator doesn't point to a node\n");
- 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",
- dn->full_name);
- goto out_error;
- }
- msic = irq_host->host_data;
- out_error:
- of_node_put(dn);
- return msic;
- }
- static int axon_msi_check_device(struct pci_dev *dev, int nvec, int type)
- {
- if (!find_msi_translator(dev))
- return -ENODEV;
- return 0;
- }
- static int setup_msi_msg_address(struct pci_dev *dev, struct msi_msg *msg)
- {
- struct device_node *dn;
- struct msi_desc *entry;
- int len;
- const u32 *prop;
- dn = of_node_get(pci_device_to_OF_node(dev));
- if (!dn) {
- dev_dbg(&dev->dev, "axon_msi: no pci_dn found\n");
- return -ENODEV;
- }
- entry = list_first_entry(&dev->msi_list, struct msi_desc, list);
- 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)
- break;
- }
- prop = of_get_property(dn, "msi-address-32", &len);
- if (prop)
- break;
- }
- if (!prop) {
- dev_dbg(&dev->dev,
- "axon_msi: no msi-address-(32|64) properties found\n");
- return -ENOENT;
- }
- switch (len) {
- case 8:
- msg->address_hi = prop[0];
- msg->address_lo = prop[1];
- break;
- case 4:
- msg->address_hi = 0;
-