diff options
author | Zubair Lutfullah Kakakhel <Zubair.Kakakhel@imgtec.com> | 2016-11-14 07:13:47 -0500 |
---|---|---|
committer | Marc Zyngier <marc.zyngier@arm.com> | 2016-11-29 04:14:49 -0500 |
commit | 591db74bfad475c2623f6e0d47307cf726a64375 (patch) | |
tree | 0bb3fad73c8f5e80b9b443c7c9864354361eb0ac /drivers/irqchip/irq-xilinx-intc.c | |
parent | a5734de263e70c75e61778be038add78441610e3 (diff) |
irqchip/xilinx: Restructure and use jump label api
Add a global structure to house various variables.
And cleanup read/write handling by using jump label api.
Tested-by; Michal Simek <michal.simek@xilinx.com>
Signed-off-by: Zubair Lutfullah Kakakhel <Zubair.Kakakhel@imgtec.com>
Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
Diffstat (limited to 'drivers/irqchip/irq-xilinx-intc.c')
-rw-r--r-- | drivers/irqchip/irq-xilinx-intc.c | 118 |
1 files changed, 66 insertions, 52 deletions
diff --git a/drivers/irqchip/irq-xilinx-intc.c b/drivers/irqchip/irq-xilinx-intc.c index 096c1ed51d5a..7331d8cb35f1 100644 --- a/drivers/irqchip/irq-xilinx-intc.c +++ b/drivers/irqchip/irq-xilinx-intc.c | |||
@@ -14,10 +14,9 @@ | |||
14 | #include <linux/irqchip.h> | 14 | #include <linux/irqchip.h> |
15 | #include <linux/of_address.h> | 15 | #include <linux/of_address.h> |
16 | #include <linux/io.h> | 16 | #include <linux/io.h> |
17 | #include <linux/jump_label.h> | ||
17 | #include <linux/bug.h> | 18 | #include <linux/bug.h> |
18 | 19 | ||
19 | static void __iomem *intc_baseaddr; | ||
20 | |||
21 | /* No one else should require these constants, so define them locally here. */ | 20 | /* No one else should require these constants, so define them locally here. */ |
22 | #define ISR 0x00 /* Interrupt Status Register */ | 21 | #define ISR 0x00 /* Interrupt Status Register */ |
23 | #define IPR 0x04 /* Interrupt Pending Register */ | 22 | #define IPR 0x04 /* Interrupt Pending Register */ |
@@ -31,27 +30,30 @@ static void __iomem *intc_baseaddr; | |||
31 | #define MER_ME (1<<0) | 30 | #define MER_ME (1<<0) |
32 | #define MER_HIE (1<<1) | 31 | #define MER_HIE (1<<1) |
33 | 32 | ||
34 | static unsigned int (*read_fn)(void __iomem *); | 33 | static DEFINE_STATIC_KEY_FALSE(xintc_is_be); |
35 | static void (*write_fn)(u32, void __iomem *); | ||
36 | 34 | ||
37 | static void intc_write32(u32 val, void __iomem *addr) | 35 | struct xintc_irq_chip { |
38 | { | 36 | void __iomem *base; |
39 | iowrite32(val, addr); | 37 | struct irq_domain *root_domain; |
40 | } | 38 | u32 intr_mask; |
39 | }; | ||
41 | 40 | ||
42 | static unsigned int intc_read32(void __iomem *addr) | 41 | static struct xintc_irq_chip *xintc_irqc; |
43 | { | ||
44 | return ioread32(addr); | ||
45 | } | ||
46 | 42 | ||
47 | static void intc_write32_be(u32 val, void __iomem *addr) | 43 | static void xintc_write(int reg, u32 data) |
48 | { | 44 | { |
49 | iowrite32be(val, addr); | 45 | if (static_branch_unlikely(&xintc_is_be)) |
46 | iowrite32be(data, xintc_irqc->base + reg); | ||
47 | else | ||
48 | iowrite32(data, xintc_irqc->base + reg); | ||
50 | } | 49 | } |
51 | 50 | ||
52 | static unsigned int intc_read32_be(void __iomem *addr) | 51 | static unsigned int xintc_read(int reg) |
53 | { | 52 | { |
54 | return ioread32be(addr); | 53 | if (static_branch_unlikely(&xintc_is_be)) |
54 | return ioread32be(xintc_irqc->base + reg); | ||
55 | else | ||
56 | return ioread32(xintc_irqc->base + reg); | ||
55 | } | 57 | } |
56 | 58 | ||
57 | static void intc_enable_or_unmask(struct irq_data *d) | 59 | static void intc_enable_or_unmask(struct irq_data *d) |
@@ -65,21 +67,21 @@ static void intc_enable_or_unmask(struct irq_data *d) | |||
65 | * acks the irq before calling the interrupt handler | 67 | * acks the irq before calling the interrupt handler |
66 | */ | 68 | */ |
67 | if (irqd_is_level_type(d)) | 69 | if (irqd_is_level_type(d)) |
68 | write_fn(mask, intc_baseaddr + IAR); | 70 | xintc_write(IAR, mask); |
69 | 71 | ||
70 | write_fn(mask, intc_baseaddr + SIE); | 72 | xintc_write(SIE, mask); |
71 | } | 73 | } |
72 | 74 | ||
73 | static void intc_disable_or_mask(struct irq_data *d) | 75 | static void intc_disable_or_mask(struct irq_data *d) |
74 | { | 76 | { |
75 | pr_debug("irq-xilinx: disable: %ld\n", d->hwirq); | 77 | pr_debug("irq-xilinx: disable: %ld\n", d->hwirq); |
76 | write_fn(1 << d->hwirq, intc_baseaddr + CIE); | 78 | xintc_write(CIE, 1 << d->hwirq); |
77 | } | 79 | } |
78 | 80 | ||
79 | static void intc_ack(struct irq_data *d) | 81 | static void intc_ack(struct irq_data *d) |
80 | { | 82 | { |
81 | pr_debug("irq-xilinx: ack: %ld\n", d->hwirq); | 83 | pr_debug("irq-xilinx: ack: %ld\n", d->hwirq); |
82 | write_fn(1 << d->hwirq, intc_baseaddr + IAR); | 84 | xintc_write(IAR, 1 << d->hwirq); |
83 | } | 85 | } |
84 | 86 | ||
85 | static void intc_mask_ack(struct irq_data *d) | 87 | static void intc_mask_ack(struct irq_data *d) |
@@ -87,8 +89,8 @@ static void intc_mask_ack(struct irq_data *d) | |||
87 | unsigned long mask = 1 << d->hwirq; | 89 | unsigned long mask = 1 << d->hwirq; |
88 | 90 | ||
89 | pr_debug("irq-xilinx: disable_and_ack: %ld\n", d->hwirq); | 91 | pr_debug("irq-xilinx: disable_and_ack: %ld\n", d->hwirq); |
90 | write_fn(mask, intc_baseaddr + CIE); | 92 | xintc_write(CIE, mask); |
91 | write_fn(mask, intc_baseaddr + IAR); | 93 | xintc_write(IAR, mask); |
92 | } | 94 | } |
93 | 95 | ||
94 | static struct irq_chip intc_dev = { | 96 | static struct irq_chip intc_dev = { |
@@ -99,15 +101,13 @@ static struct irq_chip intc_dev = { | |||
99 | .irq_mask_ack = intc_mask_ack, | 101 | .irq_mask_ack = intc_mask_ack, |
100 | }; | 102 | }; |
101 | 103 | ||
102 | static struct irq_domain *root_domain; | ||
103 | |||
104 | unsigned int get_irq(void) | 104 | unsigned int get_irq(void) |
105 | { | 105 | { |
106 | unsigned int hwirq, irq = -1; | 106 | unsigned int hwirq, irq = -1; |
107 | 107 | ||
108 | hwirq = read_fn(intc_baseaddr + IVR); | 108 | hwirq = xintc_read(IVR); |
109 | if (hwirq != -1U) | 109 | if (hwirq != -1U) |
110 | irq = irq_find_mapping(root_domain, hwirq); | 110 | irq = irq_find_mapping(xintc_irqc->root_domain, hwirq); |
111 | 111 | ||
112 | pr_debug("irq-xilinx: hwirq=%d, irq=%d\n", hwirq, irq); | 112 | pr_debug("irq-xilinx: hwirq=%d, irq=%d\n", hwirq, irq); |
113 | 113 | ||
@@ -116,9 +116,7 @@ unsigned int get_irq(void) | |||
116 | 116 | ||
117 | static int xintc_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hw) | 117 | static int xintc_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hw) |
118 | { | 118 | { |
119 | u32 intr_mask = (u32)d->host_data; | 119 | if (xintc_irqc->intr_mask & (1 << hw)) { |
120 | |||
121 | if (intr_mask & (1 << hw)) { | ||
122 | irq_set_chip_and_handler_name(irq, &intc_dev, | 120 | irq_set_chip_and_handler_name(irq, &intc_dev, |
123 | handle_edge_irq, "edge"); | 121 | handle_edge_irq, "edge"); |
124 | irq_clear_status_flags(irq, IRQ_LEVEL); | 122 | irq_clear_status_flags(irq, IRQ_LEVEL); |
@@ -138,59 +136,75 @@ static const struct irq_domain_ops xintc_irq_domain_ops = { | |||
138 | static int __init xilinx_intc_of_init(struct device_node *intc, | 136 | static int __init xilinx_intc_of_init(struct device_node *intc, |
139 | struct device_node *parent) | 137 | struct device_node *parent) |
140 | { | 138 | { |
141 | u32 nr_irq, intr_mask; | 139 | u32 nr_irq; |
142 | int ret; | 140 | int ret; |
141 | struct xintc_irq_chip *irqc; | ||
143 | 142 | ||
144 | intc_baseaddr = of_iomap(intc, 0); | 143 | if (xintc_irqc) { |
145 | BUG_ON(!intc_baseaddr); | 144 | pr_err("irq-xilinx: Multiple instances aren't supported\n"); |
145 | return -EINVAL; | ||
146 | } | ||
147 | |||
148 | irqc = kzalloc(sizeof(*irqc), GFP_KERNEL); | ||
149 | if (!irqc) | ||
150 | return -ENOMEM; | ||
151 | |||
152 | xintc_irqc = irqc; | ||
153 | |||
154 | irqc->base = of_iomap(intc, 0); | ||
155 | BUG_ON(!irqc->base); | ||
146 | 156 | ||
147 | ret = of_property_read_u32(intc, "xlnx,num-intr-inputs", &nr_irq); | 157 | ret = of_property_read_u32(intc, "xlnx,num-intr-inputs", &nr_irq); |
148 | if (ret < 0) { | 158 | if (ret < 0) { |
149 | pr_err("irq-xilinx: unable to read xlnx,num-intr-inputs\n"); | 159 | pr_err("irq-xilinx: unable to read xlnx,num-intr-inputs\n"); |
150 | return ret; | 160 | goto err_alloc; |
151 | } | 161 | } |
152 | 162 | ||
153 | ret = of_property_read_u32(intc, "xlnx,kind-of-intr", &intr_mask); | 163 | ret = of_property_read_u32(intc, "xlnx,kind-of-intr", &irqc->intr_mask); |
154 | if (ret < 0) { | 164 | if (ret < 0) { |
155 | pr_err("irq-xilinx: unable to read xlnx,kind-of-intr\n"); | 165 | pr_err("irq-xilinx: unable to read xlnx,kind-of-intr\n"); |
156 | return ret; | 166 | goto err_alloc; |
157 | } | 167 | } |
158 | 168 | ||
159 | if (intr_mask >> nr_irq) | 169 | if (irqc->intr_mask >> nr_irq) |
160 | pr_warn("irq-xilinx: mismatch in kind-of-intr param\n"); | 170 | pr_warn("irq-xilinx: mismatch in kind-of-intr param\n"); |
161 | 171 | ||
162 | pr_info("irq-xilinx: %s: num_irq=%d, edge=0x%x\n", | 172 | pr_info("irq-xilinx: %s: num_irq=%d, edge=0x%x\n", |
163 | intc->full_name, nr_irq, intr_mask); | 173 | intc->full_name, nr_irq, irqc->intr_mask); |
164 | 174 | ||
165 | write_fn = intc_write32; | ||
166 | read_fn = intc_read32; | ||
167 | 175 | ||
168 | /* | 176 | /* |
169 | * Disable all external interrupts until they are | 177 | * Disable all external interrupts until they are |
170 | * explicity requested. | 178 | * explicity requested. |
171 | */ | 179 | */ |
172 | write_fn(0, intc_baseaddr + IER); | 180 | xintc_write(IER, 0); |
173 | 181 | ||
174 | /* Acknowledge any pending interrupts just in case. */ | 182 | /* Acknowledge any pending interrupts just in case. */ |
175 | write_fn(0xffffffff, intc_baseaddr + IAR); | 183 | xintc_write(IAR, 0xffffffff); |
176 | 184 | ||
177 | /* Turn on the Master Enable. */ | 185 | /* Turn on the Master Enable. */ |
178 | write_fn(MER_HIE | MER_ME, intc_baseaddr + MER); | 186 | xintc_write(MER, MER_HIE | MER_ME); |
179 | if (!(read_fn(intc_baseaddr + MER) & (MER_HIE | MER_ME))) { | 187 | if (!(xintc_read(MER) & (MER_HIE | MER_ME))) { |
180 | write_fn = intc_write32_be; | 188 | static_branch_enable(&xintc_is_be); |
181 | read_fn = intc_read32_be; | 189 | xintc_write(MER, MER_HIE | MER_ME); |
182 | write_fn(MER_HIE | MER_ME, intc_baseaddr + MER); | ||
183 | } | 190 | } |
184 | 191 | ||
185 | /* Yeah, okay, casting the intr_mask to a void* is butt-ugly, but I'm | 192 | irqc->root_domain = irq_domain_add_linear(intc, nr_irq, |
186 | * lazy and Michal can clean it up to something nicer when he tests | 193 | &xintc_irq_domain_ops, irqc); |
187 | * and commits this patch. ~~gcl */ | 194 | if (!irqc->root_domain) { |
188 | root_domain = irq_domain_add_linear(intc, nr_irq, &xintc_irq_domain_ops, | 195 | pr_err("irq-xilinx: Unable to create IRQ domain\n"); |
189 | (void *)intr_mask); | 196 | goto err_alloc; |
197 | } | ||
190 | 198 | ||
191 | irq_set_default_host(root_domain); | 199 | irq_set_default_host(irqc->root_domain); |
192 | 200 | ||
193 | return 0; | 201 | return 0; |
202 | |||
203 | err_alloc: | ||
204 | xintc_irqc = NULL; | ||
205 | kfree(irqc); | ||
206 | return ret; | ||
207 | |||
194 | } | 208 | } |
195 | 209 | ||
196 | IRQCHIP_DECLARE(xilinx_intc, "xlnx,xps-intc-1.00.a", xilinx_intc_of_init); | 210 | IRQCHIP_DECLARE(xilinx_intc, "xlnx,xps-intc-1.00.a", xilinx_intc_of_init); |