cregit-Linux how code gets into the kernel

Release 4.14 arch/ia64/sn/pci/pcibr/pcibr_dma.c

/*
 * This file is subject to the terms and conditions of the GNU General Public
 * License.  See the file "COPYING" in the main directory of this archive
 * for more details.
 *
 * Copyright (C) 2001-2005 Silicon Graphics, Inc. All rights reserved.
 */

#include <linux/types.h>
#include <linux/pci.h>
#include <linux/export.h>
#include <asm/sn/addrs.h>
#include <asm/sn/geo.h>
#include <asm/sn/pcibr_provider.h>
#include <asm/sn/pcibus_provider_defs.h>
#include <asm/sn/pcidev.h>
#include <asm/sn/pic.h>
#include <asm/sn/sn_sal.h>
#include <asm/sn/tiocp.h>
#include "tio.h"
#include "xtalk/xwidgetdev.h"
#include "xtalk/hubdev.h"

extern int sn_ioif_inited;

/* =====================================================================
 *    DMA MANAGEMENT
 *
 *      The Bridge ASIC provides three methods of doing DMA: via a "direct map"
 *      register available in 32-bit PCI space (which selects a contiguous 2G
 *      address space on some other widget), via "direct" addressing via 64-bit
 *      PCI space (all destination information comes from the PCI address,
 *      including transfer attributes), and via a "mapped" region that allows 
 *      a bunch of different small mappings to be established with the PMU.
 *
 *      For efficiency, we most prefer to use the 32bit direct mapping facility,
 *      since it requires no resource allocations. The advantage of using the
 *      PMU over the 64-bit direct is that single-cycle PCI addressing can be
 *      used; the advantage of using 64-bit direct over PMU addressing is that
 *      we do not have to allocate entries in the PMU.
 */


static dma_addr_t pcibr_dmamap_ate32(struct pcidev_info *info, u64 paddr, size_t req_size, u64 flags, int dma_flags) { struct pcidev_info *pcidev_info = info->pdi_host_pcidev_info; struct pcibus_info *pcibus_info = (struct pcibus_info *)pcidev_info-> pdi_pcibus_info; u8 internal_device = (PCI_SLOT(pcidev_info->pdi_host_pcidev_info-> pdi_linux_pcidev->devfn)) - 1; int ate_count; int ate_index; u64 ate_flags = flags | PCI32_ATE_V; u64 ate; u64 pci_addr; u64 xio_addr; u64 offset; /* PIC in PCI-X mode does not supports 32bit PageMap mode */ if (IS_PIC_SOFT(pcibus_info) && IS_PCIX(pcibus_info)) { return 0; } /* Calculate the number of ATEs needed. */ if (!(MINIMAL_ATE_FLAG(paddr, req_size))) { ate_count = IOPG((IOPGSIZE - 1) /* worst case start offset */ +req_size /* max mapping bytes */ - 1) + 1; /* round UP */ } else { /* assume requested target is page aligned */ ate_count = IOPG(req_size /* max mapping bytes */ - 1) + 1; /* round UP */ } /* Get the number of ATEs required. */ ate_index = pcibr_ate_alloc(pcibus_info, ate_count); if (ate_index < 0) return 0; /* In PCI-X mode, Prefetch not supported */ if (IS_PCIX(pcibus_info)) ate_flags &= ~(PCI32_ATE_PREF); if (SN_DMA_ADDRTYPE(dma_flags == SN_DMA_ADDR_PHYS)) xio_addr = IS_PIC_SOFT(pcibus_info) ? PHYS_TO_DMA(paddr) : PHYS_TO_TIODMA(paddr); else xio_addr = paddr; offset = IOPGOFF(xio_addr); ate = ate_flags | (xio_addr - offset); /* If PIC, put the targetid in the ATE */ if (IS_PIC_SOFT(pcibus_info)) { ate |= (pcibus_info->pbi_hub_xid << PIC_ATE_TARGETID_SHFT); } /* * If we're mapping for MSI, set the MSI bit in the ATE. If it's a * TIOCP based pci bus, we also need to set the PIO bit in the ATE. */ if (dma_flags & SN_DMA_MSI) { ate |= PCI32_ATE_MSI; if (IS_TIOCP_SOFT(pcibus_info)) ate |= PCI32_ATE_PIO; } ate_write(pcibus_info, ate_index, ate_count, ate); /* * Set up the DMA mapped Address. */ pci_addr = PCI32_MAPPED_BASE + offset + IOPGSIZE * ate_index; /* * If swap was set in device in pcibr_endian_set() * we need to turn swapping on. */ if (pcibus_info->pbi_devreg[internal_device] & PCIBR_DEV_SWAP_DIR) ATE_SWAP_ON(pci_addr); return pci_addr; }

