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