[ARM] iop13xx: msi support
Daniel Wolstenholme [Fri, 11 May 2007 05:33:02 +0000 (22:33 -0700)]
Enable devices to signal interrupts via PCI memory cycles.

rev6:
* fix enable/disable typo, Michael Ellerman

rev5:
* fix up ack, enable, and disable for iop13xx_msi_chip

rev4:
* move smp compile fix to separate patch
* use dynamic_irq_init in create_irq()
* hookup mask/unmask routines in iop13xx_msi_chip

rev3:
* change msi.c to use linux/smp.h instead of asm/smp.h
* call dynamic_irq_cleanup at destroy_irq time

rev2:
* destroy_irq did not take the full 128 bits of msi_irq_in_use into account
* added missing '&' for calls to test_and_set_bit and clear_bit

[ebiederm@xmission.com: review comments/suggestions]
[dan.j.williams@intel.com: cleanups/forward port to 2.6-git]
Signed-off-by: Daniel Wolstenholme <daniel.e.wolstenholme@intel.com>
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
Acked-by: Eric W. Biederman <ebiederm@xmission.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>

arch/arm/mach-iop13xx/Makefile
arch/arm/mach-iop13xx/irq.c
arch/arm/mach-iop13xx/msi.c [new file with mode: 0644]
arch/arm/mach-iop13xx/pci.c
include/asm-arm/arch-iop13xx/iop13xx.h
include/asm-arm/arch-iop13xx/irqs.h
include/asm-arm/arch-iop13xx/msi.h [new file with mode: 0644]

index da1609d..cad015f 100644 (file)
@@ -10,3 +10,4 @@ obj-$(CONFIG_ARCH_IOP13XX) += io.o
 obj-$(CONFIG_ARCH_IOP13XX) += tpmi.o
 obj-$(CONFIG_MACH_IQ81340SC) += iq81340sc.o
 obj-$(CONFIG_MACH_IQ81340MC) += iq81340mc.o
+obj-$(CONFIG_PCI_MSI) += msi.o
index b2eb0b9..5791add 100644 (file)
@@ -26,6 +26,7 @@
 #include <asm/hardware.h>
 #include <asm/mach-types.h>
 #include <asm/arch/irqs.h>
+#include <asm/arch/msi.h>
 
 /* INTCTL0 CP6 R0 Page 4
  */
@@ -258,7 +259,7 @@ void __init iop13xx_init_irq(void)
        write_intbase(INTBASE);
        write_intsize(INTSIZE_4);
 
-       for(i = 0; i < NR_IOP13XX_IRQS; i++) {
+       for(i = 0; i <= IRQ_IOP13XX_HPI; i++) {
                if (i < 32)
                        set_irq_chip(i, &iop13xx_irqchip1);
                else if (i < 64)
@@ -271,4 +272,6 @@ void __init iop13xx_init_irq(void)
                set_irq_handler(i, handle_level_irq);
                set_irq_flags(i, IRQF_VALID | IRQF_PROBE);
        }
+
+       iop13xx_msi_init();
 }
