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