ftp://ftp.kernel.org/pub/linux/kernel/v2.6/linux-2.6.6.tar.bz2
[linux-2.6.git] / drivers / parisc / gsc.c
1 /*
2  * Interrupt management for most GSC and related devices.
3  *
4  * (c) Copyright 1999 Alex deVries for The Puffin Group
5  * (c) Copyright 1999 Grant Grundler for Hewlett-Packard
6  * (c) Copyright 1999 Matthew Wilcox
7  * (c) Copyright 2000 Helge Deller
8  * (c) Copyright 2001 Matthew Wilcox for Hewlett-Packard
9  *
10  *      This program is free software; you can redistribute it and/or modify
11  *      it under the terms of the GNU General Public License as published by
12  *      the Free Software Foundation; either version 2 of the License, or
13  *      (at your option) any later version.
14  */
15
16 #include <linux/bitops.h>
17 #include <linux/config.h>
18 #include <linux/errno.h>
19 #include <linux/init.h>
20 #include <linux/interrupt.h>
21 #include <linux/ioport.h>
22 #include <linux/module.h>
23 #include <linux/slab.h>
24 #include <linux/types.h>
25
26 #include <asm/hardware.h>
27 #include <asm/io.h>
28 #include <asm/irq.h>
29
30 #include "gsc.h"
31
32 /* This sets the vmerge boundary and size, it's here because it has to
33  * be available on all platforms (zero means no-virtual merging) */
34 unsigned long parisc_vmerge_boundary = 0;
35 unsigned long parisc_vmerge_max_size = 0;
36
37 #undef DEBUG
38
39 #ifdef DEBUG
40 #define DEBPRINTK printk
41 #else
42 #define DEBPRINTK(x,...)
43 #endif
44
45 int gsc_alloc_irq(struct gsc_irq *i)
46 {
47         int irq = txn_alloc_irq();
48         if (irq < 0) {
49                 printk("cannot get irq\n");
50                 return irq;
51         }
52
53         i->txn_addr = txn_alloc_addr(irq);
54         i->txn_data = txn_alloc_data(irq, GSC_EIM_WIDTH);
55         i->irq = irq;
56
57         return irq;
58 }
59
60 int gsc_claim_irq(struct gsc_irq *i, int irq)
61 {
62         int c = irq;
63
64         irq += IRQ_FROM_REGION(CPU_IRQ_REGION); /* virtualize the IRQ first */
65
66         irq = txn_claim_irq(irq);
67         if (irq < 0) {
68                 printk("cannot claim irq %d\n", c);
69                 return irq;
70         }
71
72         i->txn_addr = txn_alloc_addr(irq);
73         i->txn_data = txn_alloc_data(irq, GSC_EIM_WIDTH);
74         i->irq = irq;
75
76         return irq;
77 }
78
79 EXPORT_SYMBOL(gsc_alloc_irq);
80 EXPORT_SYMBOL(gsc_claim_irq);
81
82 /* IRQ bits must be numbered from Most Significant Bit */
83 #define GSC_FIX_IRQ(x)  (31-(x))
84 #define GSC_MASK_IRQ(x) (1<<(GSC_FIX_IRQ(x)))
85
86 /* Common interrupt demultiplexer used by Asp, Lasi & Wax.  */
87 irqreturn_t busdev_barked(int busdev_irq, void *dev, struct pt_regs *regs)
88 {
89         unsigned long irq;
90         struct busdevice *busdev = (struct busdevice *) dev;
91
92         /* 
93             Don't need to protect OFFSET_IRR with spinlock since this is
94             the only place it's touched.
95             Protect busdev_region by disabling this region's interrupts,
96             modifying the region, and then re-enabling the region.
97         */
98
99         irq = gsc_readl(busdev->hpa+OFFSET_IRR);
100         if (irq == 0) {
101                 printk(KERN_ERR "%s: barking without apparent reason.\n", busdev->name);
102         } else {
103                 DEBPRINTK ("%s (0x%x) barked, mask=0x%x, irq=%d\n", 
104                     busdev->name, busdev->busdev_region->data.irqbase, 
105                     irq, GSC_FIX_IRQ(ffs(irq))+1 );
106
107                 do_irq_mask(irq, busdev->busdev_region, regs);
108         }
109         return IRQ_HANDLED;
110 }
111
112 static void
113 busdev_disable_irq(void *irq_dev, int irq)
114 {
115         /* Disable the IRQ line by clearing the bit in the IMR */
116         u32 imr = gsc_readl(BUSDEV_DEV(irq_dev)->hpa+OFFSET_IMR);
117         imr &= ~(GSC_MASK_IRQ(irq));
118
119         DEBPRINTK( KERN_WARNING "%s(%p, %d) %s: IMR 0x%x\n", 
120                     __FUNCTION__, irq_dev, irq, BUSDEV_DEV(irq_dev)->name, imr);
121
122         gsc_writel(imr, BUSDEV_DEV(irq_dev)->hpa+OFFSET_IMR);
123 }
124
125
126 static void
127 busdev_enable_irq(void *irq_dev, int irq)
128 {
129         /* Enable the IRQ line by setting the bit in the IMR */
130         unsigned long addr = BUSDEV_DEV(irq_dev)->hpa + OFFSET_IMR;
131         u32 imr = gsc_readl(addr);
132         imr |= GSC_MASK_IRQ(irq);
133
134         DEBPRINTK (KERN_WARNING "%s(%p, %d) %s: IMR 0x%x\n", 
135                     __FUNCTION__, irq_dev, irq, BUSDEV_DEV(irq_dev)->name, imr);
136
137         gsc_writel(imr, addr);
138 //      gsc_writel(~0L, addr);
139
140 /* FIXME: read IPR to make sure the IRQ isn't already pending.
141 **   If so, we need to read IRR and manually call do_irq_mask().
142 **   This code should be shared with busdev_unmask_irq().
143 */
144 }
145
146 static void
147 busdev_mask_irq(void *irq_dev, int irq)
148 {
149 /* FIXME: Clear the IMR bit in busdev for that IRQ */
150 }
151
152 static void
153 busdev_unmask_irq(void *irq_dev, int irq)
154 {
155 /* FIXME: Read IPR. Set the IMR bit in busdev for that IRQ.
156    call do_irq_mask() if IPR is non-zero
157 */
158 }
159
160 struct irq_region_ops busdev_irq_ops = {
161         .disable_irq =  busdev_disable_irq,
162         .enable_irq =   busdev_enable_irq,
163         .mask_irq =     busdev_mask_irq,
164         .unmask_irq =   busdev_unmask_irq
165 };
166
167
168 int gsc_common_irqsetup(struct parisc_device *parent, struct busdevice *busdev)
169 {
170         struct resource *res;
171
172         busdev->gsc = parent;
173
174         /* the IRQs we simulate */
175         busdev->busdev_region = alloc_irq_region(32, &busdev_irq_ops,
176                                                  busdev->name, busdev);
177         if (!busdev->busdev_region)
178                 return -ENOMEM;
179
180         /* allocate resource region */
181         res = request_mem_region(busdev->hpa, 0x100000, busdev->name);
182         if (res) {
183                 res->flags = IORESOURCE_MEM;    /* do not mark it busy ! */
184         }
185
186 #if 0
187         printk(KERN_WARNING "%s IRQ %d EIM 0x%x", busdev->name,
188                         busdev->parent_irq, busdev->eim);
189         if (gsc_readl(busdev->hpa + OFFSET_IMR))
190                 printk("  IMR is non-zero! (0x%x)",
191                                 gsc_readl(busdev->hpa + OFFSET_IMR));
192         printk("\n");
193 #endif
194
195         return 0;
196 }
197
198 extern struct parisc_driver lasi_driver;
199 extern struct parisc_driver asp_driver;
200 extern struct parisc_driver wax_driver;
201
202 void __init gsc_init(void)
203 {
204 #ifdef CONFIG_GSC_LASI
205         register_parisc_driver(&lasi_driver);
206         register_parisc_driver(&asp_driver);
207 #endif
208 #ifdef CONFIG_GSC_WAX
209         register_parisc_driver(&wax_driver);
210 #endif
211 }