]>
Commit | Line | Data |
---|---|---|
2874c5fd | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
1da177e4 LT |
2 | /* |
3 | * Interrupt management for most GSC and related devices. | |
4 | * | |
5 | * (c) Copyright 1999 Alex deVries for The Puffin Group | |
6 | * (c) Copyright 1999 Grant Grundler for Hewlett-Packard | |
7 | * (c) Copyright 1999 Matthew Wilcox | |
8 | * (c) Copyright 2000 Helge Deller | |
9 | * (c) Copyright 2001 Matthew Wilcox for Hewlett-Packard | |
1da177e4 LT |
10 | */ |
11 | ||
12 | #include <linux/bitops.h> | |
1da177e4 LT |
13 | #include <linux/errno.h> |
14 | #include <linux/init.h> | |
15 | #include <linux/interrupt.h> | |
16 | #include <linux/ioport.h> | |
17 | #include <linux/module.h> | |
1da177e4 LT |
18 | #include <linux/types.h> |
19 | ||
20 | #include <asm/hardware.h> | |
21 | #include <asm/io.h> | |
22 | ||
23 | #include "gsc.h" | |
24 | ||
25 | #undef DEBUG | |
26 | ||
27 | #ifdef DEBUG | |
28 | #define DEBPRINTK printk | |
29 | #else | |
30 | #define DEBPRINTK(x,...) | |
31 | #endif | |
32 | ||
33 | int gsc_alloc_irq(struct gsc_irq *i) | |
34 | { | |
35 | int irq = txn_alloc_irq(GSC_EIM_WIDTH); | |
36 | if (irq < 0) { | |
37 | printk("cannot get irq\n"); | |
38 | return irq; | |
39 | } | |
40 | ||
41 | i->txn_addr = txn_alloc_addr(irq); | |
42 | i->txn_data = txn_alloc_data(irq); | |
43 | i->irq = irq; | |
44 | ||
45 | return irq; | |
46 | } | |
47 | ||
48 | int gsc_claim_irq(struct gsc_irq *i, int irq) | |
49 | { | |
50 | int c = irq; | |
51 | ||
52 | irq += CPU_IRQ_BASE; /* virtualize the IRQ first */ | |
53 | ||
54 | irq = txn_claim_irq(irq); | |
55 | if (irq < 0) { | |
56 | printk("cannot claim irq %d\n", c); | |
57 | return irq; | |
58 | } | |
59 | ||
60 | i->txn_addr = txn_alloc_addr(irq); | |
61 | i->txn_data = txn_alloc_data(irq); | |
62 | i->irq = irq; | |
63 | ||
64 | return irq; | |
65 | } | |
66 | ||
67 | EXPORT_SYMBOL(gsc_alloc_irq); | |
68 | EXPORT_SYMBOL(gsc_claim_irq); | |
69 | ||
70 | /* Common interrupt demultiplexer used by Asp, Lasi & Wax. */ | |
7d12e780 | 71 | irqreturn_t gsc_asic_intr(int gsc_asic_irq, void *dev) |
1da177e4 LT |
72 | { |
73 | unsigned long irr; | |
74 | struct gsc_asic *gsc_asic = dev; | |
75 | ||
76 | irr = gsc_readl(gsc_asic->hpa + OFFSET_IRR); | |
77 | if (irr == 0) | |
78 | return IRQ_NONE; | |
79 | ||
80 | DEBPRINTK("%s intr, mask=0x%x\n", gsc_asic->name, irr); | |
81 | ||
82 | do { | |
83 | int local_irq = __ffs(irr); | |
84 | unsigned int irq = gsc_asic->global_irq[local_irq]; | |
ba20085c | 85 | generic_handle_irq(irq); |
1da177e4 LT |
86 | irr &= ~(1 << local_irq); |
87 | } while (irr); | |
88 | ||
89 | return IRQ_HANDLED; | |
90 | } | |
91 | ||
92 | int gsc_find_local_irq(unsigned int irq, int *global_irqs, int limit) | |
93 | { | |
94 | int local_irq; | |
95 | ||
96 | for (local_irq = 0; local_irq < limit; local_irq++) { | |
97 | if (global_irqs[local_irq] == irq) | |
98 | return local_irq; | |
99 | } | |
100 | ||
101 | return NO_IRQ; | |
102 | } | |
103 | ||
4c4231ea | 104 | static void gsc_asic_mask_irq(struct irq_data *d) |
1da177e4 | 105 | { |
4c4231ea TG |
106 | struct gsc_asic *irq_dev = irq_data_get_irq_chip_data(d); |
107 | int local_irq = gsc_find_local_irq(d->irq, irq_dev->global_irq, 32); | |
1da177e4 LT |
108 | u32 imr; |
109 | ||
4c4231ea | 110 | DEBPRINTK(KERN_DEBUG "%s(%d) %s: IMR 0x%x\n", __func__, d->irq, |
1da177e4 LT |
111 | irq_dev->name, imr); |
112 | ||
113 | /* Disable the IRQ line by clearing the bit in the IMR */ | |
114 | imr = gsc_readl(irq_dev->hpa + OFFSET_IMR); | |
115 | imr &= ~(1 << local_irq); | |
116 | gsc_writel(imr, irq_dev->hpa + OFFSET_IMR); | |
117 | } | |
118 | ||
4c4231ea | 119 | static void gsc_asic_unmask_irq(struct irq_data *d) |
1da177e4 | 120 | { |
4c4231ea TG |
121 | struct gsc_asic *irq_dev = irq_data_get_irq_chip_data(d); |
122 | int local_irq = gsc_find_local_irq(d->irq, irq_dev->global_irq, 32); | |
1da177e4 LT |
123 | u32 imr; |
124 | ||
4c4231ea | 125 | DEBPRINTK(KERN_DEBUG "%s(%d) %s: IMR 0x%x\n", __func__, d->irq, |
1da177e4 LT |
126 | irq_dev->name, imr); |
127 | ||
128 | /* Enable the IRQ line by setting the bit in the IMR */ | |
129 | imr = gsc_readl(irq_dev->hpa + OFFSET_IMR); | |
130 | imr |= 1 << local_irq; | |
131 | gsc_writel(imr, irq_dev->hpa + OFFSET_IMR); | |
132 | /* | |
133 | * FIXME: read IPR to make sure the IRQ isn't already pending. | |
134 | * If so, we need to read IRR and manually call do_irq(). | |
135 | */ | |
136 | } | |
137 | ||
dfe07565 | 138 | static struct irq_chip gsc_asic_interrupt_type = { |
4c4231ea TG |
139 | .name = "GSC-ASIC", |
140 | .irq_unmask = gsc_asic_unmask_irq, | |
141 | .irq_mask = gsc_asic_mask_irq, | |
1da177e4 LT |
142 | }; |
143 | ||
dfe07565 | 144 | int gsc_assign_irq(struct irq_chip *type, void *data) |
1da177e4 LT |
145 | { |
146 | static int irq = GSC_IRQ_BASE; | |
147 | ||
148 | if (irq > GSC_IRQ_MAX) | |
149 | return NO_IRQ; | |
150 | ||
e2f571d2 TG |
151 | irq_set_chip_and_handler(irq, type, handle_simple_irq); |
152 | irq_set_chip_data(irq, data); | |
ba20085c | 153 | |
1da177e4 LT |
154 | return irq++; |
155 | } | |
156 | ||
157 | void gsc_asic_assign_irq(struct gsc_asic *asic, int local_irq, int *irqp) | |
158 | { | |
159 | int irq = asic->global_irq[local_irq]; | |
160 | ||
161 | if (irq <= 0) { | |
162 | irq = gsc_assign_irq(&gsc_asic_interrupt_type, asic); | |
163 | if (irq == NO_IRQ) | |
164 | return; | |
165 | ||
166 | asic->global_irq[local_irq] = irq; | |
167 | } | |
168 | *irqp = irq; | |
169 | } | |
170 | ||
bfe4f4f8 JB |
171 | struct gsc_fixup_struct { |
172 | void (*choose_irq)(struct parisc_device *, void *); | |
173 | void *ctrl; | |
174 | }; | |
175 | ||
176 | static int gsc_fixup_irqs_callback(struct device *dev, void *data) | |
56583747 | 177 | { |
bfe4f4f8 JB |
178 | struct parisc_device *padev = to_parisc_device(dev); |
179 | struct gsc_fixup_struct *gf = data; | |
180 | ||
181 | /* work-around for 715/64 and others which have parent | |
182 | at path [5] and children at path [5/0/x] */ | |
183 | if (padev->id.hw_type == HPHW_FAULTY) | |
184 | gsc_fixup_irqs(padev, gf->ctrl, gf->choose_irq); | |
185 | gf->choose_irq(padev, gf->ctrl); | |
186 | ||
187 | return 0; | |
56583747 MW |
188 | } |
189 | ||
1da177e4 LT |
190 | void gsc_fixup_irqs(struct parisc_device *parent, void *ctrl, |
191 | void (*choose_irq)(struct parisc_device *, void *)) | |
192 | { | |
bfe4f4f8 JB |
193 | struct gsc_fixup_struct data = { |
194 | .choose_irq = choose_irq, | |
195 | .ctrl = ctrl, | |
196 | }; | |
197 | ||
198 | device_for_each_child(&parent->dev, &data, gsc_fixup_irqs_callback); | |
1da177e4 LT |
199 | } |
200 | ||
201 | int gsc_common_setup(struct parisc_device *parent, struct gsc_asic *gsc_asic) | |
202 | { | |
203 | struct resource *res; | |
204 | int i; | |
205 | ||
206 | gsc_asic->gsc = parent; | |
207 | ||
208 | /* Initialise local irq -> global irq mapping */ | |
209 | for (i = 0; i < 32; i++) { | |
210 | gsc_asic->global_irq[i] = NO_IRQ; | |
211 | } | |
212 | ||
213 | /* allocate resource region */ | |
214 | res = request_mem_region(gsc_asic->hpa, 0x100000, gsc_asic->name); | |
215 | if (res) { | |
216 | res->flags = IORESOURCE_MEM; /* do not mark it busy ! */ | |
217 | } | |
218 | ||
219 | #if 0 | |
220 | printk(KERN_WARNING "%s IRQ %d EIM 0x%x", gsc_asic->name, | |
221 | parent->irq, gsc_asic->eim); | |
222 | if (gsc_readl(gsc_asic->hpa + OFFSET_IMR)) | |
223 | printk(" IMR is non-zero! (0x%x)", | |
224 | gsc_readl(gsc_asic->hpa + OFFSET_IMR)); | |
225 | printk("\n"); | |
226 | #endif | |
227 | ||
228 | return 0; | |
229 | } | |
230 | ||
231 | extern struct parisc_driver lasi_driver; | |
232 | extern struct parisc_driver asp_driver; | |
233 | extern struct parisc_driver wax_driver; | |
234 | ||
235 | void __init gsc_init(void) | |
236 | { | |
237 | #ifdef CONFIG_GSC_LASI | |
238 | register_parisc_driver(&lasi_driver); | |
239 | register_parisc_driver(&asp_driver); | |
240 | #endif | |
241 | #ifdef CONFIG_GSC_WAX | |
242 | register_parisc_driver(&wax_driver); | |
243 | #endif | |
244 | } |