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