Altix: ACPI SSDT PCI device support
[sfrench/cifs-2.6.git] / arch / ia64 / sn / pci / pcibr / pcibr_provider.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-2004, 2006 Silicon Graphics, Inc. All rights reserved.
7  */
8
9 #include <linux/interrupt.h>
10 #include <linux/types.h>
11 #include <linux/pci.h>
12 #include <asm/sn/addrs.h>
13 #include <asm/sn/geo.h>
14 #include <asm/sn/pcibr_provider.h>
15 #include <asm/sn/pcibus_provider_defs.h>
16 #include <asm/sn/pcidev.h>
17 #include <asm/sn/sn_sal.h>
18 #include <asm/sn/sn2/sn_hwperf.h>
19 #include "xtalk/xwidgetdev.h"
20 #include "xtalk/hubdev.h"
21
22 int
23 sal_pcibr_slot_enable(struct pcibus_info *soft, int device, void *resp,
24                       char **ssdt)
25 {
26         struct ia64_sal_retval ret_stuff;
27         u64 busnum;
28         u64 segment;
29
30         ret_stuff.status = 0;
31         ret_stuff.v0 = 0;
32
33         segment = soft->pbi_buscommon.bs_persist_segment;
34         busnum = soft->pbi_buscommon.bs_persist_busnum;
35         SAL_CALL_NOLOCK(ret_stuff, (u64) SN_SAL_IOIF_SLOT_ENABLE, segment,
36                         busnum, (u64) device, (u64) resp, (u64)ia64_tpa(ssdt),
37                         0, 0);
38
39         return (int)ret_stuff.v0;
40 }
41
42 int
43 sal_pcibr_slot_disable(struct pcibus_info *soft, int device, int action,
44                        void *resp)
45 {
46         struct ia64_sal_retval ret_stuff;
47         u64 busnum;
48         u64 segment;
49
50         ret_stuff.status = 0;
51         ret_stuff.v0 = 0;
52
53         segment = soft->pbi_buscommon.bs_persist_segment;
54         busnum = soft->pbi_buscommon.bs_persist_busnum;
55         SAL_CALL_NOLOCK(ret_stuff, (u64) SN_SAL_IOIF_SLOT_DISABLE,
56                         segment, busnum, (u64) device, (u64) action,
57                         (u64) resp, 0, 0);
58
59         return (int)ret_stuff.v0;
60 }
61
62 static int sal_pcibr_error_interrupt(struct pcibus_info *soft)
63 {
64         struct ia64_sal_retval ret_stuff;
65         u64 busnum;
66         int segment;
67         ret_stuff.status = 0;
68         ret_stuff.v0 = 0;
69
70         segment = soft->pbi_buscommon.bs_persist_segment;
71         busnum = soft->pbi_buscommon.bs_persist_busnum;
72         SAL_CALL_NOLOCK(ret_stuff,
73                         (u64) SN_SAL_IOIF_ERROR_INTERRUPT,
74                         (u64) segment, (u64) busnum, 0, 0, 0, 0, 0);
75
76         return (int)ret_stuff.v0;
77 }
78
79 u16 sn_ioboard_to_pci_bus(struct pci_bus *pci_bus)
80 {
81         s64 rc;
82         u16 ioboard;
83         nasid_t nasid = NASID_GET(SN_PCIBUS_BUSSOFT(pci_bus)->bs_base);
84
85         rc = ia64_sn_sysctl_ioboard_get(nasid, &ioboard);
86         if (rc) {
87                 printk(KERN_WARNING "ia64_sn_sysctl_ioboard_get failed: %ld\n",
88                        rc);
89                 return 0;
90         }
91
92         return ioboard;
93 }
94
95 /* 
96  * PCI Bridge Error interrupt handler.  Gets invoked whenever a PCI 
97  * bridge sends an error interrupt.
98  */
99 static irqreturn_t
100 pcibr_error_intr_handler(int irq, void *arg)
101 {
102         struct pcibus_info *soft = (struct pcibus_info *)arg;
103
104         if (sal_pcibr_error_interrupt(soft) < 0) {
105                 panic("pcibr_error_intr_handler(): Fatal Bridge Error");
106         }
107         return IRQ_HANDLED;
108 }
109
110 void *
111 pcibr_bus_fixup(struct pcibus_bussoft *prom_bussoft, struct pci_controller *controller)
112 {
113         int nasid, cnode, j;
114         struct hubdev_info *hubdev_info;
115         struct pcibus_info *soft;
116         struct sn_flush_device_kernel *sn_flush_device_kernel;
117         struct sn_flush_device_common *common;
118
119         if (! IS_PCI_BRIDGE_ASIC(prom_bussoft->bs_asic_type)) {
120                 return NULL;
121         }
122
123         /*
124          * Allocate kernel bus soft and copy from prom.
125          */
126
127         soft = kmalloc(sizeof(struct pcibus_info), GFP_KERNEL);
128         if (!soft) {
129                 return NULL;
130         }
131
132         memcpy(soft, prom_bussoft, sizeof(struct pcibus_info));
133         soft->pbi_buscommon.bs_base =
134             (((u64) soft->pbi_buscommon.
135               bs_base << 4) >> 4) | __IA64_UNCACHED_OFFSET;
136
137         spin_lock_init(&soft->pbi_lock);
138
139         /*
140          * register the bridge's error interrupt handler
141          */
142         if (request_irq(SGI_PCIASIC_ERROR, pcibr_error_intr_handler,
143                         IRQF_SHARED, "PCIBR error", (void *)(soft))) {
144                 printk(KERN_WARNING
145                        "pcibr cannot allocate interrupt for error handler\n");
146         }
147
148         /* 
149          * Update the Bridge with the "kernel" pagesize 
150          */
151         if (PAGE_SIZE < 16384) {
152                 pcireg_control_bit_clr(soft, PCIBR_CTRL_PAGE_SIZE);
153         } else {
154                 pcireg_control_bit_set(soft, PCIBR_CTRL_PAGE_SIZE);
155         }
156
157         nasid = NASID_GET(soft->pbi_buscommon.bs_base);
158         cnode = nasid_to_cnodeid(nasid);
159         hubdev_info = (struct hubdev_info *)(NODEPDA(cnode)->pdinfo);
160
161         if (hubdev_info->hdi_flush_nasid_list.widget_p) {
162                 sn_flush_device_kernel = hubdev_info->hdi_flush_nasid_list.
163                     widget_p[(int)soft->pbi_buscommon.bs_xid];
164                 if (sn_flush_device_kernel) {
165                         for (j = 0; j < DEV_PER_WIDGET;
166                              j++, sn_flush_device_kernel++) {
167                                 common = sn_flush_device_kernel->common;
168                                 if (common->sfdl_slot == -1)
169                                         continue;
170                                 if ((common->sfdl_persistent_segment ==
171                                      soft->pbi_buscommon.bs_persist_segment) &&
172                                      (common->sfdl_persistent_busnum ==
173                                      soft->pbi_buscommon.bs_persist_busnum))
174                                         common->sfdl_pcibus_info =
175                                             soft;
176                         }
177                 }
178         }
179
180         /* Setup the PMU ATE map */
181         soft->pbi_int_ate_resource.lowest_free_index = 0;
182         soft->pbi_int_ate_resource.ate =
183             kzalloc(soft->pbi_int_ate_size * sizeof(u64), GFP_KERNEL);
184
185         if (!soft->pbi_int_ate_resource.ate) {
186                 kfree(soft);
187                 return NULL;
188         }
189
190         return soft;
191 }
192
193 void pcibr_force_interrupt(struct sn_irq_info *sn_irq_info)
194 {
195         struct pcidev_info *pcidev_info;
196         struct pcibus_info *pcibus_info;
197         int bit = sn_irq_info->irq_int_bit;
198
199         if (! sn_irq_info->irq_bridge)
200                 return;
201
202         pcidev_info = (struct pcidev_info *)sn_irq_info->irq_pciioinfo;
203         if (pcidev_info) {
204                 pcibus_info =
205                     (struct pcibus_info *)pcidev_info->pdi_host_pcidev_info->
206                     pdi_pcibus_info;
207                 pcireg_force_intr_set(pcibus_info, bit);
208         }
209 }
210
211 void pcibr_target_interrupt(struct sn_irq_info *sn_irq_info)
212 {
213         struct pcidev_info *pcidev_info;
214         struct pcibus_info *pcibus_info;
215         int bit = sn_irq_info->irq_int_bit;
216         u64 xtalk_addr = sn_irq_info->irq_xtalkaddr;
217
218         pcidev_info = (struct pcidev_info *)sn_irq_info->irq_pciioinfo;
219         if (pcidev_info) {
220                 pcibus_info =
221                     (struct pcibus_info *)pcidev_info->pdi_host_pcidev_info->
222                     pdi_pcibus_info;
223
224                 /* Disable the device's IRQ   */
225                 pcireg_intr_enable_bit_clr(pcibus_info, (1 << bit));
226
227                 /* Change the device's IRQ    */
228                 pcireg_intr_addr_addr_set(pcibus_info, bit, xtalk_addr);
229
230                 /* Re-enable the device's IRQ */
231                 pcireg_intr_enable_bit_set(pcibus_info, (1 << bit));
232
233                 pcibr_force_interrupt(sn_irq_info);
234         }
235 }
236
237 /*
238  * Provider entries for PIC/CP
239  */
240
241 struct sn_pcibus_provider pcibr_provider = {
242         .dma_map = pcibr_dma_map,
243         .dma_map_consistent = pcibr_dma_map_consistent,
244         .dma_unmap = pcibr_dma_unmap,
245         .bus_fixup = pcibr_bus_fixup,
246         .force_interrupt = pcibr_force_interrupt,
247         .target_interrupt = pcibr_target_interrupt
248 };
249
250 int
251 pcibr_init_provider(void)
252 {
253         sn_pci_provider[PCIIO_ASIC_TYPE_PIC] = &pcibr_provider;
254         sn_pci_provider[PCIIO_ASIC_TYPE_TIOCP] = &pcibr_provider;
255
256         return 0;
257 }
258
259 EXPORT_SYMBOL_GPL(sal_pcibr_slot_enable);
260 EXPORT_SYMBOL_GPL(sal_pcibr_slot_disable);
261 EXPORT_SYMBOL_GPL(sn_ioboard_to_pci_bus);