📄 pciio.c
字号:
/* $Id$ * * 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) 1992 - 1997, 2000-2003 Silicon Graphics, Inc. All rights reserved. */#include <linux/init.h>#include <linux/types.h>#include <linux/pci.h>#include <linux/pci_ids.h>#include <linux/sched.h>#include <linux/ioport.h>#include <linux/slab.h>#include <asm/sn/sgi.h>#include <asm/sn/xtalk/xbow.h> /* Must be before iograph.h to get MAX_PORT_NUM */#include <asm/sn/iograph.h>#include <asm/sn/invent.h>#include <asm/sn/hcl.h>#include <asm/sn/hcl_util.h>#include <asm/sn/labelcl.h>#include <asm/sn/pci/bridge.h>#include <asm/sn/ioerror_handling.h>#include <asm/sn/pci/pciio.h>#include <asm/sn/pci/pciio_private.h>#include <asm/sn/sn_sal.h>#include <asm/sn/io.h>#include <asm/sn/pci/pci_bus_cvlink.h>#include <asm/sn/simulator.h>#define DEBUG_PCIIO#undef DEBUG_PCIIO /* turn this on for yet more console output */char pciio_info_fingerprint[] = "pciio_info";/* ===================================================================== * PCI Generic Bus Provider * Implement PCI provider operations. The pciio* layer provides a * platform-independent interface for PCI devices. This layer * switches among the possible implementations of a PCI adapter. *//* ===================================================================== * Provider Function Location SHORTCUT * * On platforms with only one possible PCI provider, macros can be * set up at the top that cause the table lookups and indirections to * completely disappear. *//* ===================================================================== * Function Table of Contents */#if !defined(DEV_FUNC)extern pciio_provider_t *pciio_to_provider_fns(vertex_hdl_t dev);#endif/* ===================================================================== * Provider Function Location * * If there is more than one possible provider for * this platform, we need to examine the master * vertex of the current vertex for a provider * function structure, and indirect through the * appropriately named member. */#if !defined(DEV_FUNC)pciio_provider_t *pciio_to_provider_fns(vertex_hdl_t dev){ pciio_info_t card_info; pciio_provider_t *provider_fns; /* * We're called with two types of vertices, one is * the bridge vertex (ends with "pci") and the other is the * pci slot vertex (ends with "pci/[0-8]"). For the first type * we need to get the provider from the PFUNCS label. For * the second we get it from fastinfo/c_pops. */ provider_fns = pciio_provider_fns_get(dev); if (provider_fns == NULL) { card_info = pciio_info_get(dev); if (card_info != NULL) { provider_fns = pciio_info_pops_get(card_info); } } if (provider_fns == NULL) { char devname[MAXDEVNAME]; panic("%s: provider_fns == NULL", vertex_to_name(dev, devname, MAXDEVNAME)); } return provider_fns;}#define DEV_FUNC(dev,func) pciio_to_provider_fns(dev)->func#define CAST_PIOMAP(x) ((pciio_piomap_t)(x))#define CAST_DMAMAP(x) ((pciio_dmamap_t)(x))#define CAST_INTR(x) ((pciio_intr_t)(x))#endif/* * Many functions are not passed their vertex * information directly; rather, they must * dive through a resource map. These macros * are available to coordinate this detail. */#define PIOMAP_FUNC(map,func) DEV_FUNC((map)->pp_dev,func)#define DMAMAP_FUNC(map,func) DEV_FUNC((map)->pd_dev,func)#define INTR_FUNC(intr_hdl,func) DEV_FUNC((intr_hdl)->pi_dev,func)/* ===================================================================== * PIO MANAGEMENT * * For mapping system virtual address space to * pciio space on a specified card */pciio_piomap_tpciio_piomap_alloc(vertex_hdl_t dev, /* set up mapping for this device */ device_desc_t dev_desc, /* device descriptor */ pciio_space_t space, /* CFG, MEM, IO, or a device-decoded window */ iopaddr_t addr, /* lowest address (or offset in window) */ size_t byte_count, /* size of region containing our mappings */ size_t byte_count_max, /* maximum size of a mapping */ unsigned flags){ /* defined in sys/pio.h */ return (pciio_piomap_t) DEV_FUNC(dev, piomap_alloc) (dev, dev_desc, space, addr, byte_count, byte_count_max, flags);}voidpciio_piomap_free(pciio_piomap_t pciio_piomap){ PIOMAP_FUNC(pciio_piomap, piomap_free) (CAST_PIOMAP(pciio_piomap));}caddr_tpciio_piomap_addr(pciio_piomap_t pciio_piomap, /* mapping resources */ iopaddr_t pciio_addr, /* map for this pciio address */ size_t byte_count){ /* map this many bytes */ pciio_piomap->pp_kvaddr = PIOMAP_FUNC(pciio_piomap, piomap_addr) (CAST_PIOMAP(pciio_piomap), pciio_addr, byte_count); return pciio_piomap->pp_kvaddr;}voidpciio_piomap_done(pciio_piomap_t pciio_piomap){ PIOMAP_FUNC(pciio_piomap, piomap_done) (CAST_PIOMAP(pciio_piomap));}caddr_tpciio_piotrans_addr(vertex_hdl_t dev, /* translate for this device */ device_desc_t dev_desc, /* device descriptor */ pciio_space_t space, /* CFG, MEM, IO, or a device-decoded window */ iopaddr_t addr, /* starting address (or offset in window) */ size_t byte_count, /* map this many bytes */ unsigned flags){ /* (currently unused) */ return DEV_FUNC(dev, piotrans_addr) (dev, dev_desc, space, addr, byte_count, flags);}caddr_tpciio_pio_addr(vertex_hdl_t dev, /* translate for this device */ device_desc_t dev_desc, /* device descriptor */ pciio_space_t space, /* CFG, MEM, IO, or a device-decoded window */ iopaddr_t addr, /* starting address (or offset in window) */ size_t byte_count, /* map this many bytes */ pciio_piomap_t *mapp, /* where to return the map pointer */ unsigned flags){ /* PIO flags */ pciio_piomap_t map = 0; int errfree = 0; caddr_t res; if (mapp) { map = *mapp; /* possible pre-allocated map */ *mapp = 0; /* record "no map used" */ } res = pciio_piotrans_addr (dev, dev_desc, space, addr, byte_count, flags); if (res) return res; /* pciio_piotrans worked */ if (!map) { map = pciio_piomap_alloc (dev, dev_desc, space, addr, byte_count, byte_count, flags); if (!map) return res; /* pciio_piomap_alloc failed */ errfree = 1; } res = pciio_piomap_addr (map, addr, byte_count); if (!res) { if (errfree) pciio_piomap_free(map); return res; /* pciio_piomap_addr failed */ } if (mapp) *mapp = map; /* pass back map used */ return res; /* pciio_piomap_addr succeeded */}iopaddr_tpciio_piospace_alloc(vertex_hdl_t dev, /* Device requiring space */ device_desc_t dev_desc, /* Device descriptor */ pciio_space_t space, /* MEM32/MEM64/IO */ size_t byte_count, /* Size of mapping */ size_t align){ /* Alignment needed */ if (align < PAGE_SIZE) align = PAGE_SIZE; return DEV_FUNC(dev, piospace_alloc) (dev, dev_desc, space, byte_count, align);}voidpciio_piospace_free(vertex_hdl_t dev, /* Device freeing space */ pciio_space_t space, /* Type of space */ iopaddr_t pciaddr, /* starting address */ size_t byte_count){ /* Range of address */ DEV_FUNC(dev, piospace_free) (dev, space, pciaddr, byte_count);}/* ===================================================================== * DMA MANAGEMENT * * For mapping from pci space to system * physical space. */pciio_dmamap_tpciio_dmamap_alloc(vertex_hdl_t dev, /* set up mappings for this device */ device_desc_t dev_desc, /* device descriptor */ size_t byte_count_max, /* max size of a mapping */ unsigned flags){ /* defined in dma.h */ return (pciio_dmamap_t) DEV_FUNC(dev, dmamap_alloc) (dev, dev_desc, byte_count_max, flags);}voidpciio_dmamap_free(pciio_dmamap_t pciio_dmamap){ DMAMAP_FUNC(pciio_dmamap, dmamap_free) (CAST_DMAMAP(pciio_dmamap));}iopaddr_tpciio_dmamap_addr(pciio_dmamap_t pciio_dmamap, /* use these mapping resources */ paddr_t paddr, /* map for this address */ size_t byte_count){ /* map this many bytes */ return DMAMAP_FUNC(pciio_dmamap, dmamap_addr) (CAST_DMAMAP(pciio_dmamap), paddr, byte_count);}voidpciio_dmamap_done(pciio_dmamap_t pciio_dmamap){ DMAMAP_FUNC(pciio_dmamap, dmamap_done) (CAST_DMAMAP(pciio_dmamap));}iopaddr_tpciio_dmatrans_addr(vertex_hdl_t dev, /* translate for this device */ device_desc_t dev_desc, /* device descriptor */ paddr_t paddr, /* system physical address */ size_t byte_count, /* length */ unsigned flags){ /* defined in dma.h */ return DEV_FUNC(dev, dmatrans_addr) (dev, dev_desc, paddr, byte_count, flags);}iopaddr_tpciio_dma_addr(vertex_hdl_t dev, /* translate for this device */ device_desc_t dev_desc, /* device descriptor */ paddr_t paddr, /* system physical address */ size_t byte_count, /* length */ pciio_dmamap_t *mapp, /* map to use, then map we used */ unsigned flags){ /* PIO flags */ pciio_dmamap_t map = 0; int errfree = 0; iopaddr_t res; if (mapp) { map = *mapp; /* possible pre-allocated map */ *mapp = 0; /* record "no map used" */ } res = pciio_dmatrans_addr (dev, dev_desc, paddr, byte_count, flags); if (res) return res; /* pciio_dmatrans worked */ if (!map) { map = pciio_dmamap_alloc (dev, dev_desc, byte_count, flags); if (!map) return res; /* pciio_dmamap_alloc failed */ errfree = 1; } res = pciio_dmamap_addr (map, paddr, byte_count); if (!res) { if (errfree) pciio_dmamap_free(map); return res; /* pciio_dmamap_addr failed */ } if (mapp) *mapp = map; /* pass back map used */ return res; /* pciio_dmamap_addr succeeded */}voidpciio_dmamap_drain(pciio_dmamap_t map){ DMAMAP_FUNC(map, dmamap_drain) (CAST_DMAMAP(map));}voidpciio_dmaaddr_drain(vertex_hdl_t dev, paddr_t addr, size_t size){ DEV_FUNC(dev, dmaaddr_drain) (dev, addr, size);}voidpciio_dmalist_drain(vertex_hdl_t dev, alenlist_t list){ DEV_FUNC(dev, dmalist_drain) (dev, list);}/* ===================================================================== * INTERRUPT MANAGEMENT * * Allow crosstalk devices to establish interrupts *//* * Allocate resources required for an interrupt as specified in intr_desc. * Return resource handle in intr_hdl. */pciio_intr_tpciio_intr_alloc(vertex_hdl_t dev, /* which Crosstalk device */ device_desc_t dev_desc, /* device descriptor */ pciio_intr_line_t lines, /* INTR line(s) to attach */ vertex_hdl_t owner_dev){ /* owner of this interrupt */ return (pciio_intr_t) DEV_FUNC(dev, intr_alloc) (dev, dev_desc, lines, owner_dev);}/* * Free resources consumed by intr_alloc. */voidpciio_intr_free(pciio_intr_t intr_hdl){ INTR_FUNC(intr_hdl, intr_free) (CAST_INTR(intr_hdl));}/* * Associate resources allocated with a previous pciio_intr_alloc call with the * described handler, arg, name, etc. * * Returns 0 on success, returns <0 on failure. */intpciio_intr_connect(pciio_intr_t intr_hdl, intr_func_t intr_func, intr_arg_t intr_arg) /* pciio intr resource handle */{ return INTR_FUNC(intr_hdl, intr_connect) (CAST_INTR(intr_hdl), intr_func, intr_arg);}/* * Disassociate handler with the specified interrupt. */voidpciio_intr_disconnect(pciio_intr_t intr_hdl){ INTR_FUNC(intr_hdl, intr_disconnect) (CAST_INTR(intr_hdl));}/* * Return a hwgraph vertex that represents the CPU currently * targeted by an interrupt. */vertex_hdl_tpciio_intr_cpu_get(pciio_intr_t intr_hdl){ return INTR_FUNC(intr_hdl, intr_cpu_get) (CAST_INTR(intr_hdl));}voidpciio_slot_func_to_name(char *name, pciio_slot_t slot, pciio_function_t func){ /* * standard connection points: * * PCIIO_SLOT_NONE: .../pci/direct * PCIIO_FUNC_NONE: .../pci/<SLOT> ie. .../pci/3 * multifunction: .../pci/<SLOT><FUNC> ie. .../pci/3c */ if (slot == PCIIO_SLOT_NONE) sprintf(name, EDGE_LBL_DIRECT); else if (func == PCIIO_FUNC_NONE) sprintf(name, "%d", slot); else sprintf(name, "%d%c", slot, 'a'+func);}/* * pciio_cardinfo_get * * Get the pciio info structure corresponding to the * specified PCI "slot" (we like it when the same index * number is used for the PCI IDSEL, the REQ/GNT pair, * and the interrupt line being used for INTA. We like * it so much we call it the slot number). */static pciio_info_tpciio_cardinfo_get( vertex_hdl_t pciio_vhdl, pciio_slot_t pci_slot){ char namebuf[16]; pciio_info_t info = 0; vertex_hdl_t conn; pciio_slot_func_to_name(namebuf, pci_slot, PCIIO_FUNC_NONE); if (GRAPH_SUCCESS == hwgraph_traverse(pciio_vhdl, namebuf, &conn)) { info = pciio_info_chk(conn); hwgraph_vertex_unref(conn); } return info;}/* * pciio_error_handler: * dispatch an error to the appropriate * pciio connection point, or process * it as a generic pci error. * Yes, the first parameter is the * provider vertex at the middle of * the bus; we get to the pciio connect * point using the ioerror widgetdev field. * * This function is called by the * specific PCI provider, after it has figured * out where on the PCI bus (including which slot, * if it can tell) the error came from. *//*ARGSUSED */intpciio_error_handler( vertex_hdl_t pciio_vhdl, int error_code, ioerror_mode_t mode, ioerror_t *ioerror){ pciio_info_t pciio_info; vertex_hdl_t pconn_vhdl; pciio_slot_t slot; int retval;#if DEBUG && ERROR_DEBUG printk("%v: pciio_error_handler\n", pciio_vhdl);#endif IOERR_PRINTF(printk(KERN_NOTICE "%v: PCI Bus Error: Error code: %d Error mode: %d\n", pciio_vhdl, error_code, mode)); /* If there is an error handler sitting on * the "no-slot" connection point, give it * first crack at the error. NOTE: it is * quite possible that this function may * do further refining of the ioerror. */ pciio_info = pciio_cardinfo_get(pciio_vhdl, PCIIO_SLOT_NONE); if (pciio_info && pciio_info->c_efunc) { pconn_vhdl = pciio_info_dev_get(pciio_info); retval = pciio_info->c_efunc (pciio_info->c_einfo, error_code, mode, ioerror); if (retval != IOERROR_UNHANDLED) return retval; } /* Is the error associated with a particular slot? */ if (IOERROR_FIELDVALID(ioerror, widgetdev)) { short widgetdev; /* * NOTE : * widgetdev is a 4byte value encoded as slot in the higher order * 2 bytes and function in the lower order 2 bytes. */ IOERROR_GETVALUE(widgetdev, ioerror, widgetdev); slot = pciio_widgetdev_slot_get(widgetdev); /* If this slot has an error handler,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -