imx6: pcie driver fixups
[librecmc/librecmc.git] / target / linux / imx6 / files-3.10 / arch / arm / mach-imx / msi.c
1 /*
2  * arch/arm/mach-mx6/msi.c
3  *
4  * PCI MSI support for the imx processor
5  *
6  * Copyright (c) 2013, Boundary Devices.
7  *
8  * This program is free software; you can redistribute it and/or modify it
9  * under the terms and conditions of the GNU General Public License,
10  * version 2, as published by the Free Software Foundation.
11  *
12  * This program is distributed in the hope it will be useful, but WITHOUT
13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
15  * more details.
16  *
17  * You should have received a copy of the GNU General Public License along with
18  * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
19  * Place - Suite 330, Boston, MA 02111-1307 USA.
20  *
21  */
22 #include <linux/module.h>
23 #include <linux/pci.h>
24 #include <linux/msi.h>
25 #include <asm/bitops.h>
26 #include <asm/mach/irq.h>
27 #include <asm/irq.h>
28 #include <linux/irqchip/chained_irq.h>
29
30 #include "hardware.h"
31 #include "msi.h"
32
33 #define IMX_NUM_MSI_IRQS 128
34 static DECLARE_BITMAP(msi_irq_in_use, IMX_NUM_MSI_IRQS);
35 static int irq_base;
36
37 static void imx_msi_handler(unsigned int irq, struct irq_desc *desc)
38 {
39         int i, j;
40         unsigned status;
41         struct irq_chip *chip = irq_get_chip(irq);
42
43         irq_base = irq_alloc_descs(-1, 0, IMX_NUM_MSI_IRQS, 0);
44         if (irq_base < 0) {
45                 printk(KERN_ERR "%s: could not allocate IRQ numbers\n", __func__);
46                 return;
47         }
48
49         chained_irq_enter(chip, desc);
50         for (i = 0; i < 8; i++) {
51                 status = imx_pcie_msi_pending(i);
52                 while (status) {
53                         j = __fls(status);
54                         generic_handle_irq(irq_base + j);
55                         status &= ~(1 << j);
56                 }
57                 irq_base += 32;
58         }
59         chained_irq_exit(chip, desc);
60 }
61
62 /*
63  * Dynamic irq allocate and deallocation
64  */
65 int create_irq(void)
66 {
67         int irq, pos;
68
69         do {
70                 pos = find_first_zero_bit(msi_irq_in_use, IMX_NUM_MSI_IRQS);
71                 if ((unsigned)pos >= IMX_NUM_MSI_IRQS)
72                         return -ENOSPC;
73                 /* test_and_set_bit operates on 32-bits at a time */
74         } while (test_and_set_bit(pos, msi_irq_in_use));
75
76         irq = irq_base + pos;
77         dynamic_irq_init(irq);
78         return irq;
79 }
80
81 void destroy_irq(unsigned int irq)
82 {
83         int pos = irq - irq_base;
84
85         dynamic_irq_cleanup(irq);
86         clear_bit(pos, msi_irq_in_use);
87 }
88
89 void arch_teardown_msi_irq(unsigned int irq)
90 {
91         destroy_irq(irq);
92 }
93
94 static void imx_msi_irq_ack(struct irq_data *d)
95 {
96         return;
97 }
98
99 static void imx_msi_irq_enable(struct irq_data *d)
100 {
101         imx_pcie_enable_irq(d->irq - irq_base, 1);
102         return unmask_msi_irq(d);
103 }
104
105 static void imx_msi_irq_disable(struct irq_data *d)
106 {
107         imx_pcie_enable_irq(d->irq - irq_base, 0);
108         return mask_msi_irq(d);
109 }
110
111 static void imx_msi_irq_mask(struct irq_data *d)
112 {
113         imx_pcie_mask_irq(d->irq - irq_base, 1);
114         return mask_msi_irq(d);
115 }
116
117 static void imx_msi_irq_unmask(struct irq_data *d)
118 {
119         imx_pcie_mask_irq(d->irq - irq_base, 0);
120         return unmask_msi_irq(d);
121 }
122
123 static struct irq_chip imx_msi_chip = {
124         .name = "PCIe-MSI",
125         .irq_ack = imx_msi_irq_ack,
126         .irq_enable = imx_msi_irq_enable,
127         .irq_disable = imx_msi_irq_disable,
128         .irq_mask = imx_msi_irq_mask,
129         .irq_unmask = imx_msi_irq_unmask,
130 };
131
132 int arch_setup_msi_irq(struct pci_dev *pdev, struct msi_desc *desc)
133 {
134         int irq = create_irq();
135         struct msi_msg msg;
136
137         if (irq < 0)
138                 return irq;
139
140         irq_set_msi_desc(irq, desc);
141
142         msg.address_hi = 0x0;
143         msg.address_lo = MSI_MATCH_ADDR;
144         msg.data = (mxc_cpu_type << 15) | ((irq - irq_base) & 0xff);
145
146         write_msi_msg(irq, &msg);
147         irq_set_chip_and_handler(irq, &imx_msi_chip, handle_simple_irq);
148         set_irq_flags(irq, IRQF_VALID);
149         pr_info("%s: %d of %d\n", __func__, irq, NR_IRQS);
150         return 0;
151 }
152
153 void imx_msi_init(void)
154 {
155         irq_set_chained_handler(MXC_INT_PCIE_0, imx_msi_handler);
156 }