ftp://ftp.kernel.org/pub/linux/kernel/v2.6/linux-2.6.6.tar.bz2
[linux-2.6.git] / arch / ia64 / sn / io / sn2 / ml_SN_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) 1992-1997, 2000-2003 Silicon Graphics, Inc.  All Rights Reserved.
7  */
8
9 #include <linux/types.h>
10 #include <linux/slab.h>
11 #include <linux/interrupt.h>
12 #include <asm/smp.h>
13 #include <asm/irq.h>
14 #include <asm/hw_irq.h>
15 #include <asm/sn/sgi.h>
16 #include <asm/sn/iograph.h>
17 #include <asm/sn/hcl.h>
18 #include <asm/sn/labelcl.h>
19 #include <asm/sn/io.h>
20 #include <asm/sn/sn_private.h>
21 #include <asm/sn/klconfig.h>
22 #include <asm/sn/sn_cpuid.h>
23 #include <asm/sn/pci/pciio.h>
24 #include <asm/sn/pci/pcibr.h>
25 #include <asm/sn/xtalk/xtalk.h>
26 #include <asm/sn/pci/pcibr_private.h>
27 #include <asm/sn/intr.h>
28 #include <asm/sn/sn2/shub_mmr_t.h>
29 #include <asm/sn/sn2/shubio.h>
30 #include <asm/sal.h>
31 #include <asm/sn/sn_sal.h>
32 #include <asm/sn/sn2/shub_mmr.h>
33 #include <asm/sn/pda.h>
34
35 extern irqpda_t *irqpdaindr;
36 extern cnodeid_t master_node_get(vertex_hdl_t vhdl);
37 extern nasid_t master_nasid;
38
39 /*  Initialize some shub registers for interrupts, both IO and error. */
40 void intr_init_vecblk(cnodeid_t node)
41 {
42         int                             nasid = cnodeid_to_nasid(node);
43         sh_ii_int0_config_u_t           ii_int_config;
44         cpuid_t                         cpu;
45         cpuid_t                         cpu0, cpu1;
46         nodepda_t                       *lnodepda;
47         sh_ii_int0_enable_u_t           ii_int_enable;
48         sh_int_node_id_config_u_t       node_id_config;
49         sh_local_int5_config_u_t        local5_config;
50         sh_local_int5_enable_u_t        local5_enable;
51
52         if (is_headless_node(node) ) {
53                 struct ia64_sal_retval ret_stuff;
54                 int cnode;
55
56                 /* retarget all interrupts on this node to the master node. */
57                 node_id_config.sh_int_node_id_config_regval = 0;
58                 node_id_config.sh_int_node_id_config_s.node_id = master_nasid;
59                 node_id_config.sh_int_node_id_config_s.id_sel = 1;
60                 HUB_S((unsigned long *)GLOBAL_MMR_ADDR(nasid, SH_INT_NODE_ID_CONFIG),
61                         node_id_config.sh_int_node_id_config_regval);
62                 cnode = nasid_to_cnodeid(master_nasid);
63                 lnodepda = NODEPDA(cnode);
64                 cpu = lnodepda->node_first_cpu;
65                 cpu = cpu_physical_id(cpu);
66                 SAL_CALL(ret_stuff, SN_SAL_REGISTER_CE, nasid, cpu, master_nasid,0,0,0,0);
67                 if (ret_stuff.status < 0)
68                         printk("%s: SN_SAL_REGISTER_CE SAL_CALL failed\n",__FUNCTION__);
69         } else {
70                 lnodepda = NODEPDA(node);
71                 cpu = lnodepda->node_first_cpu;
72                 cpu = cpu_physical_id(cpu);
73         }
74
75         /* Get the physical id's of the cpu's on this node. */
76         cpu0 = nasid_slice_to_cpu_physical_id(nasid, 0);
77         cpu1 = nasid_slice_to_cpu_physical_id(nasid, 2);
78
79         HUB_S( (unsigned long *)GLOBAL_MMR_ADDR(nasid, SH_PI_ERROR_MASK), 0);
80         HUB_S( (unsigned long *)GLOBAL_MMR_ADDR(nasid, SH_PI_CRBP_ERROR_MASK), 0);
81
82         /* Config and enable UART interrupt, all nodes. */
83         local5_config.sh_local_int5_config_regval = 0;
84         local5_config.sh_local_int5_config_s.idx = SGI_UART_VECTOR;
85         local5_config.sh_local_int5_config_s.pid = cpu;
86         HUB_S((unsigned long *)GLOBAL_MMR_ADDR(nasid, SH_LOCAL_INT5_CONFIG),
87                 local5_config.sh_local_int5_config_regval);
88
89         local5_enable.sh_local_int5_enable_regval = 0;
90         local5_enable.sh_local_int5_enable_s.uart_int = 1;
91         HUB_S((unsigned long *)GLOBAL_MMR_ADDR(nasid, SH_LOCAL_INT5_ENABLE),
92                 local5_enable.sh_local_int5_enable_regval);
93
94
95         /* The II_INT_CONFIG register for cpu 0. */
96         ii_int_config.sh_ii_int0_config_regval = 0;
97         ii_int_config.sh_ii_int0_config_s.type = 0;
98         ii_int_config.sh_ii_int0_config_s.agt = 0;
99         ii_int_config.sh_ii_int0_config_s.pid = cpu0;
100         ii_int_config.sh_ii_int0_config_s.base = 0;
101
102         HUB_S((unsigned long *)GLOBAL_MMR_ADDR(nasid, SH_II_INT0_CONFIG),
103                 ii_int_config.sh_ii_int0_config_regval);
104
105
106         /* The II_INT_CONFIG register for cpu 1. */
107         ii_int_config.sh_ii_int0_config_regval = 0;
108         ii_int_config.sh_ii_int0_config_s.type = 0;
109         ii_int_config.sh_ii_int0_config_s.agt = 0;
110         ii_int_config.sh_ii_int0_config_s.pid = cpu1;
111         ii_int_config.sh_ii_int0_config_s.base = 0;
112
113         HUB_S((unsigned long *)GLOBAL_MMR_ADDR(nasid, SH_II_INT1_CONFIG),
114                 ii_int_config.sh_ii_int0_config_regval);
115
116
117         /* Enable interrupts for II_INT0 and 1. */
118         ii_int_enable.sh_ii_int0_enable_regval = 0;
119         ii_int_enable.sh_ii_int0_enable_s.ii_enable = 1;
120
121         HUB_S((unsigned long *)GLOBAL_MMR_ADDR(nasid, SH_II_INT0_ENABLE),
122                 ii_int_enable.sh_ii_int0_enable_regval);
123         HUB_S((unsigned long *)GLOBAL_MMR_ADDR(nasid, SH_II_INT1_ENABLE),
124                 ii_int_enable.sh_ii_int0_enable_regval);
125 }
126
127 static int intr_reserve_level(cpuid_t cpu, int bit)
128 {
129         irqpda_t        *irqs = irqpdaindr;
130         int             min_shared;
131         int             i;
132
133         if (bit < 0) {
134                 for (i = IA64_SN2_FIRST_DEVICE_VECTOR; i <= IA64_SN2_LAST_DEVICE_VECTOR; i++) {
135                         if (irqs->irq_flags[i] == 0) {
136                                 bit = i;
137                                 break;
138                         }
139                 }
140         }
141
142         if (bit < 0) {  /* ran out of irqs.  Have to share.  This will be rare. */
143                 min_shared = 256;
144                 for (i=IA64_SN2_FIRST_DEVICE_VECTOR; i < IA64_SN2_LAST_DEVICE_VECTOR; i++) {
145                         /* Share with the same device class */
146                         /* XXX: gross layering violation.. */
147                         if (irqpdaindr->curr->vendor == irqpdaindr->device_dev[i]->vendor &&
148                                 irqpdaindr->curr->device == irqpdaindr->device_dev[i]->device &&
149                                 irqpdaindr->share_count[i] < min_shared) {
150                                         min_shared = irqpdaindr->share_count[i];
151                                         bit = i;
152                         }
153                 }
154         
155                 min_shared = 256;
156                 if (bit < 0) {  /* didn't find a matching device, just pick one. This will be */
157                                 /* exceptionally rare. */
158                         for (i=IA64_SN2_FIRST_DEVICE_VECTOR; i < IA64_SN2_LAST_DEVICE_VECTOR; i++) {
159                                 if (irqpdaindr->share_count[i] < min_shared) {
160                                         min_shared = irqpdaindr->share_count[i];
161                                         bit = i;
162                                 }
163                         }
164                 }
165                 irqpdaindr->share_count[bit]++;
166         }
167
168         if (!(irqs->irq_flags[bit] & SN2_IRQ_SHARED)) {
169                 if (irqs->irq_flags[bit] & SN2_IRQ_RESERVED)
170                         return -1;
171                 irqs->num_irq_used++;
172         }
173
174         irqs->irq_flags[bit] |= SN2_IRQ_RESERVED;
175         return bit;
176 }
177
178 void intr_unreserve_level(cpuid_t cpu,
179                 int bit)
180 {
181         irqpda_t        *irqs = irqpdaindr;
182
183         if (irqs->irq_flags[bit] & SN2_IRQ_RESERVED) {
184                 irqs->num_irq_used--;
185                 irqs->irq_flags[bit] &= ~SN2_IRQ_RESERVED;
186         }
187 }
188
189 int intr_connect_level(cpuid_t cpu, int bit)
190 {
191         irqpda_t        *irqs = irqpdaindr;
192
193         if (!(irqs->irq_flags[bit] & SN2_IRQ_SHARED) &&
194              (irqs->irq_flags[bit] & SN2_IRQ_CONNECTED))
195                 return -1;
196  
197         irqs->irq_flags[bit] |= SN2_IRQ_CONNECTED;
198         return bit;
199 }
200
201 int intr_disconnect_level(cpuid_t cpu, int bit)
202 {
203         irqpda_t        *irqs = irqpdaindr;
204
205         if (!(irqs->irq_flags[bit] & SN2_IRQ_CONNECTED))
206                 return -1;
207         irqs->irq_flags[bit] &= ~SN2_IRQ_CONNECTED;
208         return bit;
209 }
210
211 /*
212  * Choose a cpu on this node.
213  *
214  * We choose the one with the least number of int's assigned to it.
215  */
216 static cpuid_t intr_cpu_choose_from_node(cnodeid_t cnode)
217 {
218         cpuid_t         cpu, best_cpu = CPU_NONE;
219         int             slice, min_count = 1000;
220
221         for (slice = CPUS_PER_NODE - 1; slice >= 0; slice--) {
222                 int intrs;
223
224                 cpu = cnode_slice_to_cpuid(cnode, slice);
225                 if (cpu == NR_CPUS)
226                         continue;
227                 if (!cpu_online(cpu))
228                         continue;
229
230                 intrs = pdacpu(cpu)->sn_num_irqs;
231
232                 if (min_count > intrs) {
233                         min_count = intrs;
234                         best_cpu = cpu;
235                         if (enable_shub_wars_1_1()) {
236                                 /*
237                                  * Rather than finding the best cpu, always
238                                  * return the first cpu.  This forces all
239                                  * interrupts to the same cpu
240                                  */
241                                 break;
242                         }
243                 }
244         }
245         pdacpu(best_cpu)->sn_num_irqs++;
246         return best_cpu;
247 }
248
249 /*
250  * We couldn't put it on the closest node.  Try to find another one.
251  * Do a stupid round-robin assignment of the node.
252  */
253 static cpuid_t intr_cpu_choose_node(void)
254 {
255         static cnodeid_t last_node = -1;        /* XXX: racy */
256         cnodeid_t candidate_node;
257         cpuid_t cpuid;
258
259         if (last_node >= numnodes)
260                 last_node = 0;
261
262         for (candidate_node = last_node + 1; candidate_node != last_node;
263                         candidate_node++) {
264                 if (candidate_node == numnodes)
265                         candidate_node = 0;
266                 cpuid = intr_cpu_choose_from_node(candidate_node);
267                 if (cpuid != CPU_NONE)
268                         return cpuid;
269         }
270
271         return CPU_NONE;
272 }
273
274 /*
275  * Find the node to assign for this interrupt.
276  *
277  * SN2 + pcibr addressing limitation:
278  *   Due to this limitation, all interrupts from a given bridge must
279  *   go to the name node.  The interrupt must also be targetted for
280  *   the same processor.  This limitation does not exist on PIC.
281  *   But, the processor limitation will stay.  The limitation will be
282  *   similar to the bedrock/xbridge limit regarding PI's
283  */
284 cpuid_t intr_heuristic(vertex_hdl_t dev, int req_bit, int *resp_bit)
285 {
286         cpuid_t         cpuid;
287         vertex_hdl_t    pconn_vhdl;
288         pcibr_soft_t    pcibr_soft;
289         int             bit;
290
291         /* XXX: gross layering violation.. */
292         if (hwgraph_edge_get(dev, EDGE_LBL_PCI, &pconn_vhdl) == GRAPH_SUCCESS) {
293                 pcibr_soft = pcibr_soft_get(pconn_vhdl);
294                 if (pcibr_soft && pcibr_soft->bsi_err_intr) {
295                         /*
296                          * The cpu was chosen already when we assigned
297                          * the error interrupt.
298                          */
299                         cpuid = ((hub_intr_t)pcibr_soft->bsi_err_intr)->i_cpuid;
300                         goto done;
301                 }
302         }
303
304         /*
305          * Need to choose one.  Try the controlling c-brick first.
306          */
307         cpuid = intr_cpu_choose_from_node(master_node_get(dev));
308         if (cpuid == CPU_NONE)
309                 cpuid = intr_cpu_choose_node();
310
311  done:
312         if (cpuid != CPU_NONE) {
313                 bit = intr_reserve_level(cpuid, req_bit);
314                 if (bit >= 0) {
315                         *resp_bit = bit;
316                         return cpuid;
317                 }
318         }
319
320         printk("Cannot target interrupt to target cpu (%ld).\n", cpuid);
321         return CPU_NONE;
322 }