Contributors

PersonTokensPropCommitsCommitProp
Patrick Gefre27784.71%120.00%
Mark Maule288.56%240.00%
Mike Habeck144.28%120.00%
Prarit Bhargava82.45%120.00%
Total327100.00%5100.00%


static dma_addr_t pcibr_dmatrans_direct64(struct pcidev_info * info, u64 paddr, u64 dma_attributes, int dma_flags) { struct pcibus_info *pcibus_info = (struct pcibus_info *) ((info->pdi_host_pcidev_info)->pdi_pcibus_info); u64 pci_addr; /* Translate to Crosstalk View of Physical Address */ if (SN_DMA_ADDRTYPE(dma_flags) == SN_DMA_ADDR_PHYS) pci_addr = IS_PIC_SOFT(pcibus_info) ? PHYS_TO_DMA(paddr) : PHYS_TO_TIODMA(paddr); else pci_addr = paddr; pci_addr |= dma_attributes; /* Handle Bus mode */ if (IS_PCIX(pcibus_info)) pci_addr &= ~PCI64_ATTR_PREF; /* Handle Bridge Chipset differences */ if (IS_PIC_SOFT(pcibus_info)) { pci_addr |= ((u64) pcibus_info-> pbi_hub_xid << PIC_PCI64_ATTR_TARG_SHFT); } else pci_addr |= (dma_flags & SN_DMA_MSI) ? TIOCP_PCI64_CMDTYPE_MSI : TIOCP_PCI64_CMDTYPE_MEM; /* If PCI mode, func zero uses VCHAN0, every other func uses VCHAN1 */ if (!IS_PCIX(pcibus_info) && PCI_FUNC(info->pdi_linux_pcidev->devfn)) pci_addr |= PCI64_ATTR_VIRTUAL; return pci_addr; }

Contributors

PersonTokensPropCommitsCommitProp
Patrick Gefre12078.43%233.33%
Mark Maule2616.99%233.33%
Prarit Bhargava42.61%116.67%
Jeremy Higdon31.96%116.67%
Total153100.00%6100.00%


static dma_addr_t pcibr_dmatrans_direct32(struct pcidev_info * info, u64 paddr, size_t req_size, u64 flags, int dma_flags) { struct pcidev_info *pcidev_info = info->pdi_host_pcidev_info; struct pcibus_info *pcibus_info = (struct pcibus_info *)pcidev_info-> pdi_pcibus_info; u64 xio_addr; u64 xio_base; u64 offset; u64 endoff; if (IS_PCIX(pcibus_info)) { return 0; } if (dma_flags & SN_DMA_MSI) return 0; if (SN_DMA_ADDRTYPE(dma_flags) == SN_DMA_ADDR_PHYS) xio_addr = IS_PIC_SOFT(pcibus_info) ? PHYS_TO_DMA(paddr) : PHYS_TO_TIODMA(paddr); else xio_addr = paddr; xio_base = pcibus_info->pbi_dir_xbase; offset = xio_addr - xio_base; endoff = req_size + offset; if ((req_size > (1ULL << 31)) || /* Too Big */ (xio_addr < xio_base) || /* Out of range for mappings */ (endoff > (1ULL << 31))) { /* Too Big */ return 0; } return PCI32_DIRECT_BASE | offset; }

Contributors

PersonTokensPropCommitsCommitProp
Patrick Gefre13680.47%125.00%
Mark Maule2715.98%250.00%
Prarit Bhargava63.55%125.00%
Total169100.00%4100.00%

/* * Wrapper routine for freeing DMA maps * DMA mappings for Direct 64 and 32 do not have any DMA maps. */
void pcibr_dma_unmap(struct pci_dev *hwdev, dma_addr_t dma_handle, int direction) { struct pcidev_info *pcidev_info = SN_PCIDEV_INFO(hwdev); struct pcibus_info *pcibus_info = (struct pcibus_info *)pcidev_info->pdi_pcibus_info; if (IS_PCI32_MAPPED(dma_handle)) { int ate_index; ate_index = IOPG((ATE_SWAP_OFF(dma_handle) - PCI32_MAPPED_BASE)); pcibr_ate_free(pcibus_info, ate_index); } }

Contributors

PersonTokensPropCommitsCommitProp
Patrick Gefre6183.56%150.00%
Mark Maule1216.44%150.00%
Total73100.00%2100.00%

