void sn_teardown_msi_irq(unsigned int irq) { nasid_t nasid; int widget; struct pci_dev *pdev; struct pcidev_info *sn_pdev; struct sn_irq_info *sn_irq_info; struct pcibus_bussoft *bussoft; struct sn_pcibus_provider *provider; sn_irq_info = sn_msi_info[irq].sn_irq_info; if (sn_irq_info == NULL || sn_irq_info->irq_int_bit >= 0) return; sn_pdev = (struct pcidev_info *)sn_irq_info->irq_pciioinfo; pdev = sn_pdev->pdi_linux_pcidev; provider = SN_PCIDEV_BUSPROVIDER(pdev); (*provider->dma_unmap)(pdev, sn_msi_info[irq].pci_addr, PCI_DMA_FROMDEVICE); sn_msi_info[irq].pci_addr = 0; bussoft = SN_PCIDEV_BUSSOFT(pdev); nasid = NASID_GET(bussoft->bs_base); widget = (nasid & 1) ? TIO_SWIN_WIDGETNUM(bussoft->bs_base) : SWIN_WIDGETNUM(bussoft->bs_base); sn_intr_free(nasid, widget, sn_irq_info); sn_msi_info[irq].sn_irq_info = NULL; destroy_irq(irq); }
void tiocx_irq_free(struct sn_irq_info *sn_irq_info) { u64 bridge = (u64) sn_irq_info->irq_bridge; nasid_t nasid = NASID_GET(bridge); int widget; if (nasid & 1) { widget = TIO_SWIN_WIDGETNUM(bridge); tiocx_intr_free(nasid, widget, sn_irq_info); kfree(sn_irq_info); } }
void sn_dma_flush(uint64_t addr) { nasid_t nasid; int is_tio; int wid_num; int i, j; uint64_t flags; uint64_t itte; struct hubdev_info *hubinfo; volatile struct sn_flush_device_list *p; struct sn_flush_nasid_entry *flush_nasid_list; if (!sn_ioif_inited) return; nasid = NASID_GET(addr); if (-1 == nasid_to_cnodeid(nasid)) return; hubinfo = (NODEPDA(nasid_to_cnodeid(nasid)))->pdinfo; if (!hubinfo) { BUG(); } flush_nasid_list = &hubinfo->hdi_flush_nasid_list; if (flush_nasid_list->widget_p == NULL) return; is_tio = (nasid & 1); if (is_tio) { int itte_index; if (TIO_HWIN(addr)) itte_index = 0; else if (TIO_BWIN_WINDOWNUM(addr)) itte_index = TIO_BWIN_WINDOWNUM(addr); else itte_index = -1; if (itte_index >= 0) { itte = flush_nasid_list->iio_itte[itte_index]; if (! TIO_ITTE_VALID(itte)) return; wid_num = TIO_ITTE_WIDGET(itte); } else wid_num = TIO_SWIN_WIDGETNUM(addr); } else { if (BWIN_WINDOWNUM(addr)) { itte = flush_nasid_list->iio_itte[BWIN_WINDOWNUM(addr)]; wid_num = IIO_ITTE_WIDGET(itte); } else wid_num = SWIN_WIDGETNUM(addr); } if (flush_nasid_list->widget_p[wid_num] == NULL) return; p = &flush_nasid_list->widget_p[wid_num][0]; /* find a matching BAR */ for (i = 0; i < DEV_PER_WIDGET; i++) { for (j = 0; j < PCI_ROM_RESOURCE; j++) { if (p->sfdl_bar_list[j].start == 0) break; if (addr >= p->sfdl_bar_list[j].start && addr <= p->sfdl_bar_list[j].end) break; } if (j < PCI_ROM_RESOURCE && p->sfdl_bar_list[j].start != 0) break; p++; } /* if no matching BAR, return without doing anything. */ if (i == DEV_PER_WIDGET) return; /* * For TIOCP use the Device(x) Write Request Buffer Flush Bridge * register since it ensures the data has entered the coherence * domain, unlike PIC. */ if (is_tio) { /* * Note: devices behind TIOCE should never be matched in the * above code, and so the following code is PIC/CP centric. * If CE ever needs the sn_dma_flush mechanism, we will have * to account for that here and in tioce_bus_fixup(). */ uint32_t tio_id = HUB_L(TIO_IOSPACE_ADDR(nasid, TIO_NODE_ID)); uint32_t revnum = XWIDGET_PART_REV_NUM(tio_id); /* TIOCP BRINGUP WAR (PV907516): Don't write buffer flush reg */ if ((1 << XWIDGET_PART_REV_NUM_REV(revnum)) & PV907516) { return; } else { pcireg_wrb_flush_get(p->sfdl_pcibus_info, (p->sfdl_slot - 1)); } } else { spin_lock_irqsave(&((struct sn_flush_device_list *)p)-> sfdl_flush_lock, flags); *p->sfdl_flush_addr = 0; /* force an interrupt. */ *(volatile uint32_t *)(p->sfdl_force_int_addr) = 1; /* wait for the interrupt to come back. */ while (*(p->sfdl_flush_addr) != 0x10f) cpu_relax(); /* okay, everything is synched up. */ spin_unlock_irqrestore((spinlock_t *)&p->sfdl_flush_lock, flags); } return; }
static void sn_set_affinity_irq(unsigned int irq, cpumask_t mask) { struct sn_irq_info *sn_irq_info, *sn_irq_info_safe; int cpuid, cpuphys; cpuid = first_cpu(mask); cpuphys = cpu_physical_id(cpuid); list_for_each_entry_safe(sn_irq_info, sn_irq_info_safe, sn_irq_lh[irq], list) { u64 bridge; int local_widget, status; nasid_t local_nasid; struct sn_irq_info *new_irq_info; struct sn_pcibus_provider *pci_provider; new_irq_info = kmalloc(sizeof(struct sn_irq_info), GFP_ATOMIC); if (new_irq_info == NULL) break; memcpy(new_irq_info, sn_irq_info, sizeof(struct sn_irq_info)); bridge = (u64) new_irq_info->irq_bridge; if (!bridge) { kfree(new_irq_info); break; /* irq is not a device interrupt */ } local_nasid = NASID_GET(bridge); if (local_nasid & 1) local_widget = TIO_SWIN_WIDGETNUM(bridge); else local_widget = SWIN_WIDGETNUM(bridge); /* Free the old PROM new_irq_info structure */ sn_intr_free(local_nasid, local_widget, new_irq_info); /* Update kernels new_irq_info with new target info */ unregister_intr_pda(new_irq_info); /* allocate a new PROM new_irq_info struct */ status = sn_intr_alloc(local_nasid, local_widget, __pa(new_irq_info), irq, cpuid_to_nasid(cpuid), cpuid_to_slice(cpuid)); /* SAL call failed */ if (status) { kfree(new_irq_info); break; } new_irq_info->irq_cpuid = cpuid; register_intr_pda(new_irq_info); pci_provider = sn_pci_provider[new_irq_info->irq_bridge_type]; if (pci_provider && pci_provider->target_interrupt) (pci_provider->target_interrupt)(new_irq_info); spin_lock(&sn_irq_info_lock); list_replace_rcu(&sn_irq_info->list, &new_irq_info->list); spin_unlock(&sn_irq_info_lock); call_rcu(&sn_irq_info->rcu, sn_irq_info_free); #ifdef CONFIG_SMP set_irq_affinity_info((irq & 0xff), cpuphys, 0); #endif }
struct sn_irq_info *sn_retarget_vector(struct sn_irq_info *sn_irq_info, nasid_t nasid, int slice) { int vector; int cpuid; #ifdef CONFIG_SMP int cpuphys; #endif int64_t bridge; int local_widget, status; nasid_t local_nasid; struct sn_irq_info *new_irq_info; struct sn_pcibus_provider *pci_provider; new_irq_info = kmalloc(sizeof(struct sn_irq_info), GFP_ATOMIC); if (new_irq_info == NULL) return NULL; memcpy(new_irq_info, sn_irq_info, sizeof(struct sn_irq_info)); bridge = (u64) new_irq_info->irq_bridge; if (!bridge) { kfree(new_irq_info); return NULL; /* irq is not a device interrupt */ } local_nasid = NASID_GET(bridge); if (local_nasid & 1) local_widget = TIO_SWIN_WIDGETNUM(bridge); else local_widget = SWIN_WIDGETNUM(bridge); vector = sn_irq_info->irq_irq; /* Free the old PROM new_irq_info structure */ sn_intr_free(local_nasid, local_widget, new_irq_info); unregister_intr_pda(new_irq_info); /* allocate a new PROM new_irq_info struct */ status = sn_intr_alloc(local_nasid, local_widget, new_irq_info, vector, nasid, slice); /* SAL call failed */ if (status) { kfree(new_irq_info); return NULL; } /* Update kernels new_irq_info with new target info */ cpuid = nasid_slice_to_cpuid(new_irq_info->irq_nasid, new_irq_info->irq_slice); new_irq_info->irq_cpuid = cpuid; register_intr_pda(new_irq_info); pci_provider = sn_pci_provider[new_irq_info->irq_bridge_type]; /* * If this represents a line interrupt, target it. If it's * an msi (irq_int_bit < 0), it's already targeted. */ if (new_irq_info->irq_int_bit >= 0 && pci_provider && pci_provider->target_interrupt) (pci_provider->target_interrupt)(new_irq_info); spin_lock(&sn_irq_info_lock); list_replace_rcu(&sn_irq_info->list, &new_irq_info->list); spin_unlock(&sn_irq_info_lock); call_rcu(&sn_irq_info->rcu, sn_irq_info_free); #ifdef CONFIG_SMP cpuphys = cpu_physical_id(cpuid); set_irq_affinity_info((vector & 0xff), cpuphys, 0); #endif return new_irq_info; }
int sn_setup_msi_irq(struct pci_dev *pdev, struct msi_desc *entry) { struct msi_msg msg; int widget; int status; nasid_t nasid; u64 bus_addr; struct sn_irq_info *sn_irq_info; struct pcibus_bussoft *bussoft = SN_PCIDEV_BUSSOFT(pdev); struct sn_pcibus_provider *provider = SN_PCIDEV_BUSPROVIDER(pdev); int irq; if (!entry->msi_attrib.is_64) return -EINVAL; if (bussoft == NULL) return -EINVAL; if (provider == NULL || provider->dma_map_consistent == NULL) return -EINVAL; irq = create_irq(); if (irq < 0) return irq; /* * Set up the vector plumbing. Let the prom (via sn_intr_alloc) * decide which cpu to direct this msi at by default. */ nasid = NASID_GET(bussoft->bs_base); widget = (nasid & 1) ? TIO_SWIN_WIDGETNUM(bussoft->bs_base) : SWIN_WIDGETNUM(bussoft->bs_base); sn_irq_info = kzalloc(sizeof(struct sn_irq_info), GFP_KERNEL); if (! sn_irq_info) { destroy_irq(irq); return -ENOMEM; } status = sn_intr_alloc(nasid, widget, sn_irq_info, irq, -1, -1); if (status) { kfree(sn_irq_info); destroy_irq(irq); return -ENOMEM; } sn_irq_info->irq_int_bit = -1; /* mark this as an MSI irq */ sn_irq_fixup(pdev, sn_irq_info); /* Prom probably should fill these in, but doesn't ... */ sn_irq_info->irq_bridge_type = bussoft->bs_asic_type; sn_irq_info->irq_bridge = (void *)bussoft->bs_base; /* * Map the xio address into bus space */ bus_addr = (*provider->dma_map_consistent)(pdev, sn_irq_info->irq_xtalkaddr, sizeof(sn_irq_info->irq_xtalkaddr), SN_DMA_MSI|SN_DMA_ADDR_XIO); if (! bus_addr) { sn_intr_free(nasid, widget, sn_irq_info); kfree(sn_irq_info); destroy_irq(irq); return -ENOMEM; } sn_msi_info[irq].sn_irq_info = sn_irq_info; sn_msi_info[irq].pci_addr = bus_addr; msg.address_hi = (u32)(bus_addr >> 32); msg.address_lo = (u32)(bus_addr & 0x00000000ffffffff); /* * In the SN platform, bit 16 is a "send vector" bit which * must be present in order to move the vector through the system. */ msg.data = 0x100 + irq; irq_set_msi_desc(irq, entry); pci_write_msi_msg(irq, &msg); irq_set_chip_and_handler(irq, &sn_msi_chip, handle_edge_irq); return 0; }
void sn_dma_flush(u64 addr) { nasid_t nasid; int is_tio; int wid_num; int i, j; unsigned long flags; u64 itte; struct hubdev_info *hubinfo; struct sn_flush_device_kernel *p; struct sn_flush_device_common *common; struct sn_flush_nasid_entry *flush_nasid_list; if (!sn_ioif_inited) return; nasid = NASID_GET(addr); if (-1 == nasid_to_cnodeid(nasid)) return; hubinfo = (NODEPDA(nasid_to_cnodeid(nasid)))->pdinfo; BUG_ON(!hubinfo); flush_nasid_list = &hubinfo->hdi_flush_nasid_list; if (flush_nasid_list->widget_p == NULL) return; is_tio = (nasid & 1); if (is_tio) { int itte_index; if (TIO_HWIN(addr)) itte_index = 0; else if (TIO_BWIN_WINDOWNUM(addr)) itte_index = TIO_BWIN_WINDOWNUM(addr); else itte_index = -1; if (itte_index >= 0) { itte = flush_nasid_list->iio_itte[itte_index]; if (! TIO_ITTE_VALID(itte)) return; wid_num = TIO_ITTE_WIDGET(itte); } else wid_num = TIO_SWIN_WIDGETNUM(addr); } else { if (BWIN_WINDOWNUM(addr)) { itte = flush_nasid_list->iio_itte[BWIN_WINDOWNUM(addr)]; wid_num = IIO_ITTE_WIDGET(itte); } else wid_num = SWIN_WIDGETNUM(addr); } if (flush_nasid_list->widget_p[wid_num] == NULL) return; p = &flush_nasid_list->widget_p[wid_num][0]; for (i = 0; i < DEV_PER_WIDGET; i++,p++) { common = p->common; for (j = 0; j < PCI_ROM_RESOURCE; j++) { if (common->sfdl_bar_list[j].start == 0) break; if (addr >= common->sfdl_bar_list[j].start && addr <= common->sfdl_bar_list[j].end) break; } if (j < PCI_ROM_RESOURCE && common->sfdl_bar_list[j].start != 0) break; } if (i == DEV_PER_WIDGET) return; if (is_tio) { u32 tio_id = HUB_L(TIO_IOSPACE_ADDR(nasid, TIO_NODE_ID)); u32 revnum = XWIDGET_PART_REV_NUM(tio_id); if ((1 << XWIDGET_PART_REV_NUM_REV(revnum)) & PV907516) { return; } else { pcireg_wrb_flush_get(common->sfdl_pcibus_info, (common->sfdl_slot - 1)); } } else { spin_lock_irqsave(&p->sfdl_flush_lock, flags); *common->sfdl_flush_addr = 0; *(volatile u32 *)(common->sfdl_force_int_addr) = 1; while (*(common->sfdl_flush_addr) != 0x10f) cpu_relax(); spin_unlock_irqrestore(&p->sfdl_flush_lock, flags); } return; }
int sn_setup_msi_irq(struct pci_dev *pdev, struct msi_desc *entry) { struct msi_msg msg; int widget; int status; nasid_t nasid; u64 bus_addr; struct sn_irq_info *sn_irq_info; struct pcibus_bussoft *bussoft = SN_PCIDEV_BUSSOFT(pdev); struct sn_pcibus_provider *provider = SN_PCIDEV_BUSPROVIDER(pdev); int irq; if (!entry->msi_attrib.is_64) return -EINVAL; if (bussoft == NULL) return -EINVAL; if (provider == NULL || provider->dma_map_consistent == NULL) return -EINVAL; irq = create_irq(); if (irq < 0) return irq; nasid = NASID_GET(bussoft->bs_base); widget = (nasid & 1) ? TIO_SWIN_WIDGETNUM(bussoft->bs_base) : SWIN_WIDGETNUM(bussoft->bs_base); sn_irq_info = kzalloc(sizeof(struct sn_irq_info), GFP_KERNEL); if (! sn_irq_info) { destroy_irq(irq); return -ENOMEM; } status = sn_intr_alloc(nasid, widget, sn_irq_info, irq, -1, -1); if (status) { kfree(sn_irq_info); destroy_irq(irq); return -ENOMEM; } sn_irq_info->irq_int_bit = -1; sn_irq_fixup(pdev, sn_irq_info); sn_irq_info->irq_bridge_type = bussoft->bs_asic_type; sn_irq_info->irq_bridge = (void *)bussoft->bs_base; bus_addr = (*provider->dma_map_consistent)(pdev, sn_irq_info->irq_xtalkaddr, sizeof(sn_irq_info->irq_xtalkaddr), SN_DMA_MSI|SN_DMA_ADDR_XIO); if (! bus_addr) { sn_intr_free(nasid, widget, sn_irq_info); kfree(sn_irq_info); destroy_irq(irq); return -ENOMEM; } sn_msi_info[irq].sn_irq_info = sn_irq_info; sn_msi_info[irq].pci_addr = bus_addr; msg.address_hi = (u32)(bus_addr >> 32); msg.address_lo = (u32)(bus_addr & 0x00000000ffffffff); msg.data = 0x100 + irq; irq_set_msi_desc(irq, entry); write_msi_msg(irq, &msg); irq_set_chip_and_handler(irq, &sn_msi_chip, handle_edge_irq); return 0; }
int sn_msi_setup(struct pci_dev *pdev, unsigned int vector, u32 *addr_hi, u32 *addr_lo, u32 *data) { int widget; int status; nasid_t nasid; u64 bus_addr; struct sn_irq_info *sn_irq_info; struct pcibus_bussoft *bussoft = SN_PCIDEV_BUSSOFT(pdev); struct sn_pcibus_provider *provider = SN_PCIDEV_BUSPROVIDER(pdev); if (bussoft == NULL) return -EINVAL; if (provider == NULL || provider->dma_map_consistent == NULL) return -EINVAL; /* * Set up the vector plumbing. Let the prom (via sn_intr_alloc) * decide which cpu to direct this msi at by default. */ nasid = NASID_GET(bussoft->bs_base); widget = (nasid & 1) ? TIO_SWIN_WIDGETNUM(bussoft->bs_base) : SWIN_WIDGETNUM(bussoft->bs_base); sn_irq_info = kzalloc(sizeof(struct sn_irq_info), GFP_KERNEL); if (! sn_irq_info) return -ENOMEM; status = sn_intr_alloc(nasid, widget, sn_irq_info, vector, -1, -1); if (status) { kfree(sn_irq_info); return -ENOMEM; } sn_irq_info->irq_int_bit = -1; /* mark this as an MSI irq */ sn_irq_fixup(pdev, sn_irq_info); /* Prom probably should fill these in, but doesn't ... */ sn_irq_info->irq_bridge_type = bussoft->bs_asic_type; sn_irq_info->irq_bridge = (void *)bussoft->bs_base; /* * Map the xio address into bus space */ bus_addr = (*provider->dma_map_consistent)(pdev, sn_irq_info->irq_xtalkaddr, sizeof(sn_irq_info->irq_xtalkaddr), SN_DMA_MSI|SN_DMA_ADDR_XIO); if (! bus_addr) { sn_intr_free(nasid, widget, sn_irq_info); kfree(sn_irq_info); return -ENOMEM; } sn_msi_info[vector].sn_irq_info = sn_irq_info; sn_msi_info[vector].pci_addr = bus_addr; *addr_hi = (u32)(bus_addr >> 32); *addr_lo = (u32)(bus_addr & 0x00000000ffffffff); /* * In the SN platform, bit 16 is a "send vector" bit which * must be present in order to move the vector through the system. */ *data = 0x100 + (unsigned int)vector; #ifdef CONFIG_SMP set_irq_affinity_info((vector & 0xff), sn_irq_info->irq_cpuid, 0); #endif return 0; }