ftp://ftp.kernel.org/pub/linux/kernel/v2.6/linux-2.6.6.tar.bz2
[linux-2.6.git] / arch / ia64 / sn / io / sn2 / pcibr / pcibr_intr.c
1 /*
2  * This file is subject to the terms and conditions of the GNU General Public
3  * License.  See the file "COPYING" in the main directory of this archive
4  * for more details.
5  *
6  * Copyright (C) 2001-2003 Silicon Graphics, Inc. All rights reserved.
7  */
8
9 #include <linux/types.h>
10 #include <linux/module.h>
11 #include <asm/sn/sgi.h>
12 #include <asm/sn/arch.h>
13 #include <asm/sn/pci/pciio.h>
14 #include <asm/sn/pci/pcibr.h>
15 #include <asm/sn/pci/pcibr_private.h>
16 #include <asm/sn/pci/pci_defs.h>
17 #include <asm/sn/io.h>
18 #include <asm/sn/sn_private.h>
19
20 #ifdef __ia64
21 inline int
22 compare_and_swap_ptr(void **location, void *old_ptr, void *new_ptr)
23 {
24         /* FIXME - compare_and_swap_ptr NOT ATOMIC */
25         if (*location == old_ptr) {
26                 *location = new_ptr;
27                 return 1;
28         }
29         else
30                 return 0;
31 }
32 #endif
33
34 unsigned int            pcibr_intr_bits(pciio_info_t info, pciio_intr_line_t lines, int nslots);
35 pcibr_intr_t            pcibr_intr_alloc(vertex_hdl_t, device_desc_t, pciio_intr_line_t, vertex_hdl_t);
36 void                    pcibr_intr_free(pcibr_intr_t);
37 void              pcibr_setpciint(xtalk_intr_t);
38 int                     pcibr_intr_connect(pcibr_intr_t, intr_func_t, intr_arg_t);
39 void                    pcibr_intr_disconnect(pcibr_intr_t);
40
41 vertex_hdl_t            pcibr_intr_cpu_get(pcibr_intr_t);
42
43 extern pcibr_info_t      pcibr_info_get(vertex_hdl_t);
44
45 /* =====================================================================
46  *    INTERRUPT MANAGEMENT
47  */
48
49 unsigned int
50 pcibr_intr_bits(pciio_info_t info,
51                 pciio_intr_line_t lines, int nslots)
52 {
53     pciio_slot_t            slot = PCIBR_INFO_SLOT_GET_INT(info);
54     unsigned                bbits = 0;
55
56     /*
57      * Currently favored mapping from PCI
58      * slot number and INTA/B/C/D to Bridge
59      * PCI Interrupt Bit Number:
60      *
61      *     SLOT     A B C D
62      *      0       0 4 0 4
63      *      1       1 5 1 5
64      *      2       2 6 2 6
65      *      3       3 7 3 7
66      *      4       4 0 4 0
67      *      5       5 1 5 1
68      *      6       6 2 6 2
69      *      7       7 3 7 3
70      */
71
72     if (slot < nslots) {
73         if (lines & (PCIIO_INTR_LINE_A| PCIIO_INTR_LINE_C))
74             bbits |= 1 << slot;
75         if (lines & (PCIIO_INTR_LINE_B| PCIIO_INTR_LINE_D))
76             bbits |= 1 << (slot ^ 4);
77     }
78     return bbits;
79 }
80
81
82 /*
83  *      On SN systems there is a race condition between a PIO read response
84  *      and DMA's.  In rare cases, the read response may beat the DMA, causing
85  *      the driver to think that data in memory is complete and meaningful.
86  *      This code eliminates that race.
87  *      This routine is called by the PIO read routines after doing the read.
88  *      This routine then forces a fake interrupt on another line, which
89  *      is logically associated with the slot that the PIO is addressed to.
90  *      (see sn_dma_flush_init() )
91  *      It then spins while watching the memory location that the interrupt
92  *      is targetted to.  When the interrupt response arrives, we are sure
93  *      that the DMA has landed in memory and it is safe for the driver
94  *      to proceed.
95  */
96
97 extern struct sn_flush_nasid_entry flush_nasid_list[MAX_NASIDS];
98
99 void
100 sn_dma_flush(unsigned long addr)
101 {
102         nasid_t nasid;
103         int wid_num;
104         struct sn_flush_device_list *p;
105         int i,j;
106         int bwin;
107         unsigned long flags;
108
109         nasid = NASID_GET(addr);
110         wid_num = SWIN_WIDGETNUM(addr);
111         bwin = BWIN_WINDOWNUM(addr);
112
113         if (flush_nasid_list[nasid].widget_p == NULL) return;
114         if (bwin > 0) {
115                 unsigned long itte = flush_nasid_list[nasid].iio_itte[bwin];
116
117                 wid_num = (itte >> IIO_ITTE_WIDGET_SHIFT) &
118                                   IIO_ITTE_WIDGET_MASK;
119         }
120         if (flush_nasid_list[nasid].widget_p == NULL) return;
121         if (flush_nasid_list[nasid].widget_p[wid_num] == NULL) return;
122         p = &flush_nasid_list[nasid].widget_p[wid_num][0];
123
124         /* find a matching BAR */
125
126         for (i=0; i<DEV_PER_WIDGET;i++) {
127                 for (j=0; j<PCI_ROM_RESOURCE;j++) {
128                         if (p->bar_list[j].start == 0) break;
129                         if (addr >= p->bar_list[j].start && addr <= p->bar_list[j].end) break;
130                 }
131                 if (j < PCI_ROM_RESOURCE && p->bar_list[j].start != 0) break;
132                 p++;
133         }
134
135         /* if no matching BAR, return without doing anything. */
136
137         if (i == DEV_PER_WIDGET) return;
138
139         spin_lock_irqsave(&p->flush_lock, flags);
140
141         p->flush_addr = 0;
142
143         /* force an interrupt. */
144
145         *(volatile uint32_t *)(p->force_int_addr) = 1;
146
147         /* wait for the interrupt to come back. */
148
149         while (p->flush_addr != 0x10f);
150
151         /* okay, everything is synched up. */
152         spin_unlock_irqrestore(&p->flush_lock, flags);
153 }
154
155 EXPORT_SYMBOL(sn_dma_flush);
156
157 /*
158  *      There are end cases where a deadlock can occur if interrupt 
159  *      processing completes and the Bridge b_int_status bit is still set.
160  *
161  *      One scenerio is if a second PCI interrupt occurs within 60ns of
162  *      the previous interrupt being cleared. In this case the Bridge
163  *      does not detect the transition, the Bridge b_int_status bit
164  *      remains set, and because no transition was detected no interrupt
165  *      packet is sent to the Hub/Heart.
166  *
167  *      A second scenerio is possible when a b_int_status bit is being
168  *      shared by multiple devices:
169  *                                              Device #1 generates interrupt
170  *                                              Bridge b_int_status bit set
171  *                                              Device #2 generates interrupt
172  *              interrupt processing begins
173  *                ISR for device #1 runs and
174  *                      clears interrupt
175  *                                              Device #1 generates interrupt
176  *                ISR for device #2 runs and
177  *                      clears interrupt
178  *                                              (b_int_status bit still set)
179  *              interrupt processing completes
180  *                
181  *      Interrupt processing is now complete, but an interrupt is still
182  *      outstanding for Device #1. But because there was no transition of
183  *      the b_int_status bit, no interrupt packet will be generated and
184  *      a deadlock will occur.
185  *
186  *      To avoid these deadlock situations, this function is used
187  *      to check if a specific Bridge b_int_status bit is set, and if so,
188  *      cause the setting of the corresponding interrupt bit.
189  *
190  *      On a XBridge (SN1) and PIC (SN2), we do this by writing the appropriate Bridge Force 
191  *      Interrupt register.
192  */
193 void
194 pcibr_force_interrupt(pcibr_intr_t intr)
195 {
196         unsigned        bit;
197         unsigned        bits;
198         pcibr_soft_t    pcibr_soft = intr->bi_soft;
199
200         bits = intr->bi_ibits;
201         for (bit = 0; bit < 8; bit++) {
202                 if (bits & (1 << bit)) {
203
204                         PCIBR_DEBUG((PCIBR_DEBUG_INTR, pcibr_soft->bs_vhdl,
205                                 "pcibr_force_interrupt: bit=0x%x\n", bit));
206
207                         pcireg_force_intr_set(pcibr_soft, bit);
208                 }
209         }
210 }
211
212 /*ARGSUSED */
213 pcibr_intr_t
214 pcibr_intr_alloc(vertex_hdl_t pconn_vhdl,
215                  device_desc_t dev_desc,
216                  pciio_intr_line_t lines,
217                  vertex_hdl_t owner_dev)
218 {
219     pcibr_info_t            pcibr_info = pcibr_info_get(pconn_vhdl);
220     pciio_slot_t            pciio_slot = PCIBR_INFO_SLOT_GET_INT(pcibr_info);
221     pcibr_soft_t            pcibr_soft = (pcibr_soft_t) pcibr_info->f_mfast;
222     vertex_hdl_t            xconn_vhdl = pcibr_soft->bs_conn;
223     int                     is_threaded = 0;
224
225     xtalk_intr_t           *xtalk_intr_p;
226     pcibr_intr_t           *pcibr_intr_p;
227     pcibr_intr_list_t      *intr_list_p;
228
229     unsigned                pcibr_int_bits;
230     unsigned                pcibr_int_bit;
231     xtalk_intr_t            xtalk_intr = (xtalk_intr_t)0;
232     hub_intr_t              hub_intr;
233     pcibr_intr_t            pcibr_intr;
234     pcibr_intr_list_t       intr_entry;
235     pcibr_intr_list_t       intr_list;
236
237     PCIBR_DEBUG_ALWAYS((PCIBR_DEBUG_INTR_ALLOC, pconn_vhdl,
238                 "pcibr_intr_alloc: %s%s%s%s%s\n",
239                 !(lines & 15) ? " No INTs?" : "",
240                 lines & 1 ? " INTA" : "",
241                 lines & 2 ? " INTB" : "",
242                 lines & 4 ? " INTC" : "",
243                 lines & 8 ? " INTD" : ""));
244
245     pcibr_intr = kmalloc(sizeof (*(pcibr_intr)), GFP_KERNEL);
246     if (!pcibr_intr)
247         return NULL;
248     memset(pcibr_intr, 0, sizeof (*(pcibr_intr)));
249
250     pcibr_intr->bi_dev = pconn_vhdl;
251     pcibr_intr->bi_lines = lines;
252     pcibr_intr->bi_soft = pcibr_soft;
253     pcibr_intr->bi_ibits = 0;           /* bits will be added below */
254     pcibr_intr->bi_func = 0;            /* unset until connect */
255     pcibr_intr->bi_arg = 0;             /* unset until connect */
256     pcibr_intr->bi_flags = is_threaded ? 0 : PCIIO_INTR_NOTHREAD;
257     pcibr_intr->bi_mustruncpu = CPU_NONE;
258     pcibr_intr->bi_ibuf.ib_in = 0;
259     pcibr_intr->bi_ibuf.ib_out = 0;
260     spin_lock_init(&pcibr_intr->bi_ibuf.ib_lock);
261
262     pcibr_int_bits = pcibr_soft->bs_intr_bits((pciio_info_t)pcibr_info, 
263                                         lines, PCIBR_NUM_SLOTS(pcibr_soft));
264
265     /*
266      * For each PCI interrupt line requested, figure
267      * out which Bridge PCI Interrupt Line it maps
268      * to, and make sure there are xtalk resources
269      * allocated for it.
270      */
271     PCIBR_DEBUG_ALWAYS((PCIBR_DEBUG_INTR_ALLOC, pconn_vhdl,
272                 "pcibr_intr_alloc: pcibr_int_bits: 0x%x\n", pcibr_int_bits));
273     for (pcibr_int_bit = 0; pcibr_int_bit < 8; pcibr_int_bit ++) {
274         if (pcibr_int_bits & (1 << pcibr_int_bit)) {
275             xtalk_intr_p = &pcibr_soft->bs_intr[pcibr_int_bit].bsi_xtalk_intr;
276
277             xtalk_intr = *xtalk_intr_p;
278
279             if (xtalk_intr == NULL) {
280                 /*
281                  * This xtalk_intr_alloc is constrained for two reasons:
282                  * 1) Normal interrupts and error interrupts need to be delivered
283                  *    through a single xtalk target widget so that there aren't any
284                  *    ordering problems with DMA, completion interrupts, and error
285                  *    interrupts. (Use of xconn_vhdl forces this.)
286                  *
287                  * 2) On SN1, addressing constraints on SN1 and Bridge force
288                  *    us to use a single PI number for all interrupts from a
289                  *    single Bridge. (SN1-specific code forces this).
290                  */
291
292                 /*
293                  * All code dealing with threaded PCI interrupt handlers
294                  * is located at the pcibr level. Because of this,
295                  * we always want the lower layers (hub/heart_intr_alloc, 
296                  * intr_level_connect) to treat us as non-threaded so we
297                  * don't set up a duplicate threaded environment. We make
298                  * this happen by calling a special xtalk interface.
299                  */
300                 xtalk_intr = xtalk_intr_alloc_nothd(xconn_vhdl, dev_desc, 
301                         owner_dev);
302
303                 PCIBR_DEBUG_ALWAYS((PCIBR_DEBUG_INTR_ALLOC, pconn_vhdl,
304                             "pcibr_intr_alloc: xtalk_intr=0x%lx\n", xtalk_intr));
305
306                 /* both an assert and a runtime check on this:
307                  * we need to check in non-DEBUG kernels, and
308                  * the ASSERT gets us more information when
309                  * we use DEBUG kernels.
310                  */
311                 ASSERT(xtalk_intr != NULL);
312                 if (xtalk_intr == NULL) {
313                     /* it is quite possible that our
314                      * xtalk_intr_alloc failed because
315                      * someone else got there first,
316                      * and we can find their results
317                      * in xtalk_intr_p.
318                      */
319                     if (!*xtalk_intr_p) {
320                         printk(KERN_ALERT "pcibr_intr_alloc %s: "
321                                 "unable to get xtalk interrupt resources",
322                                 pcibr_soft->bs_name);
323                         /* yes, we leak resources here. */
324                         return 0;
325                     }
326                 } else if (compare_and_swap_ptr((void **) xtalk_intr_p, NULL, xtalk_intr)) {
327                     /*
328                      * now tell the bridge which slot is
329                      * using this interrupt line.
330                      */
331                     pcireg_intr_device_bit_clr(pcibr_soft, 
332                             BRIDGE_INT_DEV_MASK(pcibr_int_bit));
333                     pcireg_intr_device_bit_set(pcibr_soft, 
334                             (pciio_slot << BRIDGE_INT_DEV_SHFT(pcibr_int_bit)));
335
336                     PCIBR_DEBUG_ALWAYS((PCIBR_DEBUG_INTR_ALLOC, pconn_vhdl,
337                                 "bridge intr bit %d clears my wrb\n",
338                                 pcibr_int_bit));
339                 } else {
340                     /* someone else got one allocated first;
341                      * free the one we just created, and
342                      * retrieve the one they allocated.
343                      */
344                     xtalk_intr_free(xtalk_intr);
345                     xtalk_intr = *xtalk_intr_p;
346                 }
347             }
348
349             pcibr_intr->bi_ibits |= 1 << pcibr_int_bit;
350
351             intr_entry = kmalloc(sizeof (*(intr_entry)), GFP_KERNEL);
352             if ( !intr_entry ) {
353                 printk(KERN_ALERT "pcibr_intr_alloc %s: "
354                         "unable to get memory",
355                         pcibr_soft->bs_name);
356                 return 0;
357             }
358             memset(intr_entry, 0, sizeof (*(intr_entry)));
359
360             intr_entry->il_next = NULL;
361             intr_entry->il_intr = pcibr_intr;
362             intr_entry->il_soft = pcibr_soft;
363             intr_entry->il_slot = pciio_slot;
364             intr_list_p = 
365                 &pcibr_soft->bs_intr[pcibr_int_bit].bsi_pcibr_intr_wrap.iw_list;
366
367             PCIBR_DEBUG_ALWAYS((PCIBR_DEBUG_INTR_ALLOC, pconn_vhdl,
368                         "Bridge bit 0x%x wrap=0x%lx\n", pcibr_int_bit,
369                         &(pcibr_soft->bs_intr[pcibr_int_bit].bsi_pcibr_intr_wrap)));
370
371             if (compare_and_swap_ptr((void **) intr_list_p, NULL, intr_entry)) {
372                 /* we are the first interrupt on this bridge bit.
373                  */
374                 PCIBR_DEBUG_ALWAYS((PCIBR_DEBUG_INTR_ALLOC, pconn_vhdl,
375                             "INT 0x%x (bridge bit %d) allocated [FIRST]\n",
376                             pcibr_int_bits, pcibr_int_bit));
377                 continue;
378             }
379             intr_list = *intr_list_p;
380             pcibr_intr_p = &intr_list->il_intr;
381             if (compare_and_swap_ptr((void **) pcibr_intr_p, NULL, pcibr_intr)) {
382                 /* first entry on list was erased,
383                  * and we replaced it, so we
384                  * don't need our intr_entry.
385                  */
386                 kfree(intr_entry);
387                 PCIBR_DEBUG_ALWAYS((PCIBR_DEBUG_INTR_ALLOC, pconn_vhdl,
388                             "INT 0x%x (bridge bit %d) replaces erased first\n",
389                             pcibr_int_bits, pcibr_int_bit));
390                 continue;
391             }
392             intr_list_p = &intr_list->il_next;
393             if (compare_and_swap_ptr((void **) intr_list_p, NULL, intr_entry)) {
394                 /* we are the new second interrupt on this bit.
395                  */
396                 pcibr_soft->bs_intr[pcibr_int_bit].bsi_pcibr_intr_wrap.iw_shared = 1;
397                 PCIBR_DEBUG_ALWAYS((PCIBR_DEBUG_INTR_ALLOC, pconn_vhdl,
398                             "INT 0x%x (bridge bit %d) is new SECOND\n",
399                             pcibr_int_bits, pcibr_int_bit));
400                 continue;
401             }
402             while (1) {
403                 pcibr_intr_p = &intr_list->il_intr;
404                 if (compare_and_swap_ptr((void **) pcibr_intr_p, NULL, pcibr_intr)) {
405                     /* an entry on list was erased,
406                      * and we replaced it, so we
407                      * don't need our intr_entry.
408                      */
409                     kfree(intr_entry);
410
411                     PCIBR_DEBUG_ALWAYS((PCIBR_DEBUG_INTR_ALLOC, pconn_vhdl,
412                                 "INT 0x%x (bridge bit %d) replaces erase Nth\n",
413                                 pcibr_int_bits, pcibr_int_bit));
414                     break;
415                 }
416                 intr_list_p = &intr_list->il_next;
417                 if (compare_and_swap_ptr((void **) intr_list_p, NULL, intr_entry)) {
418                     /* entry appended to share list
419                      */
420                     PCIBR_DEBUG_ALWAYS((PCIBR_DEBUG_INTR_ALLOC, pconn_vhdl,
421                                 "INT 0x%x (bridge bit %d) is new Nth\n",
422                                 pcibr_int_bits, pcibr_int_bit));
423                     break;
424                 }
425                 /* step to next record in chain
426                  */
427                 intr_list = *intr_list_p;
428             }
429         }
430     }
431
432     hub_intr = (hub_intr_t)xtalk_intr;
433     pcibr_intr->bi_irq = hub_intr->i_bit;
434     pcibr_intr->bi_cpu = hub_intr->i_cpuid;
435     PCIBR_DEBUG_ALWAYS((PCIBR_DEBUG_INTR_ALLOC, pconn_vhdl,
436                 "pcibr_intr_alloc complete: pcibr_intr=0x%lx\n", pcibr_intr));
437     return pcibr_intr;
438 }
439
440 /*ARGSUSED */
441 void
442 pcibr_intr_free(pcibr_intr_t pcibr_intr)
443 {
444     unsigned                pcibr_int_bits = pcibr_intr->bi_ibits;
445     pcibr_soft_t            pcibr_soft = pcibr_intr->bi_soft;
446     unsigned                pcibr_int_bit;
447     pcibr_intr_list_t       intr_list;
448     int                     intr_shared;
449     xtalk_intr_t            *xtalk_intrp;
450
451     for (pcibr_int_bit = 0; pcibr_int_bit < 8; pcibr_int_bit++) {
452         if (pcibr_int_bits & (1 << pcibr_int_bit)) {
453             for (intr_list = 
454                      pcibr_soft->bs_intr[pcibr_int_bit].bsi_pcibr_intr_wrap.iw_list;
455                  intr_list != NULL;
456                  intr_list = intr_list->il_next)
457                 if (compare_and_swap_ptr((void **) &intr_list->il_intr, 
458                                          pcibr_intr, 
459                                          NULL)) {
460
461                     PCIBR_DEBUG_ALWAYS((PCIBR_DEBUG_INTR_ALLOC, 
462                                 pcibr_intr->bi_dev,
463                                 "pcibr_intr_free: cleared hdlr from bit 0x%x\n",
464                                 pcibr_int_bit));
465                 }
466             /* If this interrupt line is not being shared between multiple
467              * devices release the xtalk interrupt resources.
468              */
469             intr_shared = 
470                 pcibr_soft->bs_intr[pcibr_int_bit].bsi_pcibr_intr_wrap.iw_shared;
471             xtalk_intrp = &pcibr_soft->bs_intr[pcibr_int_bit].bsi_xtalk_intr;
472
473             if ((!intr_shared) && (*xtalk_intrp)) {
474
475                 xtalk_intr_free(*xtalk_intrp);
476                 *xtalk_intrp = 0;
477
478                 /* Clear the PCI device interrupt to bridge interrupt pin
479                  * mapping.
480                  */
481                 pcireg_intr_device_bit_clr(pcibr_soft, 
482                         BRIDGE_INT_DEV_MASK(pcibr_int_bit));
483             }
484         }
485     }
486     kfree(pcibr_intr);
487 }
488
489 void
490 pcibr_setpciint(xtalk_intr_t xtalk_intr)
491 {
492     iopaddr_t            addr;
493     xtalk_intr_vector_t  vect;
494     vertex_hdl_t         vhdl;
495     int                  bus_num;
496     int                  pcibr_int_bit;
497     void                 *bridge;
498     
499     addr = xtalk_intr_addr_get(xtalk_intr);
500     vect = xtalk_intr_vector_get(xtalk_intr);
501     vhdl = xtalk_intr_dev_get(xtalk_intr);
502
503     /* bus and int_bits are stored in sfarg, bus bit3, int_bits bit2:0 */
504     pcibr_int_bit = *((int *)xtalk_intr_sfarg_get(xtalk_intr)) & 0x7;
505     bus_num = ((*((int *)xtalk_intr_sfarg_get(xtalk_intr)) & 0x8) >> 3);
506
507     bridge = pcibr_bridge_ptr_get(vhdl, bus_num);
508     pcireg_bridge_intr_addr_vect_set(bridge, pcibr_int_bit, vect);
509     pcireg_bridge_intr_addr_addr_set(bridge, pcibr_int_bit, addr);
510 }
511
512 /*ARGSUSED */
513 int
514 pcibr_intr_connect(pcibr_intr_t pcibr_intr, intr_func_t intr_func, intr_arg_t intr_arg)
515 {
516     pcibr_soft_t            pcibr_soft = pcibr_intr->bi_soft;
517     unsigned                pcibr_int_bits = pcibr_intr->bi_ibits;
518     unsigned                pcibr_int_bit;
519     unsigned long           s;
520
521     if (pcibr_intr == NULL)
522         return -1;
523
524     PCIBR_DEBUG_ALWAYS((PCIBR_DEBUG_INTR_ALLOC, pcibr_intr->bi_dev,
525                 "pcibr_intr_connect: intr_func=0x%lx, intr_arg=0x%lx\n",
526                 intr_func, intr_arg));
527
528     pcibr_intr->bi_func = intr_func;
529     pcibr_intr->bi_arg = intr_arg;
530     *((volatile unsigned *)&pcibr_intr->bi_flags) |= PCIIO_INTR_CONNECTED;
531
532     /*
533      * For each PCI interrupt line requested, figure
534      * out which Bridge PCI Interrupt Line it maps
535      * to, and make sure there are xtalk resources
536      * allocated for it.
537      */
538     for (pcibr_int_bit = 0; pcibr_int_bit < 8; pcibr_int_bit++)
539         if (pcibr_int_bits & (1 << pcibr_int_bit)) {
540             pcibr_intr_wrap_t       intr_wrap;
541             xtalk_intr_t            xtalk_intr;
542             void                   *int_addr;
543
544             xtalk_intr = pcibr_soft->bs_intr[pcibr_int_bit].bsi_xtalk_intr;
545             intr_wrap = &pcibr_soft->bs_intr[pcibr_int_bit].bsi_pcibr_intr_wrap;
546
547             /*
548              * If this interrupt line is being shared and the connect has
549              * already been done, no need to do it again.
550              */
551             if (pcibr_soft->bs_intr[pcibr_int_bit].bsi_pcibr_intr_wrap.iw_connected)
552                 continue;
553
554
555             /*
556              * Use the pcibr wrapper function to handle all Bridge interrupts
557              * regardless of whether the interrupt line is shared or not.
558              */
559             int_addr = pcireg_intr_addr_addr(pcibr_soft, pcibr_int_bit);
560             pcibr_soft->bs_intr[pcibr_int_bit].bsi_int_bit = 
561                                ((pcibr_soft->bs_busnum << 3) | pcibr_int_bit);
562             xtalk_intr_connect(xtalk_intr,
563                                NULL,
564                                (intr_arg_t) intr_wrap,
565                                (xtalk_intr_setfunc_t) pcibr_setpciint,
566                                &pcibr_soft->bs_intr[pcibr_int_bit].bsi_int_bit);
567
568             pcibr_soft->bs_intr[pcibr_int_bit].bsi_pcibr_intr_wrap.iw_connected = 1;
569
570             PCIBR_DEBUG_ALWAYS((PCIBR_DEBUG_INTR_ALLOC, pcibr_intr->bi_dev,
571                         "pcibr_setpciint: int_addr=0x%lx, *int_addr=0x%lx, "
572                         "pcibr_int_bit=0x%x\n", int_addr, 
573                         pcireg_intr_addr_get(pcibr_soft, pcibr_int_bit),
574                         pcibr_int_bit));
575         }
576
577         s = pcibr_lock(pcibr_soft);
578         pcireg_intr_enable_bit_set(pcibr_soft, pcibr_int_bits);
579         pcireg_tflush_get(pcibr_soft);
580         pcibr_unlock(pcibr_soft, s);
581
582     return 0;
583 }
584
585 /*ARGSUSED */
586 void
587 pcibr_intr_disconnect(pcibr_intr_t pcibr_intr)
588 {
589     pcibr_soft_t            pcibr_soft = pcibr_intr->bi_soft;
590     unsigned                pcibr_int_bits = pcibr_intr->bi_ibits;
591     unsigned                pcibr_int_bit;
592     pcibr_intr_wrap_t       intr_wrap;
593     unsigned long           s;
594
595     /* Stop calling the function. Now.
596      */
597     *((volatile unsigned *)&pcibr_intr->bi_flags) &= ~PCIIO_INTR_CONNECTED;
598     pcibr_intr->bi_func = 0;
599     pcibr_intr->bi_arg = 0;
600     /*
601      * For each PCI interrupt line requested, figure
602      * out which Bridge PCI Interrupt Line it maps
603      * to, and disconnect the interrupt.
604      */
605
606     /* don't disable interrupts for lines that
607      * are shared between devices.
608      */
609     for (pcibr_int_bit = 0; pcibr_int_bit < 8; pcibr_int_bit++)
610         if ((pcibr_int_bits & (1 << pcibr_int_bit)) &&
611             (pcibr_soft->bs_intr[pcibr_int_bit].bsi_pcibr_intr_wrap.iw_shared))
612             pcibr_int_bits &= ~(1 << pcibr_int_bit);
613     if (!pcibr_int_bits)
614         return;
615
616     s = pcibr_lock(pcibr_soft);
617     pcireg_intr_enable_bit_clr(pcibr_soft, pcibr_int_bits);
618     pcireg_tflush_get(pcibr_soft);      /* wait until Bridge PIO complete */
619     pcibr_unlock(pcibr_soft, s);
620
621     PCIBR_DEBUG_ALWAYS((PCIBR_DEBUG_INTR_ALLOC, pcibr_intr->bi_dev,
622                 "pcibr_intr_disconnect: disabled int_bits=0x%x\n", 
623                 pcibr_int_bits));
624
625     for (pcibr_int_bit = 0; pcibr_int_bit < 8; pcibr_int_bit++)
626         if (pcibr_int_bits & (1 << pcibr_int_bit)) {
627
628             /* if the interrupt line is now shared,
629              * do not disconnect it.
630              */
631             if (pcibr_soft->bs_intr[pcibr_int_bit].bsi_pcibr_intr_wrap.iw_shared)
632                 continue;
633
634             xtalk_intr_disconnect(pcibr_soft->bs_intr[pcibr_int_bit].bsi_xtalk_intr);
635             pcibr_soft->bs_intr[pcibr_int_bit].bsi_pcibr_intr_wrap.iw_connected = 0;
636
637             PCIBR_DEBUG_ALWAYS((PCIBR_DEBUG_INTR_ALLOC, pcibr_intr->bi_dev,
638                         "pcibr_intr_disconnect: disconnect int_bits=0x%x\n",
639                         pcibr_int_bits));
640
641             /* if we are sharing the interrupt line,
642              * connect us up; this closes the hole
643              * where the another pcibr_intr_alloc()
644              * was in progress as we disconnected.
645              */
646             intr_wrap = &pcibr_soft->bs_intr[pcibr_int_bit].bsi_pcibr_intr_wrap;
647             if (!pcibr_soft->bs_intr[pcibr_int_bit].bsi_pcibr_intr_wrap.iw_shared)
648                 continue;
649
650             pcibr_soft->bs_intr[pcibr_int_bit].bsi_int_bit =
651                                 ((pcibr_soft->bs_busnum << 3) | pcibr_int_bit);
652             xtalk_intr_connect(pcibr_soft->bs_intr[pcibr_int_bit].bsi_xtalk_intr,
653                                NULL,
654                                (intr_arg_t) intr_wrap,
655                                (xtalk_intr_setfunc_t) pcibr_setpciint,
656                                &pcibr_soft->bs_intr[pcibr_int_bit].bsi_int_bit);
657
658             PCIBR_DEBUG_ALWAYS((PCIBR_DEBUG_INTR_ALLOC, pcibr_intr->bi_dev,
659                         "pcibr_intr_disconnect: now-sharing int_bits=0x%x\n",
660                         pcibr_int_bit));
661         }
662 }
663
664 /*ARGSUSED */
665 vertex_hdl_t
666 pcibr_intr_cpu_get(pcibr_intr_t pcibr_intr)
667 {
668     pcibr_soft_t            pcibr_soft = pcibr_intr->bi_soft;
669     unsigned                pcibr_int_bits = pcibr_intr->bi_ibits;
670     unsigned                pcibr_int_bit;
671
672     for (pcibr_int_bit = 0; pcibr_int_bit < 8; pcibr_int_bit++)
673         if (pcibr_int_bits & (1 << pcibr_int_bit))
674             return xtalk_intr_cpu_get(pcibr_soft->bs_intr[pcibr_int_bit].bsi_xtalk_intr);
675     return 0;
676 }
677
678 /* =====================================================================
679  *    INTERRUPT HANDLING
680  */
681 void
682 pcibr_clearwidint(pcibr_soft_t pcibr_soft)
683 {
684     pcireg_intr_dst_set(pcibr_soft, 0);
685 }
686
687
688 void
689 pcibr_setwidint(xtalk_intr_t intr)
690 {
691     xwidgetnum_t            targ = xtalk_intr_target_get(intr);
692     iopaddr_t               addr = xtalk_intr_addr_get(intr);
693     xtalk_intr_vector_t     vect = xtalk_intr_vector_get(intr);
694
695     pcibr_soft_t           bridge = (pcibr_soft_t)xtalk_intr_sfarg_get(intr);
696
697     pcireg_intr_dst_target_id_set(bridge, targ);
698     pcireg_intr_dst_addr_set(bridge, addr);
699     pcireg_intr_host_err_set(bridge, vect);
700 }