Path: blob/master/arch/ia64/sn/pci/tioca_provider.c
15126 views
/*1* This file is subject to the terms and conditions of the GNU General Public2* License. See the file "COPYING" in the main directory of this archive3* for more details.4*5* Copyright (C) 2003-2005 Silicon Graphics, Inc. All Rights Reserved.6*/78#include <linux/types.h>9#include <linux/interrupt.h>10#include <linux/pci.h>11#include <linux/bitmap.h>12#include <linux/slab.h>13#include <asm/sn/sn_sal.h>14#include <asm/sn/addrs.h>15#include <asm/sn/io.h>16#include <asm/sn/pcidev.h>17#include <asm/sn/pcibus_provider_defs.h>18#include <asm/sn/tioca_provider.h>1920u32 tioca_gart_found;21EXPORT_SYMBOL(tioca_gart_found); /* used by agp-sgi */2223LIST_HEAD(tioca_list);24EXPORT_SYMBOL(tioca_list); /* used by agp-sgi */2526static int tioca_gart_init(struct tioca_kernel *);2728/**29* tioca_gart_init - Initialize SGI TIOCA GART30* @tioca_common: ptr to common prom/kernel struct identifying the31*32* If the indicated tioca has devices present, initialize its associated33* GART MMR's and kernel memory.34*/35static int36tioca_gart_init(struct tioca_kernel *tioca_kern)37{38u64 ap_reg;39u64 offset;40struct page *tmp;41struct tioca_common *tioca_common;42struct tioca __iomem *ca_base;4344tioca_common = tioca_kern->ca_common;45ca_base = (struct tioca __iomem *)tioca_common->ca_common.bs_base;4647if (list_empty(tioca_kern->ca_devices))48return 0;4950ap_reg = 0;5152/*53* Validate aperature size54*/5556switch (CA_APERATURE_SIZE >> 20) {57case 4:58ap_reg |= (0x3ff << CA_GART_AP_SIZE_SHFT); /* 4MB */59break;60case 8:61ap_reg |= (0x3fe << CA_GART_AP_SIZE_SHFT); /* 8MB */62break;63case 16:64ap_reg |= (0x3fc << CA_GART_AP_SIZE_SHFT); /* 16MB */65break;66case 32:67ap_reg |= (0x3f8 << CA_GART_AP_SIZE_SHFT); /* 32 MB */68break;69case 64:70ap_reg |= (0x3f0 << CA_GART_AP_SIZE_SHFT); /* 64 MB */71break;72case 128:73ap_reg |= (0x3e0 << CA_GART_AP_SIZE_SHFT); /* 128 MB */74break;75case 256:76ap_reg |= (0x3c0 << CA_GART_AP_SIZE_SHFT); /* 256 MB */77break;78case 512:79ap_reg |= (0x380 << CA_GART_AP_SIZE_SHFT); /* 512 MB */80break;81case 1024:82ap_reg |= (0x300 << CA_GART_AP_SIZE_SHFT); /* 1GB */83break;84case 2048:85ap_reg |= (0x200 << CA_GART_AP_SIZE_SHFT); /* 2GB */86break;87case 4096:88ap_reg |= (0x000 << CA_GART_AP_SIZE_SHFT); /* 4 GB */89break;90default:91printk(KERN_ERR "%s: Invalid CA_APERATURE_SIZE "92"0x%lx\n", __func__, (ulong) CA_APERATURE_SIZE);93return -1;94}9596/*97* Set up other aperature parameters98*/99100if (PAGE_SIZE >= 16384) {101tioca_kern->ca_ap_pagesize = 16384;102ap_reg |= CA_GART_PAGE_SIZE;103} else {104tioca_kern->ca_ap_pagesize = 4096;105}106107tioca_kern->ca_ap_size = CA_APERATURE_SIZE;108tioca_kern->ca_ap_bus_base = CA_APERATURE_BASE;109tioca_kern->ca_gart_entries =110tioca_kern->ca_ap_size / tioca_kern->ca_ap_pagesize;111112ap_reg |= (CA_GART_AP_ENB_AGP | CA_GART_AP_ENB_PCI);113ap_reg |= tioca_kern->ca_ap_bus_base;114115/*116* Allocate and set up the GART117*/118119tioca_kern->ca_gart_size = tioca_kern->ca_gart_entries * sizeof(u64);120tmp =121alloc_pages_node(tioca_kern->ca_closest_node,122GFP_KERNEL | __GFP_ZERO,123get_order(tioca_kern->ca_gart_size));124125if (!tmp) {126printk(KERN_ERR "%s: Could not allocate "127"%llu bytes (order %d) for GART\n",128__func__,129tioca_kern->ca_gart_size,130get_order(tioca_kern->ca_gart_size));131return -ENOMEM;132}133134tioca_kern->ca_gart = page_address(tmp);135tioca_kern->ca_gart_coretalk_addr =136PHYS_TO_TIODMA(virt_to_phys(tioca_kern->ca_gart));137138/*139* Compute PCI/AGP convenience fields140*/141142offset = CA_PCI32_MAPPED_BASE - CA_APERATURE_BASE;143tioca_kern->ca_pciap_base = CA_PCI32_MAPPED_BASE;144tioca_kern->ca_pciap_size = CA_PCI32_MAPPED_SIZE;145tioca_kern->ca_pcigart_start = offset / tioca_kern->ca_ap_pagesize;146tioca_kern->ca_pcigart_base =147tioca_kern->ca_gart_coretalk_addr + offset;148tioca_kern->ca_pcigart =149&tioca_kern->ca_gart[tioca_kern->ca_pcigart_start];150tioca_kern->ca_pcigart_entries =151tioca_kern->ca_pciap_size / tioca_kern->ca_ap_pagesize;152tioca_kern->ca_pcigart_pagemap =153kzalloc(tioca_kern->ca_pcigart_entries / 8, GFP_KERNEL);154if (!tioca_kern->ca_pcigart_pagemap) {155free_pages((unsigned long)tioca_kern->ca_gart,156get_order(tioca_kern->ca_gart_size));157return -1;158}159160offset = CA_AGP_MAPPED_BASE - CA_APERATURE_BASE;161tioca_kern->ca_gfxap_base = CA_AGP_MAPPED_BASE;162tioca_kern->ca_gfxap_size = CA_AGP_MAPPED_SIZE;163tioca_kern->ca_gfxgart_start = offset / tioca_kern->ca_ap_pagesize;164tioca_kern->ca_gfxgart_base =165tioca_kern->ca_gart_coretalk_addr + offset;166tioca_kern->ca_gfxgart =167&tioca_kern->ca_gart[tioca_kern->ca_gfxgart_start];168tioca_kern->ca_gfxgart_entries =169tioca_kern->ca_gfxap_size / tioca_kern->ca_ap_pagesize;170171/*172* various control settings:173* use agp op-combining174* use GET semantics to fetch memory175* participate in coherency domain176* DISABLE GART PREFETCHING due to hw bug tracked in SGI PV930029177*/178179__sn_setq_relaxed(&ca_base->ca_control1,180CA_AGPDMA_OP_ENB_COMBDELAY); /* PV895469 ? */181__sn_clrq_relaxed(&ca_base->ca_control2, CA_GART_MEM_PARAM);182__sn_setq_relaxed(&ca_base->ca_control2,183(0x2ull << CA_GART_MEM_PARAM_SHFT));184tioca_kern->ca_gart_iscoherent = 1;185__sn_clrq_relaxed(&ca_base->ca_control2,186(CA_GART_WR_PREFETCH_ENB | CA_GART_RD_PREFETCH_ENB));187188/*189* Unmask GART fetch error interrupts. Clear residual errors first.190*/191192writeq(CA_GART_FETCH_ERR, &ca_base->ca_int_status_alias);193writeq(CA_GART_FETCH_ERR, &ca_base->ca_mult_error_alias);194__sn_clrq_relaxed(&ca_base->ca_int_mask, CA_GART_FETCH_ERR);195196/*197* Program the aperature and gart registers in TIOCA198*/199200writeq(ap_reg, &ca_base->ca_gart_aperature);201writeq(tioca_kern->ca_gart_coretalk_addr|1, &ca_base->ca_gart_ptr_table);202203return 0;204}205206/**207* tioca_fastwrite_enable - enable AGP FW for a tioca and its functions208* @tioca_kernel: structure representing the CA209*210* Given a CA, scan all attached functions making sure they all support211* FastWrite. If so, enable FastWrite for all functions and the CA itself.212*/213214void215tioca_fastwrite_enable(struct tioca_kernel *tioca_kern)216{217int cap_ptr;218u32 reg;219struct tioca __iomem *tioca_base;220struct pci_dev *pdev;221struct tioca_common *common;222223common = tioca_kern->ca_common;224225/*226* Scan all vga controllers on this bus making sure they all227* support FW. If not, return.228*/229230list_for_each_entry(pdev, tioca_kern->ca_devices, bus_list) {231if (pdev->class != (PCI_CLASS_DISPLAY_VGA << 8))232continue;233234cap_ptr = pci_find_capability(pdev, PCI_CAP_ID_AGP);235if (!cap_ptr)236return; /* no AGP CAP means no FW */237238pci_read_config_dword(pdev, cap_ptr + PCI_AGP_STATUS, ®);239if (!(reg & PCI_AGP_STATUS_FW))240return; /* function doesn't support FW */241}242243/*244* Set fw for all vga fn's245*/246247list_for_each_entry(pdev, tioca_kern->ca_devices, bus_list) {248if (pdev->class != (PCI_CLASS_DISPLAY_VGA << 8))249continue;250251cap_ptr = pci_find_capability(pdev, PCI_CAP_ID_AGP);252pci_read_config_dword(pdev, cap_ptr + PCI_AGP_COMMAND, ®);253reg |= PCI_AGP_COMMAND_FW;254pci_write_config_dword(pdev, cap_ptr + PCI_AGP_COMMAND, reg);255}256257/*258* Set ca's fw to match259*/260261tioca_base = (struct tioca __iomem*)common->ca_common.bs_base;262__sn_setq_relaxed(&tioca_base->ca_control1, CA_AGP_FW_ENABLE);263}264265EXPORT_SYMBOL(tioca_fastwrite_enable); /* used by agp-sgi */266267/**268* tioca_dma_d64 - create a DMA mapping using 64-bit direct mode269* @paddr: system physical address270*271* Map @paddr into 64-bit CA bus space. No device context is necessary.272* Bits 53:0 come from the coretalk address. We just need to mask in the273* following optional bits of the 64-bit pci address:274*275* 63:60 - Coretalk Packet Type - 0x1 for Mem Get/Put (coherent)276* 0x2 for PIO (non-coherent)277* We will always use 0x1278* 55:55 - Swap bytes Currently unused279*/280static u64281tioca_dma_d64(unsigned long paddr)282{283dma_addr_t bus_addr;284285bus_addr = PHYS_TO_TIODMA(paddr);286287BUG_ON(!bus_addr);288BUG_ON(bus_addr >> 54);289290/* Set upper nibble to Cache Coherent Memory op */291bus_addr |= (1UL << 60);292293return bus_addr;294}295296/**297* tioca_dma_d48 - create a DMA mapping using 48-bit direct mode298* @pdev: linux pci_dev representing the function299* @paddr: system physical address300*301* Map @paddr into 64-bit bus space of the CA associated with @pcidev_info.302*303* The CA agp 48 bit direct address falls out as follows:304*305* When direct mapping AGP addresses, the 48 bit AGP address is306* constructed as follows:307*308* [47:40] - Low 8 bits of the page Node ID extracted from coretalk309* address [47:40]. The upper 8 node bits are fixed310* and come from the xxx register bits [5:0]311* [39:38] - Chiplet ID extracted from coretalk address [39:38]312* [37:00] - node offset extracted from coretalk address [37:00]313*314* Since the node id in general will be non-zero, and the chiplet id315* will always be non-zero, it follows that the device must support316* a dma mask of at least 0xffffffffff (40 bits) to target node 0317* and in general should be 0xffffffffffff (48 bits) to target nodes318* up to 255. Nodes above 255 need the support of the xxx register,319* and so a given CA can only directly target nodes in the range320* xxx - xxx+255.321*/322static u64323tioca_dma_d48(struct pci_dev *pdev, u64 paddr)324{325struct tioca_common *tioca_common;326struct tioca __iomem *ca_base;327u64 ct_addr;328dma_addr_t bus_addr;329u32 node_upper;330u64 agp_dma_extn;331struct pcidev_info *pcidev_info = SN_PCIDEV_INFO(pdev);332333tioca_common = (struct tioca_common *)pcidev_info->pdi_pcibus_info;334ca_base = (struct tioca __iomem *)tioca_common->ca_common.bs_base;335336ct_addr = PHYS_TO_TIODMA(paddr);337if (!ct_addr)338return 0;339340bus_addr = (dma_addr_t) (ct_addr & 0xffffffffffffUL);341node_upper = ct_addr >> 48;342343if (node_upper > 64) {344printk(KERN_ERR "%s: coretalk addr 0x%p node id out "345"of range\n", __func__, (void *)ct_addr);346return 0;347}348349agp_dma_extn = __sn_readq_relaxed(&ca_base->ca_agp_dma_addr_extn);350if (node_upper != (agp_dma_extn >> CA_AGP_DMA_NODE_ID_SHFT)) {351printk(KERN_ERR "%s: coretalk upper node (%u) "352"mismatch with ca_agp_dma_addr_extn (%llu)\n",353__func__,354node_upper, (agp_dma_extn >> CA_AGP_DMA_NODE_ID_SHFT));355return 0;356}357358return bus_addr;359}360361/**362* tioca_dma_mapped - create a DMA mapping using a CA GART363* @pdev: linux pci_dev representing the function364* @paddr: host physical address to map365* @req_size: len (bytes) to map366*367* Map @paddr into CA address space using the GART mechanism. The mapped368* dma_addr_t is guaranteed to be contiguous in CA bus space.369*/370static dma_addr_t371tioca_dma_mapped(struct pci_dev *pdev, unsigned long paddr, size_t req_size)372{373int ps, ps_shift, entry, entries, mapsize;374u64 xio_addr, end_xio_addr;375struct tioca_common *tioca_common;376struct tioca_kernel *tioca_kern;377dma_addr_t bus_addr = 0;378struct tioca_dmamap *ca_dmamap;379void *map;380unsigned long flags;381struct pcidev_info *pcidev_info = SN_PCIDEV_INFO(pdev);382383tioca_common = (struct tioca_common *)pcidev_info->pdi_pcibus_info;384tioca_kern = (struct tioca_kernel *)tioca_common->ca_kernel_private;385386xio_addr = PHYS_TO_TIODMA(paddr);387if (!xio_addr)388return 0;389390spin_lock_irqsave(&tioca_kern->ca_lock, flags);391392/*393* allocate a map struct394*/395396ca_dmamap = kzalloc(sizeof(struct tioca_dmamap), GFP_ATOMIC);397if (!ca_dmamap)398goto map_return;399400/*401* Locate free entries that can hold req_size. Account for402* unaligned start/length when allocating.403*/404405ps = tioca_kern->ca_ap_pagesize; /* will be power of 2 */406ps_shift = ffs(ps) - 1;407end_xio_addr = xio_addr + req_size - 1;408409entries = (end_xio_addr >> ps_shift) - (xio_addr >> ps_shift) + 1;410411map = tioca_kern->ca_pcigart_pagemap;412mapsize = tioca_kern->ca_pcigart_entries;413414entry = bitmap_find_next_zero_area(map, mapsize, 0, entries, 0);415if (entry >= mapsize) {416kfree(ca_dmamap);417goto map_return;418}419420bitmap_set(map, entry, entries);421422bus_addr = tioca_kern->ca_pciap_base + (entry * ps);423424ca_dmamap->cad_dma_addr = bus_addr;425ca_dmamap->cad_gart_size = entries;426ca_dmamap->cad_gart_entry = entry;427list_add(&ca_dmamap->cad_list, &tioca_kern->ca_dmamaps);428429if (xio_addr % ps) {430tioca_kern->ca_pcigart[entry] = tioca_paddr_to_gart(xio_addr);431bus_addr += xio_addr & (ps - 1);432xio_addr &= ~(ps - 1);433xio_addr += ps;434entry++;435}436437while (xio_addr < end_xio_addr) {438tioca_kern->ca_pcigart[entry] = tioca_paddr_to_gart(xio_addr);439xio_addr += ps;440entry++;441}442443tioca_tlbflush(tioca_kern);444445map_return:446spin_unlock_irqrestore(&tioca_kern->ca_lock, flags);447return bus_addr;448}449450/**451* tioca_dma_unmap - release CA mapping resources452* @pdev: linux pci_dev representing the function453* @bus_addr: bus address returned by an earlier tioca_dma_map454* @dir: mapping direction (unused)455*456* Locate mapping resources associated with @bus_addr and release them.457* For mappings created using the direct modes (64 or 48) there are no458* resources to release.459*/460static void461tioca_dma_unmap(struct pci_dev *pdev, dma_addr_t bus_addr, int dir)462{463int i, entry;464struct tioca_common *tioca_common;465struct tioca_kernel *tioca_kern;466struct tioca_dmamap *map;467struct pcidev_info *pcidev_info = SN_PCIDEV_INFO(pdev);468unsigned long flags;469470tioca_common = (struct tioca_common *)pcidev_info->pdi_pcibus_info;471tioca_kern = (struct tioca_kernel *)tioca_common->ca_kernel_private;472473/* return straight away if this isn't be a mapped address */474475if (bus_addr < tioca_kern->ca_pciap_base ||476bus_addr >= (tioca_kern->ca_pciap_base + tioca_kern->ca_pciap_size))477return;478479spin_lock_irqsave(&tioca_kern->ca_lock, flags);480481list_for_each_entry(map, &tioca_kern->ca_dmamaps, cad_list)482if (map->cad_dma_addr == bus_addr)483break;484485BUG_ON(map == NULL);486487entry = map->cad_gart_entry;488489for (i = 0; i < map->cad_gart_size; i++, entry++) {490clear_bit(entry, tioca_kern->ca_pcigart_pagemap);491tioca_kern->ca_pcigart[entry] = 0;492}493tioca_tlbflush(tioca_kern);494495list_del(&map->cad_list);496spin_unlock_irqrestore(&tioca_kern->ca_lock, flags);497kfree(map);498}499500/**501* tioca_dma_map - map pages for PCI DMA502* @pdev: linux pci_dev representing the function503* @paddr: host physical address to map504* @byte_count: bytes to map505*506* This is the main wrapper for mapping host physical pages to CA PCI space.507* The mapping mode used is based on the devices dma_mask. As a last resort508* use the GART mapped mode.509*/510static u64511tioca_dma_map(struct pci_dev *pdev, unsigned long paddr, size_t byte_count, int dma_flags)512{513u64 mapaddr;514515/*516* Not supported for now ...517*/518if (dma_flags & SN_DMA_MSI)519return 0;520521/*522* If card is 64 or 48 bit addressable, use a direct mapping. 32523* bit direct is so restrictive w.r.t. where the memory resides that524* we don't use it even though CA has some support.525*/526527if (pdev->dma_mask == ~0UL)528mapaddr = tioca_dma_d64(paddr);529else if (pdev->dma_mask == 0xffffffffffffUL)530mapaddr = tioca_dma_d48(pdev, paddr);531else532mapaddr = 0;533534/* Last resort ... use PCI portion of CA GART */535536if (mapaddr == 0)537mapaddr = tioca_dma_mapped(pdev, paddr, byte_count);538539return mapaddr;540}541542/**543* tioca_error_intr_handler - SGI TIO CA error interrupt handler544* @irq: unused545* @arg: pointer to tioca_common struct for the given CA546*547* Handle a CA error interrupt. Simply a wrapper around a SAL call which548* defers processing to the SGI prom.549*/550static irqreturn_t551tioca_error_intr_handler(int irq, void *arg)552{553struct tioca_common *soft = arg;554struct ia64_sal_retval ret_stuff;555u64 segment;556u64 busnum;557ret_stuff.status = 0;558ret_stuff.v0 = 0;559560segment = soft->ca_common.bs_persist_segment;561busnum = soft->ca_common.bs_persist_busnum;562563SAL_CALL_NOLOCK(ret_stuff,564(u64) SN_SAL_IOIF_ERROR_INTERRUPT,565segment, busnum, 0, 0, 0, 0, 0);566567return IRQ_HANDLED;568}569570/**571* tioca_bus_fixup - perform final PCI fixup for a TIO CA bus572* @prom_bussoft: Common prom/kernel struct representing the bus573*574* Replicates the tioca_common pointed to by @prom_bussoft in kernel575* space. Allocates and initializes a kernel-only area for a given CA,576* and sets up an irq for handling CA error interrupts.577*578* On successful setup, returns the kernel version of tioca_common back to579* the caller.580*/581static void *582tioca_bus_fixup(struct pcibus_bussoft *prom_bussoft, struct pci_controller *controller)583{584struct tioca_common *tioca_common;585struct tioca_kernel *tioca_kern;586struct pci_bus *bus;587588/* sanity check prom rev */589590if (is_shub1() && sn_sal_rev() < 0x0406) {591printk592(KERN_ERR "%s: SGI prom rev 4.06 or greater required "593"for tioca support\n", __func__);594return NULL;595}596597/*598* Allocate kernel bus soft and copy from prom.599*/600601tioca_common = kzalloc(sizeof(struct tioca_common), GFP_KERNEL);602if (!tioca_common)603return NULL;604605memcpy(tioca_common, prom_bussoft, sizeof(struct tioca_common));606tioca_common->ca_common.bs_base = (unsigned long)607ioremap(REGION_OFFSET(tioca_common->ca_common.bs_base),608sizeof(struct tioca_common));609610/* init kernel-private area */611612tioca_kern = kzalloc(sizeof(struct tioca_kernel), GFP_KERNEL);613if (!tioca_kern) {614kfree(tioca_common);615return NULL;616}617618tioca_kern->ca_common = tioca_common;619spin_lock_init(&tioca_kern->ca_lock);620INIT_LIST_HEAD(&tioca_kern->ca_dmamaps);621tioca_kern->ca_closest_node =622nasid_to_cnodeid(tioca_common->ca_closest_nasid);623tioca_common->ca_kernel_private = (u64) tioca_kern;624625bus = pci_find_bus(tioca_common->ca_common.bs_persist_segment,626tioca_common->ca_common.bs_persist_busnum);627BUG_ON(!bus);628tioca_kern->ca_devices = &bus->devices;629630/* init GART */631632if (tioca_gart_init(tioca_kern) < 0) {633kfree(tioca_kern);634kfree(tioca_common);635return NULL;636}637638tioca_gart_found++;639list_add(&tioca_kern->ca_list, &tioca_list);640641if (request_irq(SGI_TIOCA_ERROR,642tioca_error_intr_handler,643IRQF_SHARED, "TIOCA error", (void *)tioca_common))644printk(KERN_WARNING645"%s: Unable to get irq %d. "646"Error interrupts won't be routed for TIOCA bus %d\n",647__func__, SGI_TIOCA_ERROR,648(int)tioca_common->ca_common.bs_persist_busnum);649650sn_set_err_irq_affinity(SGI_TIOCA_ERROR);651652/* Setup locality information */653controller->node = tioca_kern->ca_closest_node;654return tioca_common;655}656657static struct sn_pcibus_provider tioca_pci_interfaces = {658.dma_map = tioca_dma_map,659.dma_map_consistent = tioca_dma_map,660.dma_unmap = tioca_dma_unmap,661.bus_fixup = tioca_bus_fixup,662.force_interrupt = NULL,663.target_interrupt = NULL664};665666/**667* tioca_init_provider - init SN PCI provider ops for TIO CA668*/669int670tioca_init_provider(void)671{672sn_pci_provider[PCIIO_ASIC_TYPE_TIOCA] = &tioca_pci_interfaces;673return 0;674}675676677