diff --git a/arch/arm/mach-iop13xx/msi.c b/arch/arm/mach-iop13xx/msi.c
new file mode 100644 (file)
index 0000000..2d23693
--- /dev/null
@@ -0,0 +1,194 @@
+/*
+ * arch/arm/mach-iop13xx/msi.c
+ *
+ * PCI MSI support for the iop13xx processor
+ *
+ * Copyright (c) 2006, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place - Suite 330, Boston, MA 02111-1307 USA.
+ *
+ */
+#include <linux/pci.h>
+#include <linux/msi.h>
+#include <asm/mach/irq.h>
+#include <asm/irq.h>
+
+
+#define IOP13XX_NUM_MSI_IRQS 128
+static DECLARE_BITMAP(msi_irq_in_use, IOP13XX_NUM_MSI_IRQS);
+
+/* IMIPR0 CP6 R8 Page 1
+ */
+static inline u32 read_imipr_0(void)
+{
+       u32 val;
+       asm volatile("mrc p6, 0, %0, c8, c1, 0":"=r" (val));
+       return val;
+}
+static inline void write_imipr_0(u32 val)
+{
+       asm volatile("mcr p6, 0, %0, c8, c1, 0"::"r" (val));
+}
+
+/* IMIPR1 CP6 R9 Page 1
+ */
+static inline u32 read_imipr_1(void)
+{
+       u32 val;
+       asm volatile("mrc p6, 0, %0, c9, c1, 0":"=r" (val));
+       return val;
+}
+static inline void write_imipr_1(u32 val)
+{
+       asm volatile("mcr p6, 0, %0, c9, c1, 0"::"r" (val));
+}
+
+/* IMIPR2 CP6 R10 Page 1
+ */
+static inline u32 read_imipr_2(void)
+{
+       u32 val;
+       asm volatile("mrc p6, 0, %0, c10, c1, 0":"=r" (val));
+       return val;
+}
+static inline void write_imipr_2(u32 val)
+{
+       asm volatile("mcr p6, 0, %0, c10, c1, 0"::"r" (val));
+}
+
+/* IMIPR3 CP6 R11 Page 1
+ */
+static inline u32 read_imipr_3(void)
+{
+       u32 val;
+       asm volatile("mrc p6, 0, %0, c11, c1, 0":"=r" (val));
+       return val;
+}
+static inline void write_imipr_3(u32 val)
+{
+       asm volatile("mcr p6, 0, %0, c11, c1, 0"::"r" (val));
+}
+
+static u32 (*read_imipr[])(void) = {
+       read_imipr_0,
+       read_imipr_1,
+       read_imipr_2,
+       read_imipr_3,
+};
+
+static void (*write_imipr[])(u32) = {
+       write_imipr_0,
+       write_imipr_1,
+       write_imipr_2,
+       write_imipr_3,
+};
+
+static void iop13xx_msi_handler(unsigned int irq, struct irq_desc *desc)
+{
+       int i, j;
+       unsigned long status;
+
+       /* read IMIPR registers and find any active interrupts,
+        * then call ISR for each active interrupt
+        */
+       for (i = 0; i < ARRAY_SIZE(read_imipr); i++) {
+               status = (read_imipr[i])();
+               if (!status)
+                       continue;
+
+               do {
+                       j = find_first_bit(&status, 32);
+                       (write_imipr[i])(1 << j); /* write back to clear bit */
+                       desc = irq_desc + IRQ_IOP13XX_MSI_0 + j + (32*i);
+                       desc_handle_irq(IRQ_IOP13XX_MSI_0 + j + (32*i), desc);
+                       status = (read_imipr[i])();
+               } while (status);
+       }
+}
+
+void __init iop13xx_msi_init(void)
+{
+       set_irq_chained_handler(IRQ_IOP13XX_INBD_MSI, iop13xx_msi_handler);
+}
+
+/*
+ * Dynamic irq allocate and deallocation
+ */
+int create_irq(void)
+{
+       int irq, pos;
+
+again:
+       pos = find_first_zero_bit(msi_irq_in_use, IOP13XX_NUM_MSI_IRQS);
+       irq = IRQ_IOP13XX_MSI_0 + pos;
+       if (irq > NR_IRQS)
+               return -ENOSPC;
+       /* test_and_set_bit operates on 32-bits at a time */
+       if (test_and_set_bit(pos, msi_irq_in_use))
+               goto again;
+
+       dynamic_irq_init(irq);
+
+       return irq;
+}
+
+void destroy_irq(unsigned int irq)
+{
+       int pos = irq - IRQ_IOP13XX_MSI_0;
+
+       dynamic_irq_cleanup(irq);
+
+       clear_bit(pos, msi_irq_in_use);
+}
+
+void arch_teardown_msi_irq(unsigned int irq)
+{
+       destroy_irq(irq);
+}
+
+static void iop13xx_msi_nop(unsigned int irq)
+{
+       return;
+}
+
+static struct irq_chip iop13xx_msi_chip = {
+       .name = "PCI-MSI",
+       .ack = iop13xx_msi_nop,
+       .enable = unmask_msi_irq,
+       .disable = mask_msi_irq,
+       .mask = mask_msi_irq,
+       .unmask = unmask_msi_irq,
+};
+
+int arch_setup_msi_irq(struct pci_dev *pdev, struct msi_desc *desc)
+{
+       int id, irq = create_irq();
+       struct msi_msg msg;
+
+       if (irq < 0)
+               return irq;
+
+       set_irq_msi(irq, desc);
+
+       msg.address_hi = 0x0;
+       msg.address_lo = IOP13XX_MU_MIMR_PCI;
+
+       id = iop13xx_cpu_id();
+       msg.data = (id << IOP13XX_MU_MIMR_CORE_SELECT) | (irq & 0x7f);
+
+       write_msi_msg(irq, &msg);
+       set_irq_chip_and_handler(irq, &iop13xx_msi_chip, handle_simple_irq);
+
+       return irq;
+}
index d1d0d32..1c9e94c 100644 (file)
@@ -559,6 +559,14 @@ void __init iop13xx_atue_setup(void)
        int func = iop13xx_atu_function(IOP13XX_INIT_ATU_ATUE);
        u32 reg_val;
 
