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