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