+#ifdef CONFIG_PCI_MSI
+       /* BAR 0 (inbound msi window) */
+       __raw_writel(IOP13XX_MU_BASE_PHYS, IOP13XX_MU_MUBAR);
+       __raw_writel(~(IOP13XX_MU_WINDOW_SIZE - 1), IOP13XX_ATUE_IALR0);
+       __raw_writel(IOP13XX_MU_BASE_PHYS, IOP13XX_ATUE_IATVR0);
+       __raw_writel(IOP13XX_MU_BASE_PCI, IOP13XX_ATUE_IABAR0);
+#endif
+
        /* BAR 1 (1:1 mapping with Physical RAM) */
        /* Set limit and enable */
        __raw_writel(~(IOP13XX_MAX_RAM_SIZE - PHYS_OFFSET - 1) & ~0x1,
@@ -720,6 +728,14 @@ void __init iop13xx_atux_setup(void)
        else
                atux_trhfa_timeout = jiffies;
 
+#ifdef CONFIG_PCI_MSI
+       /* BAR 0 (inbound msi window) */
+       __raw_writel(IOP13XX_MU_BASE_PHYS, IOP13XX_MU_MUBAR);
+       __raw_writel(~(IOP13XX_MU_WINDOW_SIZE - 1), IOP13XX_ATUX_IALR0);
+       __raw_writel(IOP13XX_MU_BASE_PHYS, IOP13XX_ATUX_IATVR0);
+       __raw_writel(IOP13XX_MU_BASE_PCI, IOP13XX_ATUX_IABAR0);
+#endif
+
        /* BAR 1 (1:1 mapping with Physical RAM) */
        /* Set limit and enable */
        __raw_writel(~(IOP13XX_MAX_RAM_SIZE - PHYS_OFFSET - 1) & ~0x1,
index 85707e9..e6736c3 100644 (file)
@@ -181,6 +181,7 @@ static inline int iop13xx_cpu_id(void)
 #define IOP13XX_ADMA1_PMMR_OFFSET      0x00000200
 #define IOP13XX_ADMA2_PMMR_OFFSET      0x00000400
 #define IOP13XX_PBI_PMMR_OFFSET        0x00001580
+#define IOP13XX_MU_PMMR_OFFSET         0x00004000
 #define IOP13XX_ESSR0_PMMR_OFFSET      0x00002188
 #define IOP13XX_ESSR0                  IOP13XX_REG_ADDR32(0x00002188)
 
@@ -412,6 +413,34 @@ static inline int iop13xx_cpu_id(void)
 #define IOP13XX_ATU_OUMBAR_FUNC_NUM_MASK       (0x7)
 /*=======================================================================*/
 
+/*============================MESSAGING UNIT=============================*/
+#define IOP13XX_MU_OFFSET(ofs) IOP13XX_REG_ADDR32(IOP13XX_MU_PMMR_OFFSET +\
+                                                       (ofs))
+
+#define IOP13XX_MU_IMR0        IOP13XX_MU_OFFSET(0x10)
+#define IOP13XX_MU_IMR1        IOP13XX_MU_OFFSET(0x14)
+#define IOP13XX_MU_OMR0        IOP13XX_MU_OFFSET(0x18)
+#define IOP13XX_MU_OMR1        IOP13XX_MU_OFFSET(0x1C)
+#define IOP13XX_MU_IDR         IOP13XX_MU_OFFSET(0x20)
+#define IOP13XX_MU_IISR        IOP13XX_MU_OFFSET(0x24)
+#define IOP13XX_MU_IIMR        IOP13XX_MU_OFFSET(0x28)
+#define IOP13XX_MU_ODR         IOP13XX_MU_OFFSET(0x2C)
+#define IOP13XX_MU_OISR        IOP13XX_MU_OFFSET(0x30)
+#define IOP13XX_MU_OIMR        IOP13XX_MU_OFFSET(0x34)
+#define IOP13XX_MU_IRCSR       IOP13XX_MU_OFFSET(0x38)
+#define IOP13XX_MU_ORCSR       IOP13XX_MU_OFFSET(0x3C)
+#define IOP13XX_MU_MIMR        IOP13XX_MU_OFFSET(0x48)
+#define IOP13XX_MU_MUCR        IOP13XX_MU_OFFSET(0x50)
+#define IOP13XX_MU_QBAR        IOP13XX_MU_OFFSET(0x54)
+#define IOP13XX_MU_MUBAR       IOP13XX_MU_OFFSET(0x84)
+
+#define IOP13XX_MU_WINDOW_SIZE (8 * 1024)
+#define IOP13XX_MU_BASE_PHYS   (0xff000000)
+#define IOP13XX_MU_BASE_PCI    (0xff000000)
+#define IOP13XX_MU_MIMR_PCI    (IOP13XX_MU_BASE_PCI + 0x48)
+#define IOP13XX_MU_MIMR_CORE_SELECT (15)
+/*=======================================================================*/
+
 /*==============================ADMA UNITS===============================*/
 #define IOP13XX_ADMA_PHYS_BASE(chan)   IOP13XX_REG_ADDR32_PHYS((chan << 9))
 #define IOP13XX_ADMA_UPPER_PA(chan)    (IOP13XX_ADMA_PHYS_BASE(chan) + 0xc0)
index 5c6fac2..054e7ac 100644 (file)
@@ -168,7 +168,7 @@ static inline u32 read_intpnd_3(void)
 #define IRQ_IOP13XX_ATUE_IMD   (110) /* 14 */
 #define IRQ_IOP13XX_MU_MSI_TB  (111) /* 15 */
 #define IRQ_IOP13XX_RSVD_112   (112) /* 16 */
-#define IRQ_IOP13XX_RSVD_113   (113) /* 17 */
+#define IRQ_IOP13XX_INBD_MSI   (113) /* 17 */
 #define IRQ_IOP13XX_RSVD_114   (114) /* 18 */
 #define IRQ_IOP13XX_RSVD_115   (115) /* 19 */
 #define IRQ_IOP13XX_RSVD_116   (116) /* 20 */
@@ -184,7 +184,13 @@ static inline u32 read_intpnd_3(void)
 #define IRQ_IOP13XX_RSVD_126   (126) /* 30 */
 #define IRQ_IOP13XX_HPI        (127) /* 31 */
 
+#ifdef CONFIG_PCI_MSI
+#define IRQ_IOP13XX_MSI_0      (IRQ_IOP13XX_HPI + 1)
+#define NR_IOP13XX_IRQS        (IRQ_IOP13XX_MSI_0 + 128)
+#else
 #define NR_IOP13XX_IRQS        (IRQ_IOP13XX_HPI + 1)
+#endif
+
 #define NR_IRQS                NR_IOP13XX_IRQS
 
 #endif /* _IOP13XX_IRQ_H_ */
diff --git a/include/asm-arm/arch-iop13xx/msi.h b/include/asm-arm/arch-iop13xx/msi.h
new file mode 100644 (file)
index 0000000..b80c5ae
--- /dev/null
@@ -0,0 +1,11 @@
+#ifndef _IOP13XX_MSI_H_
+#define _IOP13XX_MSI_H_
+#ifdef CONFIG_PCI_MSI
+void iop13xx_msi_init(void);
+#else
+static inline void iop13xx_msi_init(void)
+{
+       return;
+}
+#endif
+#endif