/* * On SN systems there is a race condition between a PIO read response and * DMA's. In rare cases, the read response may beat the DMA, causing the * driver to think that data in memory is complete and meaningful. This code * eliminates that race. This routine is called by the PIO read routines * after doing the read. For PIC this routine then forces a fake interrupt * on another line, which is logically associated with the slot that the PIO * is addressed to. It then spins while watching the memory location that * the interrupt is targeted to. When the interrupt response arrives, we * are sure that the DMA has landed in memory and it is safe for the driver * to proceed. For TIOCP use the Device(x) Write Request Buffer Flush * Bridge register since it ensures the data has entered the coherence domain, * unlike the PIC Device(x) Write Request Buffer Flush register. */
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]; /* find a matching BAR */ 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 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(). */ u32 tio_id = HUB_L(TIO_IOSPACE_ADDR(nasid, TIO_NODE_ID)); u32 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(common->sfdl_pcibus_info, (common->sfdl_slot - 1)); } } else { spin_lock_irqsave(&p->sfdl_flush_lock, flags); *common->sfdl_flush_addr = 0; /* force an interrupt. */ *(volatile u32 *)(common->sfdl_force_int_addr) = 1; /* wait for the interrupt to come back. */ while (*(common->sfdl_flush_addr) != 0x10f) cpu_relax(); /* okay, everything is synched up. */ spin_unlock_irqrestore(&p->sfdl_flush_lock, flags); } return; }

Contributors

PersonTokensPropCommitsCommitProp
Patrick Gefre35571.86%110.00%
Mark Maule9318.83%110.00%
Prarit Bhargava306.07%220.00%
Jack Steiner71.42%220.00%
Stoyan Gaydarov30.61%110.00%
Jesse Barnes20.40%110.00%
Alexey Dobriyan20.40%110.00%
Mike Habeck20.40%110.00%
Total494100.00%10100.00%

/* * DMA interfaces. Called from pci_dma.c routines. */
dma_addr_t pcibr_dma_map(struct pci_dev * hwdev, unsigned long phys_addr, size_t size, int dma_flags) { dma_addr_t dma_handle; struct pcidev_info *pcidev_info = SN_PCIDEV_INFO(hwdev); /* SN cannot support DMA addresses smaller than 32 bits. */ if (hwdev->dma_mask < 0x7fffffff) { return 0; } if (hwdev->dma_mask == ~0UL) { /* * Handle the most common case: 64 bit cards. This * call should always succeed. */ dma_handle = pcibr_dmatrans_direct64(pcidev_info, phys_addr, PCI64_ATTR_PREF, dma_flags); } else { /* Handle 32-63 bit cards via direct mapping */ dma_handle = pcibr_dmatrans_direct32(pcidev_info, phys_addr, size, 0, dma_flags); if (!dma_handle) { /* * It is a 32 bit card and we cannot do direct mapping, * so we use an ATE. */ dma_handle = pcibr_dmamap_ate32(pcidev_info, phys_addr, size, PCI32_ATE_PREF, dma_flags); } } return dma_handle; }

Contributors

PersonTokensPropCommitsCommitProp
Patrick Gefre6353.85%133.33%
Mark Maule5446.15%266.67%
Total117100.00%3100.00%


dma_addr_t pcibr_dma_map_consistent(struct pci_dev * hwdev, unsigned long phys_addr, size_t size, int dma_flags) { dma_addr_t dma_handle; struct pcidev_info *pcidev_info = SN_PCIDEV_INFO(hwdev); if (hwdev->dev.coherent_dma_mask == ~0UL) { dma_handle = pcibr_dmatrans_direct64(pcidev_info, phys_addr, PCI64_ATTR_BAR, dma_flags); } else { dma_handle = (dma_addr_t) pcibr_dmamap_ate32(pcidev_info, phys_addr, size, PCI32_ATE_BAR, dma_flags); } return dma_handle; }

Contributors

PersonTokensPropCommitsCommitProp
Mark Maule4554.22%266.67%
Patrick Gefre3845.78%133.33%
Total83100.00%3100.00%

EXPORT_SYMBOL(sn_dma_flush);

Overall Contributors

PersonTokensPropCommitsCommitProp
Patrick Gefre108973.98%210.00%
Mark Maule28919.63%420.00%
Prarit Bhargava563.80%315.00%
Mike Habeck161.09%210.00%
Jack Steiner70.48%210.00%
Stoyan Gaydarov30.20%15.00%
Jeremy Higdon30.20%15.00%
Paul Gortmaker30.20%15.00%
Jesse Barnes20.14%15.00%
Alexey Dobriyan20.14%15.00%
Simon Arlott10.07%15.00%
Lucas De Marchi10.07%15.00%
Total1472100.00%20100.00%
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with cregit.