1 /* $NetBSD: pic_heathrow.c,v 1.12 2021/01/26 14:49:41 thorpej Exp $ */
2
3 /*-
4 * Copyright (c) 2007 Michael Lorenz
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
17 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 #include <sys/cdefs.h>
30 __KERNEL_RCSID(0, "$NetBSD: pic_heathrow.c,v 1.12 2021/01/26 14:49:41 thorpej Exp $");
31
32 #include "opt_interrupt.h"
33
34 #include <sys/param.h>
35 #include <sys/kmem.h>
36 #include <sys/kernel.h>
37
38 #include <machine/pio.h>
39
40 #include <dev/ofw/openfirm.h>
41
42 #include <machine/autoconf.h>
43 #include <arch/powerpc/pic/picvar.h>
44
45 static void heathrow_enable_irq(struct pic_ops *, int, int);
46 static void heathrow_reenable_irq(struct pic_ops *, int, int);
47 static void heathrow_disable_irq(struct pic_ops *, int);
48 static int heathrow_get_irq(struct pic_ops *, int);
49 static void heathrow_ack_irq(struct pic_ops *, int);
50 static void heathrow_establish_irq(struct pic_ops *, int, int, int);
51
52 struct heathrow_ops {
53 struct pic_ops pic;
54 uint32_t pending_events_h;
55 uint32_t pending_events_l;
56 uint32_t enable_mask_h;
57 uint32_t enable_mask_l;
58 uint32_t level_mask_h;
59 uint32_t level_mask_l;
60 };
61
62 static struct heathrow_ops *setup_heathrow(uint32_t);
63 static inline void heathrow_read_events(struct heathrow_ops *);
64
65 #define INT_STATE_REG_H ((uint32_t)pic->pic_cookie + 0x10)
66 #define INT_ENABLE_REG_H ((uint32_t)pic->pic_cookie + 0x14)
67 #define INT_CLEAR_REG_H ((uint32_t)pic->pic_cookie + 0x18)
68 #define INT_LEVEL_REG_H ((uint32_t)pic->pic_cookie + 0x1c)
69 #define INT_STATE_REG_L ((uint32_t)pic->pic_cookie + 0x20)
70 #define INT_ENABLE_REG_L ((uint32_t)pic->pic_cookie + 0x24)
71 #define INT_CLEAR_REG_L ((uint32_t)pic->pic_cookie + 0x28)
72 #define INT_LEVEL_REG_L ((uint32_t)pic->pic_cookie + 0x2c)
73
74 static const char *compat[] = {
75 "heathrow",
76 NULL
77 };
78
init_heathrow(void)79 int init_heathrow(void)
80 {
81 uint32_t reg[5];
82 uint32_t obio_base;
83 int heathrow;
84
85 heathrow = OF_finddevice("/pci/mac-io");
86 if (heathrow == -1)
87 heathrow = OF_finddevice("mac-io");
88 if (heathrow == -1)
89 return FALSE;
90
91 if (! of_compatible(heathrow, compat))
92 return FALSE;
93
94 if (OF_getprop(heathrow, "assigned-addresses", reg, sizeof(reg)) != 20)
95 return FALSE;
96
97 obio_base = reg[2];
98 aprint_normal("found heathrow PIC at %08x\n", obio_base);
99 setup_heathrow(obio_base);
100 /* TODO: look for 2nd Heathrow */
101 return TRUE;
102 }
103
104 static struct heathrow_ops *
setup_heathrow(uint32_t addr)105 setup_heathrow(uint32_t addr)
106 {
107 struct heathrow_ops *heathrow;
108 struct pic_ops *pic;
109
110 heathrow = kmem_zalloc(sizeof(struct heathrow_ops), KM_SLEEP);
111 pic = &heathrow->pic;
112
113 pic->pic_numintrs = 64;
114 pic->pic_cookie = (void *)addr;
115 pic->pic_enable_irq = heathrow_enable_irq;
116 pic->pic_reenable_irq = heathrow_reenable_irq;
117 pic->pic_disable_irq = heathrow_disable_irq;
118 pic->pic_get_irq = heathrow_get_irq;
119 pic->pic_ack_irq = heathrow_ack_irq;
120 pic->pic_establish_irq = heathrow_establish_irq;
121 pic->pic_finish_setup = NULL;
122
123 strcpy(pic->pic_name, "heathrow");
124 pic_add(pic);
125 heathrow->pending_events_l = 0;
126 heathrow->enable_mask_l = 0;
127 heathrow->level_mask_l = 0;
128 heathrow->pending_events_h = 0;
129 heathrow->enable_mask_h = 0;
130 heathrow->level_mask_h = 0;
131 out32rb(INT_ENABLE_REG_L, 0);
132 out32rb(INT_CLEAR_REG_L, 0xffffffff);
133 out32rb(INT_ENABLE_REG_H, 0);
134 out32rb(INT_CLEAR_REG_H, 0xffffffff);
135 return heathrow;
136 }
137
138 static void
heathrow_enable_irq(struct pic_ops * pic,int irq,int type)139 heathrow_enable_irq(struct pic_ops *pic, int irq, int type)
140 {
141 struct heathrow_ops *heathrow = (struct heathrow_ops *)pic;
142 uint32_t mask = 1 << (irq & 0x1f);
143
144 if (irq & 0x20) {
145 heathrow->enable_mask_h |= mask;
146 out32rb(INT_ENABLE_REG_H, heathrow->enable_mask_h);
147 } else {
148 heathrow->enable_mask_l |= mask;
149 out32rb(INT_ENABLE_REG_L, heathrow->enable_mask_l);
150 }
151 }
152
153 static void
heathrow_reenable_irq(struct pic_ops * pic,int irq,int type)154 heathrow_reenable_irq(struct pic_ops *pic, int irq, int type)
155 {
156 struct heathrow_ops *heathrow = (struct heathrow_ops *)pic;
157 uint32_t levels;
158 uint32_t mask = 1 << (irq & 0x1f);
159
160 if (irq & 0x20) {
161 heathrow->enable_mask_h |= mask;
162 out32rb(INT_ENABLE_REG_H, heathrow->enable_mask_h);
163 levels = in32rb(INT_STATE_REG_H);
164 if (levels & mask) {
165 pic_mark_pending(pic->pic_intrbase + irq);
166 out32rb(INT_CLEAR_REG_H, mask);
167 }
168 } else {
169 heathrow->enable_mask_l |= mask;
170 out32rb(INT_ENABLE_REG_L, heathrow->enable_mask_l);
171 levels = in32rb(INT_STATE_REG_L);
172 if (levels & mask) {
173 pic_mark_pending(pic->pic_intrbase + irq);
174 out32rb(INT_CLEAR_REG_L, mask);
175 }
176 }
177 }
178
179 static void
heathrow_disable_irq(struct pic_ops * pic,int irq)180 heathrow_disable_irq(struct pic_ops *pic, int irq)
181 {
182 struct heathrow_ops *heathrow = (struct heathrow_ops *)pic;
183 uint32_t mask = 1 << (irq & 0x1f);
184
185 if (irq & 0x20) {
186 heathrow->enable_mask_h &= ~mask;
187 out32rb(INT_ENABLE_REG_H, heathrow->enable_mask_h);
188 } else {
189 heathrow->enable_mask_l &= ~mask;
190 out32rb(INT_ENABLE_REG_L, heathrow->enable_mask_l);
191 }
192 }
193
194 static inline void
heathrow_read_events(struct heathrow_ops * heathrow)195 heathrow_read_events(struct heathrow_ops *heathrow)
196 {
197 struct pic_ops *pic = &heathrow->pic;
198 uint32_t irqs, events, levels;
199
200 /* first the low 32 IRQs */
201 irqs = in32rb(INT_STATE_REG_L);
202 events = irqs & ~heathrow->level_mask_l;
203
204 levels = in32rb(INT_LEVEL_REG_L) & heathrow->enable_mask_l;
205 events |= levels & heathrow->level_mask_l;
206 out32rb(INT_CLEAR_REG_L, events | irqs);
207 heathrow->pending_events_l |= events;
208
209 /* then the upper 32 */
210 irqs = in32rb(INT_STATE_REG_H);
211 events = irqs & ~heathrow->level_mask_h;
212 levels = in32rb(INT_LEVEL_REG_L) & heathrow->enable_mask_h;
213 events |= levels & heathrow->level_mask_h;
214 out32rb(INT_CLEAR_REG_H, events);
215 heathrow->pending_events_h |= events;
216 }
217
218 static int
heathrow_get_irq(struct pic_ops * pic,int mode)219 heathrow_get_irq(struct pic_ops *pic, int mode)
220 {
221 struct heathrow_ops *heathrow = (struct heathrow_ops *)pic;
222 int bit, mask;
223
224 if ((heathrow->pending_events_h == 0) &&
225 (heathrow->pending_events_l == 0))
226 heathrow_read_events(heathrow);
227
228 if ((heathrow->pending_events_h == 0) &&
229 (heathrow->pending_events_l == 0))
230 return 255;
231
232 if (heathrow->pending_events_l != 0) {
233 bit = 31 - __builtin_clz(heathrow->pending_events_l);
234 mask = 1 << bit;
235 heathrow->pending_events_l &= ~mask;
236 return bit;
237 }
238
239 if (heathrow->pending_events_h != 0) {
240 bit = 31 - __builtin_clz(heathrow->pending_events_h);
241 mask = 1 << bit;
242 heathrow->pending_events_h &= ~mask;
243 return bit + 32;
244 }
245 /* we should never get here */
246 return 255;
247 }
248
249 static void
heathrow_ack_irq(struct pic_ops * pic,int irq)250 heathrow_ack_irq(struct pic_ops *pic, int irq)
251 {
252 }
253
254 static void
heathrow_establish_irq(struct pic_ops * pic,int irq,int type,int pri)255 heathrow_establish_irq(struct pic_ops *pic, int irq, int type, int pri)
256 {
257 struct heathrow_ops *heathrow = (struct heathrow_ops *)pic;
258 uint32_t mask;
259
260 KASSERT((irq >= 0) && (irq < 64));
261
262 mask = 1 << (irq & 0x1f);
263 if (irq & 0x20) {
264 if (type == IST_LEVEL) {
265
266 heathrow->level_mask_h |= mask;
267 } else {
268
269 heathrow->level_mask_h &= ~mask;
270 }
271 } else {
272 if (type == IST_LEVEL) {
273
274 heathrow->level_mask_l |= mask;
275 } else {
276
277 heathrow->level_mask_l &= ~mask;
278 }
279 }
280 aprint_debug("mask: %08x %08x\n", heathrow->level_mask_h,
281 heathrow->level_mask_l);
282 }
283