Merge git://git.kernel.org/pub/scm/linux/kernel/git/brodo/pcmcia-2.6/
[safe/jmp/linux-2.6] / arch / sh / cchips / hd6446x / hd64461 / setup.c
1 /*
2  *      $Id: setup.c,v 1.5 2004/03/16 00:07:50 lethal Exp $
3  *      Copyright (C) 2000 YAEGASHI Takeshi
4  *      Hitachi HD64461 companion chip support
5  */
6
7 #include <linux/config.h>
8 #include <linux/sched.h>
9 #include <linux/module.h>
10 #include <linux/kernel.h>
11 #include <linux/param.h>
12 #include <linux/interrupt.h>
13 #include <linux/init.h>
14 #include <linux/irq.h>
15
16 #include <asm/io.h>
17 #include <asm/irq.h>
18
19 #include <asm/hd64461/hd64461.h>
20
21 static void disable_hd64461_irq(unsigned int irq)
22 {
23         unsigned long flags;
24         unsigned short nimr;
25         unsigned short mask = 1 << (irq - HD64461_IRQBASE);
26
27         local_irq_save(flags);
28         nimr = inw(HD64461_NIMR);
29         nimr |= mask;
30         outw(nimr, HD64461_NIMR);
31         local_irq_restore(flags);
32 }
33
34 static void enable_hd64461_irq(unsigned int irq)
35 {
36         unsigned long flags;
37         unsigned short nimr;
38         unsigned short mask = 1 << (irq - HD64461_IRQBASE);
39
40         local_irq_save(flags);
41         nimr = inw(HD64461_NIMR);
42         nimr &= ~mask;
43         outw(nimr, HD64461_NIMR);
44         local_irq_restore(flags);
45 }
46
47 static void mask_and_ack_hd64461(unsigned int irq)
48 {
49         disable_hd64461_irq(irq);
50 #ifdef CONFIG_HD64461_ENABLER
51         if (irq == HD64461_IRQBASE + 13)
52                 outb(0x00, HD64461_PCC1CSCR);
53 #endif
54 }
55
56 static void end_hd64461_irq(unsigned int irq)
57 {
58         if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS)))
59                 enable_hd64461_irq(irq);
60 }
61
62 static unsigned int startup_hd64461_irq(unsigned int irq)
63 {
64         enable_hd64461_irq(irq);
65         return 0;
66 }
67
68 static void shutdown_hd64461_irq(unsigned int irq)
69 {
70         disable_hd64461_irq(irq);
71 }
72
73 static struct hw_interrupt_type hd64461_irq_type = {
74         .typename       = "HD64461-IRQ",
75         .startup        = startup_hd64461_irq,
76         .shutdown       = shutdown_hd64461_irq,
77         .enable         = enable_hd64461_irq,
78         .disable        = disable_hd64461_irq,
79         .ack            = mask_and_ack_hd64461,
80         .end            = end_hd64461_irq,
81 };
82
83 static irqreturn_t hd64461_interrupt(int irq, void *dev_id, struct pt_regs *regs)
84 {
85         printk(KERN_INFO
86                "HD64461: spurious interrupt, nirr: 0x%x nimr: 0x%x\n",
87                inw(HD64461_NIRR), inw(HD64461_NIMR));
88
89         return IRQ_NONE;
90 }
91
92 static struct {
93         int (*func) (int, void *);
94         void *dev;
95 } hd64461_demux[HD64461_IRQ_NUM];
96
97 void hd64461_register_irq_demux(int irq,
98                                 int (*demux) (int irq, void *dev), void *dev)
99 {
100         hd64461_demux[irq - HD64461_IRQBASE].func = demux;
101         hd64461_demux[irq - HD64461_IRQBASE].dev = dev;
102 }
103
104 EXPORT_SYMBOL(hd64461_register_irq_demux);
105
106 void hd64461_unregister_irq_demux(int irq)
107 {
108         hd64461_demux[irq - HD64461_IRQBASE].func = 0;
109 }
110
111 EXPORT_SYMBOL(hd64461_unregister_irq_demux);
112
113 int hd64461_irq_demux(int irq)
114 {
115         if (irq == CONFIG_HD64461_IRQ) {
116                 unsigned short bit;
117                 unsigned short nirr = inw(HD64461_NIRR);
118                 unsigned short nimr = inw(HD64461_NIMR);
119                 int i;
120
121                 nirr &= ~nimr;
122                 for (bit = 1, i = 0; i < 16; bit <<= 1, i++)
123                         if (nirr & bit)
124                                 break;
125                 if (i == 16)
126                         irq = CONFIG_HD64461_IRQ;
127                 else {
128                         irq = HD64461_IRQBASE + i;
129                         if (hd64461_demux[i].func != 0) {
130                                 irq = hd64461_demux[i].func(irq, hd64461_demux[i].dev);
131                         }
132                 }
133         }
134         return __irq_demux(irq);
135 }
136
137 static struct irqaction irq0 = { hd64461_interrupt, SA_INTERRUPT, CPU_MASK_NONE, "HD64461", NULL, NULL };
138
139 int __init setup_hd64461(void)
140 {
141         int i;
142
143         if (!MACH_HD64461)
144                 return 0;
145
146         printk(KERN_INFO
147                "HD64461 configured at 0x%x on irq %d(mapped into %d to %d)\n",
148                CONFIG_HD64461_IOBASE, CONFIG_HD64461_IRQ, HD64461_IRQBASE,
149                HD64461_IRQBASE + 15);
150
151 #if defined(CONFIG_CPU_SUBTYPE_SH7709)  /* Should be at processor specific part.. */
152         outw(0x2240, INTC_ICR1);
153 #endif
154         outw(0xffff, HD64461_NIMR);
155
156         for (i = HD64461_IRQBASE; i < HD64461_IRQBASE + 16; i++) {
157                 irq_desc[i].chip = &hd64461_irq_type;
158         }
159
160         setup_irq(CONFIG_HD64461_IRQ, &irq0);
161
162 #ifdef CONFIG_HD64461_ENABLER
163         printk(KERN_INFO "HD64461: enabling PCMCIA devices\n");
164         outb(0x4c, HD64461_PCC1CSCIER);
165         outb(0x00, HD64461_PCC1CSCR);
166 #endif
167
168         return 0;
169 }
170
171 module_init(setup_hd64461);