pcibr.c
上传用户:jlfgdled
上传日期:2013-04-10
资源大小:33168k
文件大小:227k
- pci_hi_fb = 0x00200000; /* High Memory FreeBlock Base */
- pci_hi_fl = 0x3FFFFFFF; /* High Memory FreeBlock Last */
- PCI_ADDR_SPACE_LIMITS_STORE();
- /* build "no-slot" connection point
- */
- pcibr_info = pcibr_device_info_new
- (pcibr_soft, PCIIO_SLOT_NONE, PCIIO_FUNC_NONE,
- PCIIO_VENDOR_ID_NONE, PCIIO_DEVICE_ID_NONE);
- noslot_conn = pciio_device_info_register
- (pcibr_vhdl, &pcibr_info->f_c);
- /* Remember the no slot connection point info for tearing it
- * down during detach.
- */
- pcibr_soft->bs_noslot_conn = noslot_conn;
- pcibr_soft->bs_noslot_info = pcibr_info;
- #if PCI_FBBE
- fast_back_to_back_enable = 1;
- #endif
- #if PCI_FBBE
- if (fast_back_to_back_enable) {
- /*
- * All devices on the bus are capable of fast back to back, so
- * we need to set the fast back to back bit in all devices on
- * the bus that are capable of doing such accesses.
- */
- }
- #endif
- #ifdef LATER
- /* If the bridge has been reset then there is no need to reset
- * the individual PCI slots.
- */
- for (slot = 0; slot < 8; ++slot)
- /* Reset all the slots */
- (void)pcibr_slot_reset(pcibr_vhdl, slot);
- #endif
- for (slot = 0; slot < 8; ++slot)
- /* Find out what is out there */
- (void)pcibr_slot_info_init(pcibr_vhdl,slot);
- for (slot = 0; slot < 8; ++slot)
- /* Set up the address space for this slot in the pci land */
- (void)pcibr_slot_addr_space_init(pcibr_vhdl,slot);
- for (slot = 0; slot < 8; ++slot)
- /* Setup the device register */
- (void)pcibr_slot_device_init(pcibr_vhdl, slot);
- #ifndef __ia64
- for (slot = 0; slot < 8; ++slot)
- /* Set up convenience links */
- if (is_xbridge(bridge))
- if (pcibr_soft->bs_slot[slot].bss_ninfo > 0) /* if occupied */
- pcibr_bus_cnvlink(pcibr_info->f_vertex, slot);
- #endif
- for (slot = 0; slot < 8; ++slot)
- /* Setup host/guest relations */
- (void)pcibr_slot_guest_info_init(pcibr_vhdl,slot);
- for (slot = 0; slot < 8; ++slot)
- /* Initial RRB management */
- (void)pcibr_slot_initial_rrb_alloc(pcibr_vhdl,slot);
- /* driver attach routines should be called out from generic linux code */
- for (slot = 0; slot < 8; ++slot)
- /* Call the device attach */
- (void)pcibr_slot_call_device_attach(pcibr_vhdl, slot, 0);
- /*
- * Each Pbrick PCI bus only has slots 1 and 2. Similarly for
- * widget 0xe on Ibricks. Allocate RRB's accordingly.
- */
- if (pcibr_soft->bs_moduleid > 0) {
- switch (MODULE_GET_BTCHAR(pcibr_soft->bs_moduleid)) {
- case 'p': /* Pbrick */
- do_pcibr_rrb_autoalloc(pcibr_soft, 1, 8);
- do_pcibr_rrb_autoalloc(pcibr_soft, 2, 8);
- break;
- case 'i': /* Ibrick */
- /* port 0xe on the Ibrick only has slots 1 and 2 */
- if (pcibr_soft->bs_xid == 0xe) {
- do_pcibr_rrb_autoalloc(pcibr_soft, 1, 8);
- do_pcibr_rrb_autoalloc(pcibr_soft, 2, 8);
- }
- else {
- /* allocate one RRB for the serial port */
- do_pcibr_rrb_autoalloc(pcibr_soft, 0, 1);
- }
- break;
- } /* switch */
- }
- #ifdef LATER
- if (strstr(nicinfo, XTALK_PCI_PART_NUM)) {
- do_pcibr_rrb_autoalloc(pcibr_soft, 1, 8);
- #if PCIBR_RRB_DEBUG
- printf("nnFound XTALK_PCI (030-1275) at %vn", xconn_vhdl);
- printf("pcibr_attach: %v Shoebox RRB MANAGEMENT: %d+%d freen",
- pcibr_vhdl,
- pcibr_soft->bs_rrb_avail[0],
- pcibr_soft->bs_rrb_avail[1]);
- for (slot = 0; slot < 8; ++slot)
- printf("t%d+%d+%d",
- 0xFFF & pcibr_soft->bs_rrb_valid[slot],
- 0xFFF & pcibr_soft->bs_rrb_valid[slot + PCIBR_RRB_SLOT_VIRTUAL],
- pcibr_soft->bs_rrb_res[slot]);
- printf("n");
- #endif
- }
- #else
- FIXME("pcibr_attach: Call do_pcibr_rrb_autoalloc nicinfon");
- #endif
- if (aa)
- async_attach_add_info(noslot_conn, aa);
- pciio_device_attach(noslot_conn, 0);
- /*
- * Tear down pointer to async attach info -- async threads for
- * bridge's descendants may be running but the bridge's work is done.
- */
- if (aa)
- async_attach_del_info(xconn_vhdl);
- return 0;
- }
- /*
- * pcibr_detach:
- * Detach the bridge device from the hwgraph after cleaning out all the
- * underlying vertices.
- */
- int
- pcibr_detach(devfs_handle_t xconn)
- {
- pciio_slot_t slot;
- devfs_handle_t pcibr_vhdl;
- pcibr_soft_t pcibr_soft;
- bridge_t *bridge;
- /* Get the bridge vertex from its xtalk connection point */
- if (hwgraph_traverse(xconn, EDGE_LBL_PCI, &pcibr_vhdl) != GRAPH_SUCCESS)
- return(1);
- pcibr_soft = pcibr_soft_get(pcibr_vhdl);
- bridge = pcibr_soft->bs_base;
- /* Disable the interrupts from the bridge */
- bridge->b_int_enable = 0;
- /* Detach all the PCI devices talking to this bridge */
- for(slot = 0; slot < 8; slot++) {
- #ifdef DEBUG
- printk("pcibr_device_detach called for %p/%dn",
- pcibr_vhdl,slot);
- #endif
- pcibr_slot_detach(pcibr_vhdl, slot, 0);
- }
- /* Unregister the no-slot connection point */
- pciio_device_info_unregister(pcibr_vhdl,
- &(pcibr_soft->bs_noslot_info->f_c));
- spin_lock_destroy(&pcibr_soft->bs_lock);
- kfree(pcibr_soft->bs_name);
-
- /* Error handler gets unregistered when the widget info is
- * cleaned
- */
- /* Free the soft ATE maps */
- if (pcibr_soft->bs_int_ate_map)
- rmfreemap(pcibr_soft->bs_int_ate_map);
- if (pcibr_soft->bs_ext_ate_map)
- rmfreemap(pcibr_soft->bs_ext_ate_map);
- /* Disconnect the error interrupt and free the xtalk resources
- * associated with it.
- */
- xtalk_intr_disconnect(pcibr_soft->bsi_err_intr);
- xtalk_intr_free(pcibr_soft->bsi_err_intr);
- /* Clear the software state maintained by the bridge driver for this
- * bridge.
- */
- DEL(pcibr_soft);
- /* Remove the Bridge revision labelled info */
- (void)hwgraph_info_remove_LBL(pcibr_vhdl, INFO_LBL_PCIBR_ASIC_REV, NULL);
- /* Remove the character device associated with this bridge */
- (void)hwgraph_edge_remove(pcibr_vhdl, EDGE_LBL_CONTROLLER, NULL);
- /* Remove the PCI bridge vertex */
- (void)hwgraph_edge_remove(xconn, EDGE_LBL_PCI, NULL);
- return(0);
- }
- int
- pcibr_asic_rev(devfs_handle_t pconn_vhdl)
- {
- devfs_handle_t pcibr_vhdl;
- arbitrary_info_t ainfo;
- if (GRAPH_SUCCESS !=
- hwgraph_traverse(pconn_vhdl, EDGE_LBL_MASTER, &pcibr_vhdl))
- return -1;
- if (GRAPH_SUCCESS !=
- hwgraph_info_get_LBL(pcibr_vhdl, INFO_LBL_PCIBR_ASIC_REV, &ainfo))
- return -1;
- return (int) ainfo;
- }
- int
- pcibr_write_gather_flush(devfs_handle_t pconn_vhdl)
- {
- pciio_info_t pciio_info = pciio_info_get(pconn_vhdl);
- pcibr_soft_t pcibr_soft = (pcibr_soft_t) pciio_info_mfast_get(pciio_info);
- pciio_slot_t slot;
- slot = pciio_info_slot_get(pciio_info);
- pcibr_device_write_gather_flush(pcibr_soft, slot);
- return 0;
- }
- /* =====================================================================
- * PIO MANAGEMENT
- */
- LOCAL iopaddr_t
- pcibr_addr_pci_to_xio(devfs_handle_t pconn_vhdl,
- pciio_slot_t slot,
- pciio_space_t space,
- iopaddr_t pci_addr,
- size_t req_size,
- unsigned flags)
- {
- pcibr_info_t pcibr_info = pcibr_info_get(pconn_vhdl);
- pciio_info_t pciio_info = &pcibr_info->f_c;
- pcibr_soft_t pcibr_soft = (pcibr_soft_t) pciio_info_mfast_get(pciio_info);
- bridge_t *bridge = pcibr_soft->bs_base;
- unsigned bar; /* which BASE reg on device is decoding */
- iopaddr_t xio_addr = XIO_NOWHERE;
- pciio_space_t wspace; /* which space device is decoding */
- iopaddr_t wbase; /* base of device decode on PCI */
- size_t wsize; /* size of device decode on PCI */
- int try; /* DevIO(x) window scanning order control */
- int win; /* which DevIO(x) window is being used */
- pciio_space_t mspace; /* target space for devio(x) register */
- iopaddr_t mbase; /* base of devio(x) mapped area on PCI */
- size_t msize; /* size of devio(x) mapped area on PCI */
- size_t mmask; /* addr bits stored in Device(x) */
- unsigned long s;
- s = pcibr_lock(pcibr_soft);
- if (pcibr_soft->bs_slot[slot].has_host) {
- slot = pcibr_soft->bs_slot[slot].host_slot;
- pcibr_info = pcibr_soft->bs_slot[slot].bss_infos[0];
- }
- if (space == PCIIO_SPACE_NONE)
- goto done;
- if (space == PCIIO_SPACE_CFG) {
- /*
- * Usually, the first mapping
- * established to a PCI device
- * is to its config space.
- *
- * In any case, we definitely
- * do NOT need to worry about
- * PCI BASE registers, and
- * MUST NOT attempt to point
- * the DevIO(x) window at
- * this access ...
- */
- if (((flags & PCIIO_BYTE_STREAM) == 0) &&
- ((pci_addr + req_size) <= BRIDGE_TYPE0_CFG_FUNC_OFF))
- xio_addr = pci_addr + BRIDGE_TYPE0_CFG_DEV(slot);
- goto done;
- }
- if (space == PCIIO_SPACE_ROM) {
- /* PIO to the Expansion Rom.
- * Driver is responsible for
- * enabling and disabling
- * decodes properly.
- */
- wbase = pcibr_info->f_rbase;
- wsize = pcibr_info->f_rsize;
- /*
- * While the driver should know better
- * than to attempt to map more space
- * than the device is decoding, he might
- * do it; better to bail out here.
- */
- if ((pci_addr + req_size) > wsize)
- goto done;
- pci_addr += wbase;
- space = PCIIO_SPACE_MEM;
- }
- /*
- * reduce window mappings to raw
- * space mappings (maybe allocating
- * windows), and try for DevIO(x)
- * usage (setting it if it is available).
- */
- bar = space - PCIIO_SPACE_WIN0;
- if (bar < 6) {
- wspace = pcibr_info->f_window[bar].w_space;
- if (wspace == PCIIO_SPACE_NONE)
- goto done;
- /* get PCI base and size */
- wbase = pcibr_info->f_window[bar].w_base;
- wsize = pcibr_info->f_window[bar].w_size;
- /*
- * While the driver should know better
- * than to attempt to map more space
- * than the device is decoding, he might
- * do it; better to bail out here.
- */
- if ((pci_addr + req_size) > wsize)
- goto done;
- /* shift from window relative to
- * decoded space relative.
- */
- pci_addr += wbase;
- space = wspace;
- } else
- bar = -1;
- /* Scan all the DevIO(x) windows twice looking for one
- * that can satisfy our request. The first time through,
- * only look at assigned windows; the second time, also
- * look at PCIIO_SPACE_NONE windows. Arrange the order
- * so we always look at our own window first.
- *
- * We will not attempt to satisfy a single request
- * by concatinating multiple windows.
- */
- for (try = 0; try < 16; ++try) {
- bridgereg_t devreg;
- unsigned offset;
- win = (try + slot) % 8;
- /* If this DevIO(x) mapping area can provide
- * a mapping to this address, use it.
- */
- msize = (win < 2) ? 0x200000 : 0x100000;
- mmask = -msize;
- if (space != PCIIO_SPACE_IO)
- mmask &= 0x3FFFFFFF;
- offset = pci_addr & (msize - 1);
- /* If this window can't possibly handle that request,
- * go on to the next window.
- */
- if (((pci_addr & (msize - 1)) + req_size) > msize)
- continue;
- devreg = pcibr_soft->bs_slot[win].bss_device;
- /* Is this window "nailed down"?
- * If not, maybe we can use it.
- * (only check this the second time through)
- */
- mspace = pcibr_soft->bs_slot[win].bss_devio.bssd_space;
- if ((try > 7) && (mspace == PCIIO_SPACE_NONE)) {
- /* If this is the primary DevIO(x) window
- * for some other device, skip it.
- */
- if ((win != slot) &&
- (PCIIO_VENDOR_ID_NONE !=
- pcibr_soft->bs_slot[win].bss_vendor_id))
- continue;
- /* It's a free window, and we fit in it.
- * Set up Device(win) to our taste.
- */
- mbase = pci_addr & mmask;
- /* check that we would really get from
- * here to there.
- */
- if ((mbase | offset) != pci_addr)
- continue;
- devreg &= ~BRIDGE_DEV_OFF_MASK;
- if (space != PCIIO_SPACE_IO)
- devreg |= BRIDGE_DEV_DEV_IO_MEM;
- else
- devreg &= ~BRIDGE_DEV_DEV_IO_MEM;
- devreg |= (mbase >> 20) & BRIDGE_DEV_OFF_MASK;
- /* default is WORD_VALUES.
- * if you specify both,
- * operation is undefined.
- */
- if (flags & PCIIO_BYTE_STREAM)
- devreg |= BRIDGE_DEV_DEV_SWAP;
- else
- devreg &= ~BRIDGE_DEV_DEV_SWAP;
- if (pcibr_soft->bs_slot[win].bss_device != devreg) {
- bridge->b_device[win].reg = devreg;
- pcibr_soft->bs_slot[win].bss_device = devreg;
- bridge->b_wid_tflush; /* wait until Bridge PIO complete */
- #if DEBUG && PCI_DEBUG
- printk("pcibr Device(%d): 0x%lxn", win, bridge->b_device[win].reg);
- #endif
- }
- pcibr_soft->bs_slot[win].bss_devio.bssd_space = space;
- pcibr_soft->bs_slot[win].bss_devio.bssd_base = mbase;
- xio_addr = BRIDGE_DEVIO(win) + (pci_addr - mbase);
- #if DEBUG && PCI_DEBUG
- printk("%s LINE %d map to space %d space desc 0x%x[%lx..%lx] for slot %d allocates DevIO(%d) devreg 0x%xn",
- __FUNCTION__, __LINE__, space, space_desc,
- pci_addr, pci_addr + req_size - 1,
- slot, win, devreg);
- #endif
- goto done;
- } /* endif DevIO(x) not pointed */
- mbase = pcibr_soft->bs_slot[win].bss_devio.bssd_base;
- /* Now check for request incompat with DevIO(x)
- */
- if ((mspace != space) ||
- (pci_addr < mbase) ||
- ((pci_addr + req_size) > (mbase + msize)) ||
- ((flags & PCIIO_BYTE_STREAM) && !(devreg & BRIDGE_DEV_DEV_SWAP)) ||
- (!(flags & PCIIO_BYTE_STREAM) && (devreg & BRIDGE_DEV_DEV_SWAP)))
- continue;
- /* DevIO(x) window is pointed at PCI space
- * that includes our target. Calculate the
- * final XIO address, release the lock and
- * return.
- */
- xio_addr = BRIDGE_DEVIO(win) + (pci_addr - mbase);
- #if DEBUG && PCI_DEBUG
- printk("%s LINE %d map to space %d [0x%p..0x%p] for slot %d uses DevIO(%d)n",
- __FUNCTION__, __LINE__, space, pci_addr, pci_addr + req_size - 1, slot, win);
- #endif
- goto done;
- }
- switch (space) {
- /*
- * Accesses to device decode
- * areas that do a not fit
- * within the DevIO(x) space are
- * modified to be accesses via
- * the direct mapping areas.
- *
- * If necessary, drivers can
- * explicitly ask for mappings
- * into these address spaces,
- * but this should never be needed.
- */
- case PCIIO_SPACE_MEM: /* "mem space" */
- case PCIIO_SPACE_MEM32: /* "mem, use 32-bit-wide bus" */
- if ((pci_addr + BRIDGE_PCI_MEM32_BASE + req_size - 1) <=
- BRIDGE_PCI_MEM32_LIMIT)
- xio_addr = pci_addr + BRIDGE_PCI_MEM32_BASE;
- break;
- case PCIIO_SPACE_MEM64: /* "mem, use 64-bit-wide bus" */
- if ((pci_addr + BRIDGE_PCI_MEM64_BASE + req_size - 1) <=
- BRIDGE_PCI_MEM64_LIMIT)
- xio_addr = pci_addr + BRIDGE_PCI_MEM64_BASE;
- break;
- case PCIIO_SPACE_IO: /* "i/o space" */
- /* Bridge Hardware Bug WAR #482741:
- * The 4G area that maps directly from
- * XIO space to PCI I/O space is busted
- * until Bridge Rev D.
- */
- if ((pcibr_soft->bs_rev_num > BRIDGE_PART_REV_C) &&
- ((pci_addr + BRIDGE_PCI_IO_BASE + req_size - 1) <=
- BRIDGE_PCI_IO_LIMIT))
- xio_addr = pci_addr + BRIDGE_PCI_IO_BASE;
- break;
- }
- /* Check that "Direct PIO" byteswapping matches,
- * try to change it if it does not.
- */
- if (xio_addr != XIO_NOWHERE) {
- unsigned bst; /* nonzero to set bytestream */
- unsigned *bfp; /* addr of record of how swapper is set */
- unsigned swb; /* which control bit to mung */
- unsigned bfo; /* current swapper setting */
- unsigned bfn; /* desired swapper setting */
- bfp = ((space == PCIIO_SPACE_IO)
- ? (&pcibr_soft->bs_pio_end_io)
- : (&pcibr_soft->bs_pio_end_mem));
- bfo = *bfp;
- bst = flags & PCIIO_BYTE_STREAM;
- bfn = bst ? PCIIO_BYTE_STREAM : PCIIO_WORD_VALUES;
- if (bfn == bfo) { /* we already match. */
- ;
- } else if (bfo != 0) { /* we have a conflict. */
- #if DEBUG && PCI_DEBUG
- printk("pcibr_addr_pci_to_xio: swap conflict in space %d , was%s%s, want%s%sn",
- space,
- bfo & PCIIO_BYTE_STREAM ? " BYTE_STREAM" : "",
- bfo & PCIIO_WORD_VALUES ? " WORD_VALUES" : "",
- bfn & PCIIO_BYTE_STREAM ? " BYTE_STREAM" : "",
- bfn & PCIIO_WORD_VALUES ? " WORD_VALUES" : "");
- #endif
- xio_addr = XIO_NOWHERE;
- } else { /* OK to make the change. */
- bridgereg_t octl, nctl;
- swb = (space == PCIIO_SPACE_IO) ? BRIDGE_CTRL_IO_SWAP : BRIDGE_CTRL_MEM_SWAP;
- octl = bridge->b_wid_control;
- nctl = bst ? octl | swb : octl & ~swb;
- if (octl != nctl) /* make the change if any */
- bridge->b_wid_control = nctl;
- *bfp = bfn; /* record the assignment */
- #if DEBUG && PCI_DEBUG
- printk("pcibr_addr_pci_to_xio: swap for space %d set to%s%sn",
- space,
- bfn & PCIIO_BYTE_STREAM ? " BYTE_STREAM" : "",
- bfn & PCIIO_WORD_VALUES ? " WORD_VALUES" : "");
- #endif
- }
- }
- done:
- pcibr_unlock(pcibr_soft, s);
- return xio_addr;
- }
- /*ARGSUSED6 */
- pcibr_piomap_t
- pcibr_piomap_alloc(devfs_handle_t pconn_vhdl,
- device_desc_t dev_desc,
- pciio_space_t space,
- iopaddr_t pci_addr,
- size_t req_size,
- size_t req_size_max,
- unsigned flags)
- {
- pcibr_info_t pcibr_info = pcibr_info_get(pconn_vhdl);
- pciio_info_t pciio_info = &pcibr_info->f_c;
- pciio_slot_t pciio_slot = pciio_info_slot_get(pciio_info);
- pcibr_soft_t pcibr_soft = (pcibr_soft_t) pciio_info_mfast_get(pciio_info);
- devfs_handle_t xconn_vhdl = pcibr_soft->bs_conn;
- pcibr_piomap_t *mapptr;
- pcibr_piomap_t maplist;
- pcibr_piomap_t pcibr_piomap;
- iopaddr_t xio_addr;
- xtalk_piomap_t xtalk_piomap;
- unsigned long s;
- /* Make sure that the req sizes are non-zero */
- if ((req_size < 1) || (req_size_max < 1))
- return NULL;
- /*
- * Code to translate slot/space/addr
- * into xio_addr is common between
- * this routine and pcibr_piotrans_addr.
- */
- xio_addr = pcibr_addr_pci_to_xio(pconn_vhdl, pciio_slot, space, pci_addr, req_size, flags);
- if (xio_addr == XIO_NOWHERE)
- return NULL;
- /* Check the piomap list to see if there is already an allocated
- * piomap entry but not in use. If so use that one. Otherwise
- * allocate a new piomap entry and add it to the piomap list
- */
- mapptr = &(pcibr_info->f_piomap);
- s = pcibr_lock(pcibr_soft);
- for (pcibr_piomap = *mapptr;
- pcibr_piomap != NULL;
- pcibr_piomap = pcibr_piomap->bp_next) {
- if (pcibr_piomap->bp_mapsz == 0)
- break;
- }
- if (pcibr_piomap)
- mapptr = NULL;
- else {
- pcibr_unlock(pcibr_soft, s);
- NEW(pcibr_piomap);
- }
- pcibr_piomap->bp_dev = pconn_vhdl;
- pcibr_piomap->bp_slot = pciio_slot;
- pcibr_piomap->bp_flags = flags;
- pcibr_piomap->bp_space = space;
- pcibr_piomap->bp_pciaddr = pci_addr;
- pcibr_piomap->bp_mapsz = req_size;
- pcibr_piomap->bp_soft = pcibr_soft;
- pcibr_piomap->bp_toc[0] = ATOMIC_INIT(0);
- if (mapptr) {
- s = pcibr_lock(pcibr_soft);
- maplist = *mapptr;
- pcibr_piomap->bp_next = maplist;
- *mapptr = pcibr_piomap;
- }
- pcibr_unlock(pcibr_soft, s);
- if (pcibr_piomap) {
- xtalk_piomap =
- xtalk_piomap_alloc(xconn_vhdl, 0,
- xio_addr,
- req_size, req_size_max,
- flags & PIOMAP_FLAGS);
- if (xtalk_piomap) {
- pcibr_piomap->bp_xtalk_addr = xio_addr;
- pcibr_piomap->bp_xtalk_pio = xtalk_piomap;
- } else {
- pcibr_piomap->bp_mapsz = 0;
- pcibr_piomap = 0;
- }
- }
- return pcibr_piomap;
- }
- /*ARGSUSED */
- void
- pcibr_piomap_free(pcibr_piomap_t pcibr_piomap)
- {
- xtalk_piomap_free(pcibr_piomap->bp_xtalk_pio);
- pcibr_piomap->bp_xtalk_pio = 0;
- pcibr_piomap->bp_mapsz = 0;
- }
- /*ARGSUSED */
- caddr_t
- pcibr_piomap_addr(pcibr_piomap_t pcibr_piomap,
- iopaddr_t pci_addr,
- size_t req_size)
- {
- return xtalk_piomap_addr(pcibr_piomap->bp_xtalk_pio,
- pcibr_piomap->bp_xtalk_addr +
- pci_addr - pcibr_piomap->bp_pciaddr,
- req_size);
- }
- /*ARGSUSED */
- void
- pcibr_piomap_done(pcibr_piomap_t pcibr_piomap)
- {
- xtalk_piomap_done(pcibr_piomap->bp_xtalk_pio);
- }
- /*ARGSUSED */
- caddr_t
- pcibr_piotrans_addr(devfs_handle_t pconn_vhdl,
- device_desc_t dev_desc,
- pciio_space_t space,
- iopaddr_t pci_addr,
- size_t req_size,
- unsigned flags)
- {
- pciio_info_t pciio_info = pciio_info_get(pconn_vhdl);
- pciio_slot_t pciio_slot = pciio_info_slot_get(pciio_info);
- pcibr_soft_t pcibr_soft = (pcibr_soft_t) pciio_info_mfast_get(pciio_info);
- devfs_handle_t xconn_vhdl = pcibr_soft->bs_conn;
- iopaddr_t xio_addr;
- xio_addr = pcibr_addr_pci_to_xio(pconn_vhdl, pciio_slot, space, pci_addr, req_size, flags);
- if (xio_addr == XIO_NOWHERE)
- return NULL;
- return xtalk_piotrans_addr(xconn_vhdl, 0, xio_addr, req_size, flags & PIOMAP_FLAGS);
- }
- /*
- * PIO Space allocation and management.
- * Allocate and Manage the PCI PIO space (mem and io space)
- * This routine is pretty simplistic at this time, and
- * does pretty trivial management of allocation and freeing..
- * The current scheme is prone for fragmentation..
- * Change the scheme to use bitmaps.
- */
- /*ARGSUSED */
- iopaddr_t
- pcibr_piospace_alloc(devfs_handle_t pconn_vhdl,
- device_desc_t dev_desc,
- pciio_space_t space,
- size_t req_size,
- size_t alignment)
- {
- pcibr_info_t pcibr_info = pcibr_info_get(pconn_vhdl);
- pciio_info_t pciio_info = &pcibr_info->f_c;
- pcibr_soft_t pcibr_soft = (pcibr_soft_t) pciio_info_mfast_get(pciio_info);
- pciio_piospace_t piosp;
- unsigned long s;
- iopaddr_t *pciaddr, *pcilast;
- iopaddr_t start_addr;
- size_t align_mask;
- /*
- * Check for proper alignment
- */
- ASSERT(alignment >= NBPP);
- ASSERT((alignment & (alignment - 1)) == 0);
- align_mask = alignment - 1;
- s = pcibr_lock(pcibr_soft);
- /*
- * First look if a previously allocated chunk exists.
- */
- if ((piosp = pcibr_info->f_piospace)) {
- /*
- * Look through the list for a right sized free chunk.
- */
- do {
- if (piosp->free &&
- (piosp->space == space) &&
- (piosp->count >= req_size) &&
- !(piosp->start & align_mask)) {
- piosp->free = 0;
- pcibr_unlock(pcibr_soft, s);
- return piosp->start;
- }
- piosp = piosp->next;
- } while (piosp);
- }
- ASSERT(!piosp);
- switch (space) {
- case PCIIO_SPACE_IO:
- pciaddr = &pcibr_soft->bs_spinfo.pci_io_base;
- pcilast = &pcibr_soft->bs_spinfo.pci_io_last;
- break;
- case PCIIO_SPACE_MEM:
- case PCIIO_SPACE_MEM32:
- pciaddr = &pcibr_soft->bs_spinfo.pci_mem_base;
- pcilast = &pcibr_soft->bs_spinfo.pci_mem_last;
- break;
- default:
- ASSERT(0);
- pcibr_unlock(pcibr_soft, s);
- return 0;
- }
- start_addr = *pciaddr;
- /*
- * Align start_addr.
- */
- if (start_addr & align_mask)
- start_addr = (start_addr + align_mask) & ~align_mask;
- if ((start_addr + req_size) > *pcilast) {
- /*
- * If too big a request, reject it.
- */
- pcibr_unlock(pcibr_soft, s);
- return 0;
- }
- *pciaddr = (start_addr + req_size);
- NEW(piosp);
- piosp->free = 0;
- piosp->space = space;
- piosp->start = start_addr;
- piosp->count = req_size;
- piosp->next = pcibr_info->f_piospace;
- pcibr_info->f_piospace = piosp;
- pcibr_unlock(pcibr_soft, s);
- return start_addr;
- }
- /*ARGSUSED */
- void
- pcibr_piospace_free(devfs_handle_t pconn_vhdl,
- pciio_space_t space,
- iopaddr_t pciaddr,
- size_t req_size)
- {
- pcibr_info_t pcibr_info = pcibr_info_get(pconn_vhdl);
- pcibr_soft_t pcibr_soft = (pcibr_soft_t) pcibr_info->f_mfast;
- pciio_piospace_t piosp;
- unsigned long s;
- char name[1024];
- /*
- * Look through the bridge data structures for the pciio_piospace_t
- * structure corresponding to 'pciaddr'
- */
- s = pcibr_lock(pcibr_soft);
- piosp = pcibr_info->f_piospace;
- while (piosp) {
- /*
- * Piospace free can only be for the complete
- * chunk and not parts of it..
- */
- if (piosp->start == pciaddr) {
- if (piosp->count == req_size)
- break;
- /*
- * Improper size passed for freeing..
- * Print a message and break;
- */
- hwgraph_vertex_name_get(pconn_vhdl, name, 1024);
- printk(KERN_WARNING "pcibr_piospace_free: error");
- printk(KERN_WARNING "Device %s freeing size (0x%lx) different than allocated (0x%lx)",
- name, req_size, piosp->count);
- printk(KERN_WARNING "Freeing 0x%lx instead", piosp->count);
- break;
- }
- piosp = piosp->next;
- }
- if (!piosp) {
- printk(KERN_WARNING
- "pcibr_piospace_free: Address 0x%lx size 0x%lx - No matchn",
- pciaddr, req_size);
- pcibr_unlock(pcibr_soft, s);
- return;
- }
- piosp->free = 1;
- pcibr_unlock(pcibr_soft, s);
- return;
- }
- /* =====================================================================
- * 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 32-bit
- * 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.
- */
- /*
- * Convert PCI-generic software flags and Bridge-specific software flags
- * into Bridge-specific Direct Map attribute bits.
- */
- LOCAL iopaddr_t
- pcibr_flags_to_d64(unsigned flags, pcibr_soft_t pcibr_soft)
- {
- iopaddr_t attributes = 0;
- /* Sanity check: Bridge only allows use of VCHAN1 via 64-bit addrs */
- #ifdef LATER
- ASSERT_ALWAYS(!(flags & PCIBR_VCHAN1) || (flags & PCIIO_DMA_A64));
- #endif
- /* Generic macro flags
- */
- if (flags & PCIIO_DMA_DATA) { /* standard data channel */
- attributes &= ~PCI64_ATTR_BAR; /* no barrier bit */
- attributes |= PCI64_ATTR_PREF; /* prefetch on */
- }
- if (flags & PCIIO_DMA_CMD) { /* standard command channel */
- attributes |= PCI64_ATTR_BAR; /* barrier bit on */
- attributes &= ~PCI64_ATTR_PREF; /* disable prefetch */
- }
- /* Generic detail flags
- */
- if (flags & PCIIO_PREFETCH)
- attributes |= PCI64_ATTR_PREF;
- if (flags & PCIIO_NOPREFETCH)
- attributes &= ~PCI64_ATTR_PREF;
- /* the swap bit is in the address attributes for xbridge */
- if (pcibr_soft->bs_xbridge) {
- if (flags & PCIIO_BYTE_STREAM)
- attributes |= PCI64_ATTR_SWAP;
- if (flags & PCIIO_WORD_VALUES)
- attributes &= ~PCI64_ATTR_SWAP;
- }
- /* Provider-specific flags
- */
- if (flags & PCIBR_BARRIER)
- attributes |= PCI64_ATTR_BAR;
- if (flags & PCIBR_NOBARRIER)
- attributes &= ~PCI64_ATTR_BAR;
- if (flags & PCIBR_PREFETCH)
- attributes |= PCI64_ATTR_PREF;
- if (flags & PCIBR_NOPREFETCH)
- attributes &= ~PCI64_ATTR_PREF;
- if (flags & PCIBR_PRECISE)
- attributes |= PCI64_ATTR_PREC;
- if (flags & PCIBR_NOPRECISE)
- attributes &= ~PCI64_ATTR_PREC;
- if (flags & PCIBR_VCHAN1)
- attributes |= PCI64_ATTR_VIRTUAL;
- if (flags & PCIBR_VCHAN0)
- attributes &= ~PCI64_ATTR_VIRTUAL;
- return (attributes);
- }
- /*
- * Convert PCI-generic software flags and Bridge-specific software flags
- * into Bridge-specific Address Translation Entry attribute bits.
- */
- LOCAL bridge_ate_t
- pcibr_flags_to_ate(unsigned flags)
- {
- bridge_ate_t attributes;
- /* default if nothing specified:
- * NOBARRIER
- * NOPREFETCH
- * NOPRECISE
- * COHERENT
- * Plus the valid bit
- */
- attributes = ATE_CO | ATE_V;
- /* Generic macro flags
- */
- if (flags & PCIIO_DMA_DATA) { /* standard data channel */
- attributes &= ~ATE_BAR; /* no barrier */
- attributes |= ATE_PREF; /* prefetch on */
- }
- if (flags & PCIIO_DMA_CMD) { /* standard command channel */
- attributes |= ATE_BAR; /* barrier bit on */
- attributes &= ~ATE_PREF; /* disable prefetch */
- }
- /* Generic detail flags
- */
- if (flags & PCIIO_PREFETCH)
- attributes |= ATE_PREF;
- if (flags & PCIIO_NOPREFETCH)
- attributes &= ~ATE_PREF;
- /* Provider-specific flags
- */
- if (flags & PCIBR_BARRIER)
- attributes |= ATE_BAR;
- if (flags & PCIBR_NOBARRIER)
- attributes &= ~ATE_BAR;
- if (flags & PCIBR_PREFETCH)
- attributes |= ATE_PREF;
- if (flags & PCIBR_NOPREFETCH)
- attributes &= ~ATE_PREF;
- if (flags & PCIBR_PRECISE)
- attributes |= ATE_PREC;
- if (flags & PCIBR_NOPRECISE)
- attributes &= ~ATE_PREC;
- return (attributes);
- }
- /*ARGSUSED */
- pcibr_dmamap_t
- pcibr_dmamap_alloc(devfs_handle_t pconn_vhdl,
- device_desc_t dev_desc,
- size_t req_size_max,
- unsigned flags)
- {
- pciio_info_t pciio_info = pciio_info_get(pconn_vhdl);
- pcibr_soft_t pcibr_soft = (pcibr_soft_t) pciio_info_mfast_get(pciio_info);
- devfs_handle_t xconn_vhdl = pcibr_soft->bs_conn;
- pciio_slot_t slot;
- xwidgetnum_t xio_port;
- xtalk_dmamap_t xtalk_dmamap;
- pcibr_dmamap_t pcibr_dmamap;
- int ate_count;
- int ate_index;
- /* merge in forced flags */
- flags |= pcibr_soft->bs_dma_flags;
- #ifdef IRIX
- NEWf(pcibr_dmamap, flags);
- #else
- /*
- * On SNIA64, these maps are pre-allocated because pcibr_dmamap_alloc()
- * can be called within an interrupt thread.
- */
- pcibr_dmamap = (pcibr_dmamap_t)get_free_pciio_dmamap(pcibr_soft->bs_vhdl);
- #endif
- if (!pcibr_dmamap)
- return 0;
- xtalk_dmamap = xtalk_dmamap_alloc(xconn_vhdl, dev_desc, req_size_max,
- flags & DMAMAP_FLAGS);
- if (!xtalk_dmamap) {
- #if PCIBR_ATE_DEBUG
- printk("pcibr_attach: xtalk_dmamap_alloc failedn");
- #endif
- #ifdef IRIX
- DEL(pcibr_dmamap);
- #else
- free_pciio_dmamap(pcibr_dmamap);
- #endif
- return 0;
- }
- xio_port = pcibr_soft->bs_mxid;
- slot = pciio_info_slot_get(pciio_info);
- pcibr_dmamap->bd_dev = pconn_vhdl;
- pcibr_dmamap->bd_slot = slot;
- pcibr_dmamap->bd_soft = pcibr_soft;
- pcibr_dmamap->bd_xtalk = xtalk_dmamap;
- pcibr_dmamap->bd_max_size = req_size_max;
- pcibr_dmamap->bd_xio_port = xio_port;
- if (flags & PCIIO_DMA_A64) {
- if (!pcibr_try_set_device(pcibr_soft, slot, flags, BRIDGE_DEV_D64_BITS)) {
- iopaddr_t pci_addr;
- int have_rrbs;
- int min_rrbs;
- /* Device is capable of A64 operations,
- * and the attributes of the DMA are
- * consistant with any previous DMA
- * mappings using shared resources.
- */
- pci_addr = pcibr_flags_to_d64(flags, pcibr_soft);
- pcibr_dmamap->bd_flags = flags;
- pcibr_dmamap->bd_xio_addr = 0;
- pcibr_dmamap->bd_pci_addr = pci_addr;
- /* Make sure we have an RRB (or two).
- */
- if (!(pcibr_soft->bs_rrb_fixed & (1 << slot))) {
- if (flags & PCIBR_VCHAN1)
- slot += PCIBR_RRB_SLOT_VIRTUAL;
- have_rrbs = pcibr_soft->bs_rrb_valid[slot];
- if (have_rrbs < 2) {
- if (pci_addr & PCI64_ATTR_PREF)
- min_rrbs = 2;
- else
- min_rrbs = 1;
- if (have_rrbs < min_rrbs)
- do_pcibr_rrb_autoalloc(pcibr_soft, slot, min_rrbs - have_rrbs);
- }
- }
- #if PCIBR_ATE_DEBUG
- printk("pcibr_dmamap_alloc: using direct64n");
- #endif
- return pcibr_dmamap;
- }
- #if PCIBR_ATE_DEBUG
- printk("pcibr_dmamap_alloc: unable to use direct64n");
- #endif
- flags &= ~PCIIO_DMA_A64;
- }
- if (flags & PCIIO_FIXED) {
- /* warning: mappings may fail later,
- * if direct32 can't get to the address.
- */
- if (!pcibr_try_set_device(pcibr_soft, slot, flags, BRIDGE_DEV_D32_BITS)) {
- /* User desires DIRECT A32 operations,
- * and the attributes of the DMA are
- * consistant with any previous DMA
- * mappings using shared resources.
- * Mapping calls may fail if target
- * is outside the direct32 range.
- */
- #if PCIBR_ATE_DEBUG
- printk("pcibr_dmamap_alloc: using direct32n");
- #endif
- pcibr_dmamap->bd_flags = flags;
- pcibr_dmamap->bd_xio_addr = pcibr_soft->bs_dir_xbase;
- pcibr_dmamap->bd_pci_addr = PCI32_DIRECT_BASE;
- return pcibr_dmamap;
- }
- #if PCIBR_ATE_DEBUG
- printk("pcibr_dmamap_alloc: unable to use direct32n");
- #endif
- /* If the user demands FIXED and we can't
- * give it to him, fail.
- */
- xtalk_dmamap_free(xtalk_dmamap);
- #ifdef IRIX
- DEL(pcibr_dmamap);
- #else
- free_pciio_dmamap(pcibr_dmamap);
- #endif
- return 0;
- }
- /*
- * Allocate Address Translation Entries from the mapping RAM.
- * Unless the PCIBR_NO_ATE_ROUNDUP flag is specified,
- * the maximum number of ATEs is based on the worst-case
- * scenario, where the requested target is in the
- * last byte of an ATE; thus, mapping IOPGSIZE+2
- * does end up requiring three ATEs.
- */
- if (!(flags & PCIBR_NO_ATE_ROUNDUP)) {
- ate_count = IOPG((IOPGSIZE - 1) /* worst case start offset */
- +req_size_max /* max mapping bytes */
- - 1) + 1; /* round UP */
- } else { /* assume requested target is page aligned */
- ate_count = IOPG(req_size_max /* max mapping bytes */
- - 1) + 1; /* round UP */
- }
- ate_index = pcibr_ate_alloc(pcibr_soft, ate_count);
- if (ate_index != -1) {
- if (!pcibr_try_set_device(pcibr_soft, slot, flags, BRIDGE_DEV_PMU_BITS)) {
- bridge_ate_t ate_proto;
- int have_rrbs;
- int min_rrbs;
- #if PCIBR_ATE_DEBUG
- printk("pcibr_dmamap_alloc: using PMUn");
- #endif
- ate_proto = pcibr_flags_to_ate(flags);
- pcibr_dmamap->bd_flags = flags;
- pcibr_dmamap->bd_pci_addr =
- PCI32_MAPPED_BASE + IOPGSIZE * ate_index;
- /*
- * for xbridge the byte-swap bit == bit 29 of PCI address
- */
- if (pcibr_soft->bs_xbridge) {
- if (flags & PCIIO_BYTE_STREAM)
- ATE_SWAP_ON(pcibr_dmamap->bd_pci_addr);
- /*
- * If swap was set in bss_device in pcibr_endian_set()
- * we need to change the address bit.
- */
- if (pcibr_soft->bs_slot[slot].bss_device &
- BRIDGE_DEV_SWAP_PMU)
- ATE_SWAP_ON(pcibr_dmamap->bd_pci_addr);
- if (flags & PCIIO_WORD_VALUES)
- ATE_SWAP_OFF(pcibr_dmamap->bd_pci_addr);
- }
- pcibr_dmamap->bd_xio_addr = 0;
- pcibr_dmamap->bd_ate_ptr = pcibr_ate_addr(pcibr_soft, ate_index);
- pcibr_dmamap->bd_ate_index = ate_index;
- pcibr_dmamap->bd_ate_count = ate_count;
- pcibr_dmamap->bd_ate_proto = ate_proto;
- /* Make sure we have an RRB (or two).
- */
- if (!(pcibr_soft->bs_rrb_fixed & (1 << slot))) {
- have_rrbs = pcibr_soft->bs_rrb_valid[slot];
- if (have_rrbs < 2) {
- if (ate_proto & ATE_PREF)
- min_rrbs = 2;
- else
- min_rrbs = 1;
- if (have_rrbs < min_rrbs)
- do_pcibr_rrb_autoalloc(pcibr_soft, slot, min_rrbs - have_rrbs);
- }
- }
- if (ate_index >= pcibr_soft->bs_int_ate_size &&
- !pcibr_soft->bs_xbridge) {
- bridge_t *bridge = pcibr_soft->bs_base;
- volatile unsigned *cmd_regp;
- unsigned cmd_reg;
- unsigned long s;
- pcibr_dmamap->bd_flags |= PCIBR_DMAMAP_SSRAM;
- s = pcibr_lock(pcibr_soft);
- cmd_regp = &(bridge->
- b_type0_cfg_dev[slot].
- l[PCI_CFG_COMMAND / 4]);
- cmd_reg = *cmd_regp;
- pcibr_soft->bs_slot[slot].bss_cmd_pointer = cmd_regp;
- pcibr_soft->bs_slot[slot].bss_cmd_shadow = cmd_reg;
- pcibr_unlock(pcibr_soft, s);
- }
- return pcibr_dmamap;
- }
- #if PCIBR_ATE_DEBUG
- printk("pcibr_dmamap_alloc: unable to use PMUn");
- #endif
- pcibr_ate_free(pcibr_soft, ate_index, ate_count);
- }
- /* total failure: sorry, you just can't
- * get from here to there that way.
- */
- #if PCIBR_ATE_DEBUG
- printk("pcibr_dmamap_alloc: complete failure.n");
- #endif
- xtalk_dmamap_free(xtalk_dmamap);
- #ifdef IRIX
- DEL(pcibr_dmamap);
- #else
- free_pciio_dmamap(pcibr_dmamap);
- #endif
- return 0;
- }
- /*ARGSUSED */
- void
- pcibr_dmamap_free(pcibr_dmamap_t pcibr_dmamap)
- {
- pcibr_soft_t pcibr_soft = pcibr_dmamap->bd_soft;
- pciio_slot_t slot = pcibr_dmamap->bd_slot;
- unsigned flags = pcibr_dmamap->bd_flags;
- /* Make sure that bss_ext_ates_active
- * is properly kept up to date.
- */
- if (PCIBR_DMAMAP_BUSY & flags)
- if (PCIBR_DMAMAP_SSRAM & flags)
- atomic_dec(&(pcibr_soft->bs_slot[slot]. bss_ext_ates_active));
- xtalk_dmamap_free(pcibr_dmamap->bd_xtalk);
- if (pcibr_dmamap->bd_flags & PCIIO_DMA_A64) {
- pcibr_release_device(pcibr_soft, slot, BRIDGE_DEV_D64_BITS);
- }
- if (pcibr_dmamap->bd_ate_count) {
- pcibr_ate_free(pcibr_dmamap->bd_soft,
- pcibr_dmamap->bd_ate_index,
- pcibr_dmamap->bd_ate_count);
- pcibr_release_device(pcibr_soft, slot, BRIDGE_DEV_PMU_BITS);
- }
- #ifdef IRIX
- DEL(pcibr_dmamap);
- #else
- free_pciio_dmamap(pcibr_dmamap);
- #endif
- }
- /*
- * Setup an Address Translation Entry as specified. Use either the Bridge
- * internal maps or the external map RAM, as appropriate.
- */
- LOCAL bridge_ate_p
- pcibr_ate_addr(pcibr_soft_t pcibr_soft,
- int ate_index)
- {
- bridge_t *bridge = pcibr_soft->bs_base;
- return (ate_index < pcibr_soft->bs_int_ate_size)
- ? &(bridge->b_int_ate_ram[ate_index].wr)
- : &(bridge->b_ext_ate_ram[ate_index]);
- }
- /*
- * pcibr_addr_xio_to_pci: given a PIO range, hand
- * back the corresponding base PCI MEM address;
- * this is used to short-circuit DMA requests that
- * loop back onto this PCI bus.
- */
- LOCAL iopaddr_t
- pcibr_addr_xio_to_pci(pcibr_soft_t soft,
- iopaddr_t xio_addr,
- size_t req_size)
- {
- iopaddr_t xio_lim = xio_addr + req_size - 1;
- iopaddr_t pci_addr;
- pciio_slot_t slot;
- if ((xio_addr >= BRIDGE_PCI_MEM32_BASE) &&
- (xio_lim <= BRIDGE_PCI_MEM32_LIMIT)) {
- pci_addr = xio_addr - BRIDGE_PCI_MEM32_BASE;
- return pci_addr;
- }
- if ((xio_addr >= BRIDGE_PCI_MEM64_BASE) &&
- (xio_lim <= BRIDGE_PCI_MEM64_LIMIT)) {
- pci_addr = xio_addr - BRIDGE_PCI_MEM64_BASE;
- return pci_addr;
- }
- for (slot = 0; slot < 8; ++slot)
- if ((xio_addr >= BRIDGE_DEVIO(slot)) &&
- (xio_lim < BRIDGE_DEVIO(slot + 1))) {
- bridgereg_t dev;
- dev = soft->bs_slot[slot].bss_device;
- pci_addr = dev & BRIDGE_DEV_OFF_MASK;
- pci_addr <<= BRIDGE_DEV_OFF_ADDR_SHFT;
- pci_addr += xio_addr - BRIDGE_DEVIO(slot);
- return (dev & BRIDGE_DEV_DEV_IO_MEM) ? pci_addr : PCI_NOWHERE;
- }
- return 0;
- }
- /* We are starting to get more complexity
- * surrounding writing ATEs, so pull
- * the writing code into this new function.
- */
- #if PCIBR_FREEZE_TIME
- #define ATE_FREEZE() s = ate_freeze(pcibr_dmamap, &freeze_time, cmd_regs)
- #else
- #define ATE_FREEZE() s = ate_freeze(pcibr_dmamap, cmd_regs)
- #endif
- LOCAL unsigned
- ate_freeze(pcibr_dmamap_t pcibr_dmamap,
- #if PCIBR_FREEZE_TIME
- unsigned *freeze_time_ptr,
- #endif
- unsigned *cmd_regs)
- {
- pcibr_soft_t pcibr_soft = pcibr_dmamap->bd_soft;
- #ifdef LATER
- int dma_slot = pcibr_dmamap->bd_slot;
- #endif
- int ext_ates = pcibr_dmamap->bd_flags & PCIBR_DMAMAP_SSRAM;
- int slot;
- unsigned long s;
- unsigned cmd_reg;
- volatile unsigned *cmd_lwa;
- unsigned cmd_lwd;
- if (!ext_ates)
- return 0;
- /* Bridge Hardware Bug WAR #484930:
- * Bridge can't handle updating External ATEs
- * while DMA is occuring that uses External ATEs,
- * even if the particular ATEs involved are disjoint.
- */
- /* need to prevent anyone else from
- * unfreezing the grant while we
- * are working; also need to prevent
- * this thread from being interrupted
- * to keep PCI grant freeze time
- * at an absolute minimum.
- */
- s = pcibr_lock(pcibr_soft);
- #ifdef LATER
- /* just in case pcibr_dmamap_done was not called */
- if (pcibr_dmamap->bd_flags & PCIBR_DMAMAP_BUSY) {
- pcibr_dmamap->bd_flags &= ~PCIBR_DMAMAP_BUSY;
- if (pcibr_dmamap->bd_flags & PCIBR_DMAMAP_SSRAM)
- atomic_dec(&(pcibr_soft->bs_slot[dma_slot]. bss_ext_ates_active));
- xtalk_dmamap_done(pcibr_dmamap->bd_xtalk);
- }
- #endif /* LATER */
- #if PCIBR_FREEZE_TIME
- *freeze_time_ptr = get_timestamp();
- #endif
- cmd_lwa = 0;
- for (slot = 0; slot < 8; ++slot)
- if (atomic_read(&pcibr_soft->bs_slot[slot].bss_ext_ates_active)) {
- cmd_reg = pcibr_soft->
- bs_slot[slot].
- bss_cmd_shadow;
- if (cmd_reg & PCI_CMD_BUS_MASTER) {
- cmd_lwa = pcibr_soft->
- bs_slot[slot].
- bss_cmd_pointer;
- cmd_lwd = cmd_reg ^ PCI_CMD_BUS_MASTER;
- cmd_lwa[0] = cmd_lwd;
- }
- cmd_regs[slot] = cmd_reg;
- } else
- cmd_regs[slot] = 0;
- if (cmd_lwa) {
- bridge_t *bridge = pcibr_soft->bs_base;
- /* Read the last master bit that has been cleared. This PIO read
- * on the PCI bus is to ensure the completion of any DMAs that
- * are due to bus requests issued by PCI devices before the
- * clearing of master bits.
- */
- cmd_lwa[0];
- /* Flush all the write buffers in the bridge */
- for (slot = 0; slot < 8; ++slot)
- if (atomic_read(&pcibr_soft->bs_slot[slot].bss_ext_ates_active)) {
- /* Flush the write buffer associated with this
- * PCI device which might be using dma map RAM.
- */
- bridge->b_wr_req_buf[slot].reg;
- }
- }
- return s;
- }
- #define ATE_WRITE() ate_write(ate_ptr, ate_count, ate)
- LOCAL void
- ate_write(bridge_ate_p ate_ptr,
- int ate_count,
- bridge_ate_t ate)
- {
- while (ate_count-- > 0) {
- *ate_ptr++ = ate;
- ate += IOPGSIZE;
- }
- }
- #if PCIBR_FREEZE_TIME
- #define ATE_THAW() ate_thaw(pcibr_dmamap, ate_index, ate, ate_total, freeze_time, cmd_regs, s)
- #else
- #define ATE_THAW() ate_thaw(pcibr_dmamap, ate_index, cmd_regs, s)
- #endif
- LOCAL void
- ate_thaw(pcibr_dmamap_t pcibr_dmamap,
- int ate_index,
- #if PCIBR_FREEZE_TIME
- bridge_ate_t ate,
- int ate_total,
- unsigned freeze_time_start,
- #endif
- unsigned *cmd_regs,
- unsigned s)
- {
- pcibr_soft_t pcibr_soft = pcibr_dmamap->bd_soft;
- int dma_slot = pcibr_dmamap->bd_slot;
- int slot;
- bridge_t *bridge = pcibr_soft->bs_base;
- int ext_ates = pcibr_dmamap->bd_flags & PCIBR_DMAMAP_SSRAM;
- unsigned cmd_reg;
- #if PCIBR_FREEZE_TIME
- unsigned freeze_time;
- static unsigned max_freeze_time = 0;
- static unsigned max_ate_total;
- #endif
- if (!ext_ates)
- return;
- /* restore cmd regs */
- for (slot = 0; slot < 8; ++slot)
- if ((cmd_reg = cmd_regs[slot]) & PCI_CMD_BUS_MASTER)
- bridge->b_type0_cfg_dev[slot].l[PCI_CFG_COMMAND / 4] = cmd_reg;
- pcibr_dmamap->bd_flags |= PCIBR_DMAMAP_BUSY;
- atomic_inc(&(pcibr_soft->bs_slot[dma_slot]. bss_ext_ates_active));
- #if PCIBR_FREEZE_TIME
- freeze_time = get_timestamp() - freeze_time_start;
- if ((max_freeze_time < freeze_time) ||
- (max_ate_total < ate_total)) {
- if (max_freeze_time < freeze_time)
- max_freeze_time = freeze_time;
- if (max_ate_total < ate_total)
- max_ate_total = ate_total;
- pcibr_unlock(pcibr_soft, s);
- printk("%s: pci freeze time %d usec for %d ATEsn"
- "tfirst ate: %Rn",
- pcibr_soft->bs_name,
- freeze_time * 1000 / 1250,
- ate_total,
- ate, ate_bits);
- } else
- #endif
- pcibr_unlock(pcibr_soft, s);
- }
- /*ARGSUSED */
- iopaddr_t
- pcibr_dmamap_addr(pcibr_dmamap_t pcibr_dmamap,
- paddr_t paddr,
- size_t req_size)
- {
- pcibr_soft_t pcibr_soft;
- iopaddr_t xio_addr;
- xwidgetnum_t xio_port;
- iopaddr_t pci_addr;
- unsigned flags;
- ASSERT(pcibr_dmamap != NULL);
- ASSERT(req_size > 0);
- ASSERT(req_size <= pcibr_dmamap->bd_max_size);
- pcibr_soft = pcibr_dmamap->bd_soft;
- flags = pcibr_dmamap->bd_flags;
- xio_addr = xtalk_dmamap_addr(pcibr_dmamap->bd_xtalk, paddr, req_size);
- if (XIO_PACKED(xio_addr)) {
- xio_port = XIO_PORT(xio_addr);
- xio_addr = XIO_ADDR(xio_addr);
- } else
- xio_port = pcibr_dmamap->bd_xio_port;
- /* If this DMA is to an address that
- * refers back to this Bridge chip,
- * reduce it back to the correct
- * PCI MEM address.
- */
- if (xio_port == pcibr_soft->bs_xid) {
- pci_addr = pcibr_addr_xio_to_pci(pcibr_soft, xio_addr, req_size);
- } else if (flags & PCIIO_DMA_A64) {
- /* A64 DMA:
- * always use 64-bit direct mapping,
- * which always works.
- * Device(x) was set up during
- * dmamap allocation.
- */
- /* attributes are already bundled up into bd_pci_addr.
- */
- pci_addr = pcibr_dmamap->bd_pci_addr
- | ((uint64_t) xio_port << PCI64_ATTR_TARG_SHFT)
- | xio_addr;
- /* Bridge Hardware WAR #482836:
- * If the transfer is not cache aligned
- * and the Bridge Rev is <= B, force
- * prefetch to be off.
- */
- if (flags & PCIBR_NOPREFETCH)
- pci_addr &= ~PCI64_ATTR_PREF;
- #if DEBUG && PCIBR_DMA_DEBUG
- printk("pcibr_dmamap_addr (direct64):n"
- "twanted paddr [0x%x..0x%x]n"
- "tXIO port 0x%x offset 0x%xn"
- "treturning PCI 0x%xn",
- paddr, paddr + req_size - 1,
- xio_port, xio_addr, pci_addr);
- #endif
- } else if (flags & PCIIO_FIXED) {
- /* A32 direct DMA:
- * always use 32-bit direct mapping,
- * which may fail.
- * Device(x) was set up during
- * dmamap allocation.
- */
- if (xio_port != pcibr_soft->bs_dir_xport)
- pci_addr = 0; /* wrong DIDN */
- else if (xio_addr < pcibr_dmamap->bd_xio_addr)
- pci_addr = 0; /* out of range */
- else if ((xio_addr + req_size) >
- (pcibr_dmamap->bd_xio_addr + BRIDGE_DMA_DIRECT_SIZE))
- pci_addr = 0; /* out of range */
- else
- pci_addr = pcibr_dmamap->bd_pci_addr +
- xio_addr - pcibr_dmamap->bd_xio_addr;
- #if DEBUG && PCIBR_DMA_DEBUG
- printk("pcibr_dmamap_addr (direct32):n"
- "twanted paddr [0x%x..0x%x]n"
- "tXIO port 0x%x offset 0x%xn"
- "treturning PCI 0x%xn",
- paddr, paddr + req_size - 1,
- xio_port, xio_addr, pci_addr);
- #endif
- } else {
- bridge_t *bridge = pcibr_soft->bs_base;
- iopaddr_t offset = IOPGOFF(xio_addr);
- bridge_ate_t ate_proto = pcibr_dmamap->bd_ate_proto;
- int ate_count = IOPG(offset + req_size - 1) + 1;
- int ate_index = pcibr_dmamap->bd_ate_index;
- unsigned cmd_regs[8];
- unsigned s;
- #if PCIBR_FREEZE_TIME
- int ate_total = ate_count;
- unsigned freeze_time;
- #endif
- #if PCIBR_ATE_DEBUG
- bridge_ate_t ate_cmp;
- bridge_ate_p ate_cptr;
- unsigned ate_lo, ate_hi;
- int ate_bad = 0;
- int ate_rbc = 0;
- #endif
- bridge_ate_p ate_ptr = pcibr_dmamap->bd_ate_ptr;
- bridge_ate_t ate;
- /* Bridge Hardware WAR #482836:
- * If the transfer is not cache aligned
- * and the Bridge Rev is <= B, force
- * prefetch to be off.
- */
- if (flags & PCIBR_NOPREFETCH)
- ate_proto &= ~ATE_PREF;
- ate = ate_proto
- | (xio_port << ATE_TIDSHIFT)
- | (xio_addr - offset);
- pci_addr = pcibr_dmamap->bd_pci_addr + offset;
- /* Fill in our mapping registers
- * with the appropriate xtalk data,
- * and hand back the PCI address.
- */
- ASSERT(ate_count > 0);
- if (ate_count <= pcibr_dmamap->bd_ate_count) {
- ATE_FREEZE();
- ATE_WRITE();
- ATE_THAW();
- bridge->b_wid_tflush; /* wait until Bridge PIO complete */
- } else {
- /* The number of ATE's required is greater than the number
- * allocated for this map. One way this can happen is if
- * pcibr_dmamap_alloc() was called with the PCIBR_NO_ATE_ROUNDUP
- * flag, and then when that map is used (right now), the
- * target address tells us we really did need to roundup.
- * The other possibility is that the map is just plain too
- * small to handle the requested target area.
- */
- #if PCIBR_ATE_DEBUG
- printk(KERN_WARNING "pcibr_dmamap_addr :n"
- "twanted paddr [0x%x..0x%x]n"
- "tate_count 0x%x bd_ate_count 0x%xn"
- "tATE's required > number allocatedn",
- paddr, paddr + req_size - 1,
- ate_count, pcibr_dmamap->bd_ate_count);
- #endif
- pci_addr = 0;
- }
- }
- return pci_addr;
- }
- /*ARGSUSED */
- alenlist_t
- pcibr_dmamap_list(pcibr_dmamap_t pcibr_dmamap,
- alenlist_t palenlist,
- unsigned flags)
- {
- pcibr_soft_t pcibr_soft;
- bridge_t *bridge=NULL;
- unsigned al_flags = (flags & PCIIO_NOSLEEP) ? AL_NOSLEEP : 0;
- int inplace = flags & PCIIO_INPLACE;
- alenlist_t pciio_alenlist = 0;
- alenlist_t xtalk_alenlist;
- size_t length;
- iopaddr_t offset;
- unsigned direct64;
- int ate_index = 0;
- int ate_count = 0;
- int ate_total = 0;
- bridge_ate_p ate_ptr = (bridge_ate_p)0;
- bridge_ate_t ate_proto = (bridge_ate_t)0;
- bridge_ate_t ate_prev;
- bridge_ate_t ate;
- alenaddr_t xio_addr;
- xwidgetnum_t xio_port;
- iopaddr_t pci_addr;
- alenaddr_t new_addr;
- unsigned cmd_regs[8];
- unsigned s = 0;
- #if PCIBR_FREEZE_TIME
- unsigned freeze_time;
- #endif
- int ate_freeze_done = 0; /* To pair ATE_THAW
- * with an ATE_FREEZE
- */
- pcibr_soft = pcibr_dmamap->bd_soft;
- xtalk_alenlist = xtalk_dmamap_list(pcibr_dmamap->bd_xtalk, palenlist,
- flags & DMAMAP_FLAGS);
- if (!xtalk_alenlist)
- goto fail;
- alenlist_cursor_init(xtalk_alenlist, 0, NULL);
- if (inplace) {
- pciio_alenlist = xtalk_alenlist;
- } else {
- pciio_alenlist = alenlist_create(al_flags);
- if (!pciio_alenlist)
- goto fail;
- }
- direct64 = pcibr_dmamap->bd_flags & PCIIO_DMA_A64;
- if (!direct64) {
- bridge = pcibr_soft->bs_base;
- ate_ptr = pcibr_dmamap->bd_ate_ptr;
- ate_index = pcibr_dmamap->bd_ate_index;
- ate_proto = pcibr_dmamap->bd_ate_proto;
- ATE_FREEZE();
- ate_freeze_done = 1; /* Remember that we need to do an ATE_THAW */
- }
- pci_addr = pcibr_dmamap->bd_pci_addr;
- ate_prev = 0; /* matches no valid ATEs */
- while (ALENLIST_SUCCESS ==
- alenlist_get(xtalk_alenlist, NULL, 0,
- &xio_addr, &length, al_flags)) {
- if (XIO_PACKED(xio_addr)) {
- xio_port = XIO_PORT(xio_addr);
- xio_addr = XIO_ADDR(xio_addr);
- } else
- xio_port = pcibr_dmamap->bd_xio_port;
- if (xio_port == pcibr_soft->bs_xid) {
- new_addr = pcibr_addr_xio_to_pci(pcibr_soft, xio_addr, length);
- if (new_addr == PCI_NOWHERE)
- goto fail;
- } else if (direct64) {
- new_addr = pci_addr | xio_addr
- | ((uint64_t) xio_port << PCI64_ATTR_TARG_SHFT);
- /* Bridge Hardware WAR #482836:
- * If the transfer is not cache aligned
- * and the Bridge Rev is <= B, force
- * prefetch to be off.
- */
- if (flags & PCIBR_NOPREFETCH)
- new_addr &= ~PCI64_ATTR_PREF;
- } else {
- /* calculate the ate value for
- * the first address. If it
- * matches the previous
- * ATE written (ie. we had
- * multiple blocks in the
- * same IOPG), then back up
- * and reuse that ATE.
- *
- * We are NOT going to
- * aggressively try to
- * reuse any other ATEs.
- */
- offset = IOPGOFF(xio_addr);
- ate = ate_proto
- | (xio_port << ATE_TIDSHIFT)
- | (xio_addr - offset);
- if (ate == ate_prev) {
- #if PCIBR_ATE_DEBUG
- printk("pcibr_dmamap_list: ATE sharen");
- #endif
- ate_ptr--;
- ate_index--;
- pci_addr -= IOPGSIZE;
- }
- new_addr = pci_addr + offset;
- /* Fill in the hardware ATEs
- * that contain this block.
- */
- ate_count = IOPG(offset + length - 1) + 1;
- ate_total += ate_count;
- /* Ensure that this map contains enough ATE's */
- if (ate_total > pcibr_dmamap->bd_ate_count) {
- #if PCIBR_ATE_DEBUG
- printk(KERN_WARNING "pcibr_dmamap_list :n"
- "twanted xio_addr [0x%x..0x%x]n"
- "tate_total 0x%x bd_ate_count 0x%xn"
- "tATE's required > number allocatedn",
- xio_addr, xio_addr + length - 1,
- ate_total, pcibr_dmamap->bd_ate_count);
- #endif
- goto fail;
- }
- ATE_WRITE();
- ate_index += ate_count;
- ate_ptr += ate_count;
- ate_count <<= IOPFNSHIFT;
- ate += ate_count;
- pci_addr += ate_count;
- }
- /* write the PCI DMA address
- * out to the scatter-gather list.
- */
- if (inplace) {
- if (ALENLIST_SUCCESS !=
- alenlist_replace(pciio_alenlist, NULL,
- &new_addr, &length, al_flags))
- goto fail;
- } else {
- if (ALENLIST_SUCCESS !=
- alenlist_append(pciio_alenlist,
- new_addr, length, al_flags))
- goto fail;
- }
- }
- if (!inplace)
- alenlist_done(xtalk_alenlist);
- /* Reset the internal cursor of the alenlist to be returned back
- * to the caller.
- */
- alenlist_cursor_init(pciio_alenlist, 0, NULL);
- /* In case an ATE_FREEZE was done do the ATE_THAW to unroll all the
- * changes that ATE_FREEZE has done to implement the external SSRAM
- * bug workaround.
- */
- if (ate_freeze_done) {
- ATE_THAW();
- bridge->b_wid_tflush; /* wait until Bridge PIO complete */
- }
- return pciio_alenlist;
- fail:
- /* There are various points of failure after doing an ATE_FREEZE
- * We need to do an ATE_THAW. Otherwise the ATEs are locked forever.
- * The decision to do an ATE_THAW needs to be based on whether a
- * an ATE_FREEZE was done before.
- */
- if (ate_freeze_done) {
- ATE_THAW();
- bridge->b_wid_tflush;
- }
- if (pciio_alenlist && !inplace)
- alenlist_destroy(pciio_alenlist);
- return 0;
- }
- /*ARGSUSED */
- void
- pcibr_dmamap_done(pcibr_dmamap_t pcibr_dmamap)
- {
- /*
- * We could go through and invalidate ATEs here;
- * for performance reasons, we don't.
- * We also don't enforce the strict alternation
- * between _addr/_list and _done, but Hub does.
- */
- if (pcibr_dmamap->bd_flags & PCIBR_DMAMAP_BUSY) {
- pcibr_dmamap->bd_flags &= ~PCIBR_DMAMAP_BUSY;
- if (pcibr_dmamap->bd_flags & PCIBR_DMAMAP_SSRAM)
- atomic_dec(&(pcibr_dmamap->bd_soft->bs_slot[pcibr_dmamap->bd_slot]. bss_ext_ates_active));
- }
- xtalk_dmamap_done(pcibr_dmamap->bd_xtalk);
- }
- /*
- * For each bridge, the DIR_OFF value in the Direct Mapping Register
- * determines the PCI to Crosstalk memory mapping to be used for all
- * 32-bit Direct Mapping memory accesses. This mapping can be to any
- * node in the system. This function will return that compact node id.
- */
- /*ARGSUSED */
- cnodeid_t
- pcibr_get_dmatrans_node(devfs_handle_t pconn_vhdl)
- {
- pciio_info_t pciio_info = pciio_info_get(pconn_vhdl);
- pcibr_soft_t pcibr_soft = (pcibr_soft_t) pciio_info_mfast_get(pciio_info);
- return(NASID_TO_COMPACT_NODEID(NASID_GET(pcibr_soft->bs_dir_xbase)));
- }
- /*ARGSUSED */
- iopaddr_t
- pcibr_dmatrans_addr(devfs_handle_t pconn_vhdl,
- device_desc_t dev_desc,
- paddr_t paddr,
- size_t req_size,
- unsigned flags)
- {
- pciio_info_t pciio_info = pciio_info_get(pconn_vhdl);
- pcibr_soft_t pcibr_soft = (pcibr_soft_t) pciio_info_mfast_get(pciio_info);
- devfs_handle_t xconn_vhdl = pcibr_soft->bs_conn;
- pciio_slot_t pciio_slot = pciio_info_slot_get(pciio_info);
- pcibr_soft_slot_t slotp = &pcibr_soft->bs_slot[pciio_slot];
- xwidgetnum_t xio_port;
- iopaddr_t xio_addr;
- iopaddr_t pci_addr;
- int have_rrbs;
- int min_rrbs;
- /* merge in forced flags */
- flags |= pcibr_soft->bs_dma_flags;
- xio_addr = xtalk_dmatrans_addr(xconn_vhdl, 0, paddr, req_size,
- flags & DMAMAP_FLAGS);
- if (!xio_addr) {
- #if PCIBR_DMA_DEBUG
- printk("pcibr_dmatrans_addr:n"
- "tpciio connection point %vn"
- "txtalk connection point %vn"
- "twanted paddr [0x%x..0x%x]n"
- "txtalk_dmatrans_addr returned 0x%xn",
- pconn_vhdl, xconn_vhdl,
- paddr, paddr + req_size - 1,
- xio_addr);
- #endif
- return 0;
- }
- /*
- * find which XIO port this goes to.
- */
- if (XIO_PACKED(xio_addr)) {
- if (xio_addr == XIO_NOWHERE) {
- #if PCIBR_DMA_DEBUG
- printk("pcibr_dmatrans_addr:n"
- "tpciio connection point %vn"
- "txtalk connection point %vn"
- "twanted paddr [0x%x..0x%x]n"
- "txtalk_dmatrans_addr returned 0x%xn",
- pconn_vhdl, xconn_vhdl,
- paddr, paddr + req_size - 1,
- xio_addr);
- #endif
- return 0;
- }
- xio_port = XIO_PORT(xio_addr);
- xio_addr = XIO_ADDR(xio_addr);
- } else
- xio_port = pcibr_soft->bs_mxid;
- /*
- * If this DMA comes back to us,
- * return the PCI MEM address on
- * which it would land, or NULL
- * if the target is something
- * on bridge other than PCI MEM.
- */
- if (xio_port == pcibr_soft->bs_xid) {
- pci_addr = pcibr_addr_xio_to_pci(pcibr_soft, xio_addr, req_size);
- return pci_addr;
- }
- /* If the caller can use A64, try to
- * satisfy the request with the 64-bit
- * direct map. This can fail if the
- * configuration bits in Device(x)
- * conflict with our flags.
- */
- if (flags & PCIIO_DMA_A64) {
- pci_addr = slotp->bss_d64_base;
- if (!(flags & PCIBR_VCHAN1))
- flags |= PCIBR_VCHAN0;
- if ((pci_addr != PCIBR_D64_BASE_UNSET) &&
- (flags == slotp->bss_d64_flags)) {
- pci_addr |= xio_addr
- | ((uint64_t) xio_port << PCI64_ATTR_TARG_SHFT);
- #if DEBUG && PCIBR_DMA_DEBUG
- #if HWG_PERF_CHECK
- if (xio_addr != 0x20000000)
- #endif
- printk("pcibr_dmatrans_addr: [reuse]n"
- "tpciio connection point %vn"
- "txtalk connection point %vn"
- "twanted paddr [0x%x..0x%x]n"
- "txtalk_dmatrans_addr returned 0x%xn"
- "tdirect 64bit address is 0x%xn",
- pconn_vhdl, xconn_vhdl,
- paddr, paddr + req_size - 1,
- xio_addr, pci_addr);
- #endif
- return (pci_addr);
- }
- if (!pcibr_try_set_device(pcibr_soft, pciio_slot, flags, BRIDGE_DEV_D64_BITS)) {
- pci_addr = pcibr_flags_to_d64(flags, pcibr_soft);
- slotp->bss_d64_flags = flags;
- slotp->bss_d64_base = pci_addr;
- pci_addr |= xio_addr
- | ((uint64_t) xio_port << PCI64_ATTR_TARG_SHFT);
- /* Make sure we have an RRB (or two).
- */
- if (!(pcibr_soft->bs_rrb_fixed & (1 << pciio_slot))) {
- if (flags & PCIBR_VCHAN1)
- pciio_slot += PCIBR_RRB_SLOT_VIRTUAL;
- have_rrbs = pcibr_soft->bs_rrb_valid[pciio_slot];
- if (have_rrbs < 2) {
- if (pci_addr & PCI64_ATTR_PREF)
- min_rrbs = 2;
- else
- min_rrbs = 1;
- if (have_rrbs < min_rrbs)
- do_pcibr_rrb_autoalloc(pcibr_soft, pciio_slot, min_rrbs - have_rrbs);
- }
- }
- #if PCIBR_DMA_DEBUG
- #if HWG_PERF_CHECK
- if (xio_addr != 0x20000000)
- #endif
- printk("pcibr_dmatrans_addr:n"
- "tpciio connection point %vn"
- "txtalk connection point %vn"
- "twanted paddr [0x%x..0x%x]n"
- "txtalk_dmatrans_addr returned 0x%xn"
- "tdirect 64bit address is 0x%xn"
- "tnew flags: 0x%xn",
- pconn_vhdl, xconn_vhdl,
- paddr, paddr + req_size - 1,
- xio_addr, pci_addr, (uint64_t) flags);
- #endif
- return (pci_addr);
- }
- /* our flags conflict with Device(x).
- */
- flags = flags
- & ~PCIIO_DMA_A64
- & ~PCIBR_VCHAN0
- ;
- #if PCIBR_DMA_DEBUG
- printk("pcibr_dmatrans_addr:n"
- "tpciio connection point %vn"
- "txtalk connection point %vn"
- "twanted paddr [0x%x..0x%x]n"
- "txtalk_dmatrans_addr returned 0x%xn"
- "tUnable to set Device(x) bits for Direct-64n",
- pconn_vhdl, xconn_vhdl,
- paddr, paddr + req_size - 1,
- xio_addr);
- #endif
- }
- /* Try to satisfy the request with the 32-bit direct
- * map. This can fail if the configuration bits in
- * Device(x) conflict with our flags, or if the
- * target address is outside where DIR_OFF points.
- */
- {
- size_t map_size = 1ULL << 31;
- iopaddr_t xio_base = pcibr_soft->bs_dir_xbase;
- iopaddr_t offset = xio_addr - xio_base;
- iopaddr_t endoff = req_size + offset;
- if ((req_size > map_size) ||
- (xio_addr < xio_base) ||
- (xio_port != pcibr_soft->bs_dir_xport) ||
- (endoff > map_size)) {
- #if PCIBR_DMA_DEBUG
- printk("pcibr_dmatrans_addr:n"
- "tpciio connection point %vn"
- "txtalk connection point %vn"
- "twanted paddr [0x%x..0x%x]n"
- "txtalk_dmatrans_addr returned 0x%xn"
- "txio region outside direct32 targetn",
- pconn_vhdl, xconn_vhdl,
- paddr, paddr + req_size - 1,
- xio_addr);
- #endif
- } else {
- pci_addr = slotp->bss_d32_base;
- if ((pci_addr != PCIBR_D32_BASE_UNSET) &&
- (flags == slotp->bss_d32_flags)) {
- pci_addr |= offset;
- #if DEBUG && PCIBR_DMA_DEBUG
- printk("pcibr_dmatrans_addr: [reuse]n"
- "tpciio connection point %vn"
- "txtalk connection point %vn"
- "twanted paddr [0x%x..0x%x]n"
- "txtalk_dmatrans_addr returned 0x%xn"
- "tmapped via direct32 offset 0x%xn"
- "twill DMA via pci addr 0x%xn",
- pconn_vhdl, xconn_vhdl,
- paddr, paddr + req_size - 1,
- xio_addr, offset, pci_addr);
- #endif
- return (pci_addr);
- }
- if (!pcibr_try_set_device(pcibr_soft, pciio_slot, flags, BRIDGE_DEV_D32_BITS)) {
- pci_addr = PCI32_DIRECT_BASE;
- slotp->bss_d32_flags = flags;
- slotp->bss_d32_base = pci_addr;
- pci_addr |= offset;
- /* Make sure we have an RRB (or two).
- */
- if (!(pcibr_soft->bs_rrb_fixed & (1 << pciio_slot))) {
- have_rrbs = pcibr_soft->bs_rrb_valid[pciio_slot];
- if (have_rrbs < 2) {
- if (slotp->bss_device & BRIDGE_DEV_PREF)
- min_rrbs = 2;
- else
- min_rrbs = 1;
- if (have_rrbs < min_rrbs)
- do_pcibr_rrb_autoalloc(pcibr_soft, pciio_slot, min_rrbs - have_rrbs);
- }
- }
- #if PCIBR_DMA_DEBUG
- #if HWG_PERF_CHECK
- if (xio_addr != 0x20000000)
- #endif
- printk("pcibr_dmatrans_addr:n"
- "tpciio connection point %vn"
- "txtalk connection point %vn"
- "twanted paddr [0x%x..0x%x]n"
- "txtalk_dmatrans_addr returned 0x%xn"
- "tmapped via direct32 offset 0x%xn"
- "twill DMA via pci addr 0x%xn"
- "tnew flags: 0x%xn",
- pconn_vhdl, xconn_vhdl,
- paddr, paddr + req_size - 1,
- xio_addr, offset, pci_addr, (uint64_t) flags);
- #endif
- return (pci_addr);
- }
- /* our flags conflict with Device(x).
- */
- #if PCIBR_DMA_DEBUG
- printk("pcibr_dmatrans_addr:n"
- "tpciio connection point %vn"
- "txtalk connection point %vn"
- "twanted paddr [0x%x..0x%x]n"
- "txtalk_dmatrans_addr returned 0x%xn"
- "tUnable to set Device(x) bits for Direct-32n",
- pconn_vhdl, xconn_vhdl,
- paddr, paddr + req_size - 1,
- xio_addr);
- #endif
- }
- }
- #if PCIBR_DMA_DEBUG
- printk("pcibr_dmatrans_addr:n"
- "tpciio connection point %vn"
- "txtalk connection point %vn"
- "twanted paddr [0x%x..0x%x]n"
- "txtalk_dmatrans_addr returned 0x%xn"
- "tno acceptable PCI address found or constructablen",
- pconn_vhdl, xconn_vhdl,
- paddr, paddr + req_size - 1,
- xio_addr);
- #endif
- return 0;
- }
- /*ARGSUSED */
- alenlist_t
- pcibr_dmatrans_list(devfs_handle_t pconn_vhdl,
- device_desc_t dev_desc,
- alenlist_t palenlist,
- unsigned flags)
- {
- pciio_info_t pciio_info = pciio_info_get(pconn_vhdl);
- pcibr_soft_t pcibr_soft = (pcibr_soft_t) pciio_info_mfast_get(pciio_info);
- devfs_handle_t xconn_vhdl = pcibr_soft->bs_conn;
- pciio_slot_t pciio_slot = pciio_info_slot_get(pciio_info);
- pcibr_soft_slot_t slotp = &pcibr_soft->bs_slot[pciio_slot];
- xwidgetnum_t xio_port;
- alenlist_t pciio_alenlist = 0;
- alenlist_t xtalk_alenlist = 0;
- int inplace;
- unsigned direct64;
- unsigned al_flags;
- iopaddr_t xio_base;
- alenaddr_t xio_addr;
- size_t xio_size;
- size_t map_size;
- iopaddr_t pci_base;
- alenaddr_t pci_addr;
- unsigned relbits = 0;
- /* merge in forced flags */
- flags |= pcibr_soft->bs_dma_flags;
- inplace = flags & PCIIO_INPLACE;
- direct64 = flags & PCIIO_DMA_A64;
- al_flags = (flags & PCIIO_NOSLEEP) ? AL_NOSLEEP : 0;
- if (direct64) {
- map_size = 1ull << 48;
- xio_base = 0;
- pci_base = slotp->bss_d64_base;
- if ((pci_base != PCIBR_D64_BASE_UNSET) &&
- (flags == slotp->bss_d64_flags)) {
- /* reuse previous base info */
- } else if (pcibr_try_set_device(pcibr_soft, pciio_slot, flags, BRIDGE_DEV_D64_BITS) < 0) {
- /* DMA configuration conflict */
- goto fail;
- } else {
- relbits = BRIDGE_DEV_D64_BITS;
- pci_base =
- pcibr_flags_to_d64(flags, pcibr_soft);
- }
- } else {
- xio_base = pcibr_soft->bs_dir_xbase;
- map_size = 1ull << 31;
- pci_base = slotp->bss_d32_base;
- if ((pci_base != PCIBR_D32_BASE_UNSET) &&
- (flags == slotp->bss_d32_flags)) {
- /* reuse previous base info */
- } else if (pcibr_try_set_device(pcibr_soft, pciio_slot, flags, BRIDGE_DEV_D32_BITS) < 0) {
- /* DMA configuration conflict */
- goto fail;
- } else {
- relbits = BRIDGE_DEV_D32_BITS;
- pci_base = PCI32_DIRECT_BASE;
- }
- }
- xtalk_alenlist = xtalk_dmatrans_list(xconn_vhdl, 0, palenlist,
- flags & DMAMAP_FLAGS);
- if (!xtalk_alenlist)
- goto fail;
- alenlist_cursor_init(xtalk_alenlist, 0, NULL);
- if (inplace) {
- pciio_alenlist = xtalk_alenlist;
- } else {
- pciio_alenlist = alenlist_create(al_flags);
- if (!pciio_alenlist)
- goto fail;
- }
- while (ALENLIST_SUCCESS ==
- alenlist_get(xtalk_alenlist, NULL, 0,
- &xio_addr, &xio_size, al_flags)) {
- /*
- * find which XIO port this goes to.
- */
- if (XIO_PACKED(xio_addr)) {
- if (xio_addr == XIO_NOWHERE) {
- #if PCIBR_DMA_DEBUG
- printk("pcibr_dmatrans_addr:n"
- "tpciio connection point %vn"
- "txtalk connection point %vn"
- "twanted paddr [0x%x..0x%x]n"
- "txtalk_dmatrans_addr returned 0x%xn",
- pconn_vhdl, xconn_vhdl,
- paddr, paddr + req_size - 1,
- xio_addr);
- #endif
- return 0;
- }
- xio_port = XIO_PORT(xio_addr);
- xio_addr = XIO_ADDR(xio_addr);
- } else
- xio_port = pcibr_soft->bs_mxid;
- /*
- * If this DMA comes back to us,
- * return the PCI MEM address on
- * which it would land, or NULL
- * if the target is something
- * on bridge other than PCI MEM.
- */
- if (xio_port == pcibr_soft->bs_xid) {
- pci_addr = pcibr_addr_xio_to_pci(pcibr_soft, xio_addr, xio_size);
- if ( (pci_addr == (alenaddr_t)NULL) )
- goto fail;
- } else if (direct64) {
- ASSERT(xio_port != 0);
- pci_addr = pci_base | xio_addr
- | ((uint64_t) xio_port << PCI64_ATTR_TARG_SHFT);
- } else {
- iopaddr_t offset = xio_addr - xio_base;
- iopaddr_t endoff = xio_size + offset;
- if ((xio_size > map_size) ||
- (xio_addr < xio_base) ||
- (xio_port != pcibr_soft->bs_dir_xport) ||
- (endoff > map_size))
- goto fail;
- pci_addr = pci_base + (xio_addr - xio_base);
- }
- /* write the PCI DMA address
- * out to the scatter-gather list.
- */
- if (inplace) {
- if (ALENLIST_SUCCESS !=
- alenlist_replace(pciio_alenlist, NULL,
- &pci_addr, &xio_size, al_flags))
- goto fail;
- } else {
- if (ALENLIST_SUCCESS !=
- alenlist_append(pciio_alenlist,
- pci_addr, xio_size, al_flags))
- goto fail;
- }
- }
- if (relbits) {
- if (direct64) {
- slotp->bss_d64_flags = flags;
- slotp->bss_d64_base = pci_base;
- } else {
- slotp->bss_d32_flags = flags;
- slotp->bss_d32_base = pci_base;
- }
- }
- if (!inplace)
- alenlist_done(xtalk_alenlist);
- /* Reset the internal cursor of the alenlist to be returned back
- * to the caller.
- */
- alenlist_cursor_init(pciio_alenlist, 0, NULL);
- return pciio_alenlist;
- fail:
- if (relbits)
- pcibr_release_device(pcibr_soft, pciio_slot, relbits);
- if (pciio_alenlist && !inplace)
- alenlist_destroy(pciio_alenlist);
- return 0;
- }
- void
- pcibr_dmamap_drain(pcibr_dmamap_t map)
- {
- xtalk_dmamap_drain(map->bd_xtalk);
- }
- void
- pcibr_dmaaddr_drain(devfs_handle_t pconn_vhdl,
- paddr_t paddr,
- size_t bytes)
- {
- pciio_info_t pciio_info = pciio_info_get(pconn_vhdl);
- pcibr_soft_t pcibr_soft = (pcibr_soft_t) pciio_info_mfast_get(pciio_info);
- devfs_handle_t xconn_vhdl = pcibr_soft->bs_conn;
- xtalk_dmaaddr_drain(xconn_vhdl, paddr, bytes);
- }
- void
- pcibr_dmalist_drain(devfs_handle_t pconn_vhdl,
- alenlist_t list)
- {
- pciio_info_t pciio_info = pciio_info_get(pconn_vhdl);
- pcibr_soft_t pcibr_soft = (pcibr_soft_t) pciio_info_mfast_get(pciio_info);
- devfs_handle_t xconn_vhdl = pcibr_soft->bs_conn;
- xtalk_dmalist_drain(xconn_vhdl, list);
- }
- /*
- * Get the starting PCIbus address out of the given DMA map.
- * This function is supposed to be used by a close friend of PCI bridge
- * since it relies on the fact that the starting address of the map is fixed at
- * the allocation time in the current implementation of PCI bridge.
- */
- iopaddr_t
- pcibr_dmamap_pciaddr_get(pcibr_dmamap_t pcibr_dmamap)
- {
- return (pcibr_dmamap->bd_pci_addr);
- }
- /*
- * There are end cases where a deadlock can occur if interrupt
- * processing completes and the Bridge b_int_status bit is still set.
- *
- * One scenerio is if a second PCI interrupt occurs within 60ns of
- * the previous interrupt being cleared. In this case the Bridge
- * does not detect the transition, the Bridge b_int_status bit
- * remains set, and because no transition was detected no interrupt
- * packet is sent to the Hub/Heart.
- *
- * A second scenerio is possible when a b_int_status bit is being
- * shared by multiple devices:
- * Device #1 generates interrupt
- * Bridge b_int_status bit set
- * Device #2 generates interrupt
- * interrupt processing begins
- * ISR for device #1 runs and
- * clears interrupt
- * Device #1 generates interrupt
- * ISR for device #2 runs and
- * clears interrupt
- * (b_int_status bit still set)
- * interrupt processing completes
- *
- * Interrupt processing is now complete, but an interrupt is still
- * outstanding for Device #1. But because there was no transition of
- * the b_int_status bit, no interrupt packet will be generated and
- * a deadlock will occur.
- *
- * To avoid these deadlock situations, this function is used
- * to check if a specific Bridge b_int_status bit is set, and if so,
- * cause the setting of the corresponding interrupt bit.
- *
- * On a XBridge (IP35), we do this by writing the appropriate Bridge Force
- * Interrupt register.
- */
- void
- pcibr_force_interrupt(pcibr_intr_wrap_t wrap)
- {
- unsigned bit;
- pcibr_soft_t pcibr_soft = wrap->iw_soft;
- bridge_t *bridge = pcibr_soft->bs_base;
- cpuid_t cpuvertex_to_cpuid(devfs_handle_t vhdl);
- bit = wrap->iw_intr;
- if (pcibr_soft->bs_xbridge) {
- bridge->b_force_pin[bit].intr = 1;
- } else if ((1 << bit) & *wrap->iw_stat) {
- cpuid_t cpu;
- unsigned intr_bit;
- xtalk_intr_t xtalk_intr =
- pcibr_soft->bs_intr[bit].bsi_xtalk_intr;
- intr_bit = (short) xtalk_intr_vector_get(xtalk_intr);
- cpu = cpuvertex_to_cpuid(xtalk_intr_cpu_get(xtalk_intr));
- #if defined(CONFIG_IA64_SGI_SN1)
- REMOTE_CPU_SEND_INTR(cpu, intr_bit);
- #endif
- }
- }
- /* =====================================================================
- * INTERRUPT MANAGEMENT
- */
- static unsigned
- pcibr_intr_bits(pciio_info_t info,
- pciio_intr_line_t lines)
- {
- pciio_slot_t slot = pciio_info_slot_get(info);
- unsigned bbits = 0;
- /*
- * Currently favored mapping from PCI
- * slot number and INTA/B/C/D to Bridge
- * PCI Interrupt Bit Number:
- *
- * SLOT A B C D
- * 0 0 4 0 4
- * 1 1 5 1 5
- * 2 2 6 2 6
- * 3 3 7 3 7
- * 4 4 0 4 0
- * 5 5 1 5 1
- * 6 6 2 6 2
- * 7 7 3 7 3
- */
- if (slot < 8) {
- if (lines & (PCIIO_INTR_LINE_A| PCIIO_INTR_LINE_C))
- bbits |= 1 << slot;
- if (lines & (PCIIO_INTR_LINE_B| PCIIO_INTR_LINE_D))
- bbits |= 1 << (slot ^ 4);
- }
- return bbits;
- }
- /*ARGSUSED */
- pcibr_intr_t
- pcibr_intr_alloc(devfs_handle_t pconn_vhdl,
- device_desc_t dev_desc,
- pciio_intr_line_t lines,
- devfs_handle_t owner_dev)
- {
- pcibr_info_t pcibr_info = pcibr_info_get(pconn_vhdl);
- pciio_slot_t pciio_slot = pcibr_info->f_slot;
- pcibr_soft_t pcibr_soft = (pcibr_soft_t) pcibr_info->f_mfast;
- devfs_handle_t xconn_vhdl = pcibr_soft->bs_conn;
- bridge_t *bridge = pcibr_soft->bs_base;
- int is_threaded = 0;
- int thread_swlevel;
- xtalk_intr_t *xtalk_intr_p;
- pcibr_intr_t *pcibr_intr_p;
- pcibr_intr_list_t *intr_list_p;
- unsigned pcibr_int_bits;
- unsigned pcibr_int_bit;
- xtalk_intr_t xtalk_intr = (xtalk_intr_t)0;
- hub_intr_t hub_intr;
- pcibr_intr_t pcibr_intr;
- pcibr_intr_list_t intr_entry;
- pcibr_intr_list_t intr_list;
- bridgereg_t int_dev;
- #if DEBUG && INTR_DEBUG
- printk("%v: pcibr_intr_allocn"
- "%v:%s%s%s%s%sn",
- owner_dev, pconn_vhdl,
- !(lines & 15) ? " No INTs?" : "",
- lines & 1 ? " INTA" : "",
- lines & 2 ? " INTB" : "",
- lines & 4 ? " INTC" : "",
- lines & 8 ? " INTD" : "");
- #endif
- NEW(pcibr_intr);
- if (!pcibr_intr)
- return NULL;
- if (dev_desc) {
- cpuid_t intr_target_from_desc(device_desc_t, int);
- } else {
- extern int default_intr_pri;
- is_threaded = 1; /* PCI interrupts are threaded, by default */
- thread_swlevel = default_intr_pri;
- }
- pcibr_intr->bi_dev = pconn_vhdl;
- pcibr_intr->bi_lines = lines;
- pcibr_intr->bi_soft = pcibr_soft;
- pcibr_intr->bi_ibits = 0; /* bits will be added below */
- pcibr_intr->bi_flags = is_threaded ? 0 : PCIIO_INTR_NOTHREAD;
- pcibr_intr->bi_mustruncpu = CPU_NONE;
- mutex_spinlock_init(&pcibr_intr->bi_ibuf.ib_lock);
- pcibr_int_bits = pcibr_soft->bs_intr_bits((pciio_info_t)pcibr_info, lines);
- /*
- * For each PCI interrupt line requested, figure
- * out which Bridge PCI Interrupt Line it maps
- * to, and make sure there are xtalk resources
- * allocated for it.
- */
- #if DEBUG && INTR_DEBUG
- printk("pcibr_int_bits: 0x%Xn", pcibr_int_bits);
- #endif
- for (pcibr_int_bit = 0; pcibr_int_bit < 8; pcibr_int_bit ++) {
- if (pcibr_int_bits & (1 << pcibr_int_bit)) {
- xtalk_intr_p = &pcibr_soft->bs_intr[pcibr_int_bit].bsi_xtalk_intr;
- xtalk_intr = *xtalk_intr_p;
- if (xtalk_intr == NULL) {
- /*
- * This xtalk_intr_alloc is constrained for two reasons:
- * 1) Normal interrupts and error interrupts need to be delivered
- * through a single xtalk target widget so that there aren't any
- * ordering problems with DMA, completion interrupts, and error
- * interrupts. (Use of xconn_vhdl forces this.)
- *
- * 2) On IP35, addressing constraints on IP35 and Bridge force
- * us to use a single PI number for all interrupts from a
- * single Bridge. (IP35-specific code forces this, and we
- * verify in pcibr_setwidint.)
- */
- /*
- * All code dealing with threaded PCI interrupt handlers
- * is located at the pcibr level. Because of this,
- * we always want the lower layers (hub/heart_intr_alloc,
- * intr_level_connect) to treat us as non-threaded so we
- * don't set up a duplicate threaded environment. We make
- * this happen by calling a special xtalk interface.
- */
- xtalk_intr = xtalk_intr_alloc_nothd(xconn_vhdl, dev_desc,
- owner_dev);
- #if DEBUG && INTR_DEBUG
- printk("%v: xtalk_intr=0x%Xn", xconn_vhdl, xtalk_intr);
- #endif
- /* both an assert and a runtime check on this:
- * we need to check in non-DEBUG kernels, and
- * the ASSERT gets us more information when
- * we use DEBUG kernels.
- */
- ASSERT(xtalk_intr != NULL);
- if (xtalk_intr == NULL) {
- /* it is quite possible that our
- * xtalk_intr_alloc failed because
- * someone else got there first,
- * and we can find their results
- * in xtalk_intr_p.
- */
- if (!*xtalk_intr_p) {
- #ifdef SUPPORT_PRINTING_V_FORMAT
- printk(KERN_ALERT
- "pcibr_intr_alloc %v: unable to get xtalk interrupt resources",
- xconn_vhdl);
- #else
- printk(KERN_ALERT
- "pcibr_intr_alloc 0x%p: unable to get xtalk interrupt resources",
- (void *)xconn_vhdl);
- #endif
- /* yes, we leak resources here. */
- return 0;
- }
- } else if (compare_and_swap_ptr((void **) xtalk_intr_p, NULL, xtalk_intr)) {
- /*
- * now tell the bridge which slot is
- * using this interrupt line.
- */
- int_dev = bridge->b_int_device;
- int_dev &= ~BRIDGE_INT_DEV_MASK(pcibr_int_bit);
- int_dev |= pciio_slot << BRIDGE_INT_DEV_SHFT(pcibr_int_bit);
- bridge->b_int_device = int_dev; /* XXXMP */
- #if DEBUG && INTR_DEBUG
- printk("%v: bridge intr bit %d clears my wrbn",
- pconn_vhdl, pcibr_int_bit);
- #endif
- } else {
- /* someone else got one allocated first;
- * free the one we just created, and
- * retrieve the one they allocated.
- */
- xtalk_intr_free(xtalk_intr);
- xtalk_intr = *xtalk_intr_p;
- #if PARANOID
- /* once xtalk_intr is set, we never clear it,
- * so if the CAS fails above, this condition
- * can "never happen" ...
- */
- if (!xtalk_intr) {
- printk(KERN_ALERT
- "pcibr_intr_alloc %v: unable to set xtalk interrupt resources",
- xconn_vhdl);
- /* yes, we leak resources here. */
- return 0;
- }
- #endif
- }
- }
- pcibr_intr->bi_ibits |= 1 << pcibr_int_bit;
- NEW(intr_entry);
- intr_entry->il_next = NULL;
- intr_entry->il_intr = pcibr_intr;
- intr_entry->il_wrbf = &(bridge->b_wr_req_buf[pciio_slot].reg);
- intr_list_p =
- &pcibr_soft->bs_intr[pcibr_int_bit].bsi_pcibr_intr_wrap.iw_list;
- #if DEBUG && INTR_DEBUG
- #if defined(SUPPORT_PRINTING_V_FORMAT)
- printk("0x%x: Bridge bit %d wrap=0x%xn",
- pconn_vhdl, pcibr_int_bit,
- pcibr_soft->bs_intr[pcibr_int_bit].bsi_pcibr_intr_wrap);
- #else
- printk("%v: Bridge bit %d wrap=0x%xn",
- pconn_vhdl, pcibr_int_bit,
- pcibr_soft->bs_intr[pcibr_int_bit].bsi_pcibr_intr_wrap);
- #endif
- #endif
- if (compare_and_swap_ptr((void **) intr_list_p, NULL, intr_entry)) {
- /* we are the first interrupt on this bridge bit.
- */
- #if DEBUG && INTR_DEBUG
- printk("%v INT 0x%x (bridge bit %d) allocated [FIRST]n",
- pconn_vhdl, pcibr_int_bits, pcibr_int_bit);
- #endif
- continue;
- }
- intr_list = *intr_list_p;
- pcibr_intr_p = &intr_list->il_intr;
- if (compare_and_swap_ptr((void **) pcibr_intr_p, NULL, pcibr_intr)) {
- /* first entry on list was erased,
- * and we replaced it, so we
- * don't need our intr_entry.
- */
- DEL(intr_entry);
- #if DEBUG && INTR_DEBUG
- printk("%v INT 0x%x (bridge bit %d) replaces erased firstn",
- pconn_vhdl, pcibr_int_bits, pcibr_int_bit);
- #endif
- continue;
- }
- intr_list_p = &intr_list->il_next;
- if (compare_and_swap_ptr((void **) intr_list_p, NULL, intr_entry)) {
- /* we are the new second interrupt on this bit.
- */
- pcibr_soft->bs_intr[pcibr_int_bit].bsi_pcibr_intr_wrap.iw_shared = 1;
- #if DEBUG && INTR_DEBUG
- printk("%v INT 0x%x (bridge bit %d) is new SECONDn",
- pconn_vhdl, pcibr_int_bits, pcibr_int_bit);
- #endif
- continue;
- }
- while (1) {
- pcibr_intr_p = &intr_list->il_intr;
- if (compare_and_swap_ptr((void **) pcibr_intr_p, NULL, pcibr_intr)) {
- /* an entry on list was erased,
- * and we replaced it, so we
- * don't need our intr_entry.
- */
- DEL(intr_entry);
- #if DEBUG && INTR_DEBUG
- printk("%v INT 0x%x (bridge bit %d) replaces erased Nthn",
- pconn_vhdl, pcibr_int_bits, pcibr_int_bit);
- #endif
- break;
- }
- intr_list_p = &intr_list->il_next;
- if (compare_and_swap_ptr((void **) intr_list_p, NULL, intr_entry)) {
- /* entry appended to share list
- */
- #if DEBUG && INTR_DEBUG
- printk("%v INT 0x%x (bridge bit %d) is new Nthn",
- pconn_vhdl, pcibr_int_bits, pcibr_int_bit);
- #endif
- break;
- }
- /* step to next record in chain
- */
- intr_list = *intr_list_p;
- }
- }
- }
- #if DEBUG && INTR_DEBUG
- printk("%v pcibr_intr_alloc completen", pconn_vhdl);
- #endif
- hub_intr = (hub_intr_t)xtalk_intr;
- pcibr_intr->bi_irq = hub_intr->i_bit;
- pcibr_intr->bi_cpu = hub_intr->i_cpuid;
- return pcibr_intr;
- }
- /*ARGSUSED */
- void
- pcibr_intr_free(pcibr_intr_t pcibr_intr)
- {
- unsigned pcibr_int_bits = pcibr_intr->bi_ibits;
- pcibr_soft_t pcibr_soft = pcibr_intr->bi_soft;
- unsigned pcibr_int_bit;
- pcibr_intr_list_t intr_list;
- int intr_shared;
- xtalk_intr_t *xtalk_intrp;
- for (pcibr_int_bit = 0; pcibr_int_bit < 8; pcibr_int_bit++) {
- if (pcibr_int_bits & (1 << pcibr_int_bit)) {
- for (intr_list =
- pcibr_soft->bs_intr[pcibr_int_bit].bsi_pcibr_intr_wrap.iw_list;
- intr_list != NULL;
- intr_list = intr_list->il_next)
- if (compare_and_swap_ptr((void **) &intr_list->il_intr,
- pcibr_intr,
- NULL)) {
- #if DEBUG && INTR_DEBUG
- printk("%s: cleared a handler from bit %dn",
- pcibr_soft->bs_name, pcibr_int_bit);
- #endif
- }
- /* If this interrupt line is not being shared between multiple
- * devices release the xtalk interrupt resources.
- */
- intr_shared =
- pcibr_soft->bs_intr[pcibr_int_bit].bsi_pcibr_intr_wrap.iw_shared;
- xtalk_intrp = &pcibr_soft->bs_intr[pcibr_int_bit].bsi_xtalk_intr;
- if ((!intr_shared) && (*xtalk_intrp)) {
- bridge_t *bridge = pcibr_soft->bs_base;
- bridgereg_t int_dev;
- xtalk_intr_free(*xtalk_intrp);
- *xtalk_intrp = 0;
- /* Clear the PCI device interrupt to bridge interrupt pin
- * mapping.
- */
- int_dev = bridge->b_int_device;
- int_dev &= ~BRIDGE_INT_DEV_MASK(pcibr_int_bit);
- bridge->b_int_device = int_dev;
- }
- }
- }
- DEL(pcibr_intr);
- }
- LOCAL void
- pcibr_setpciint(xtalk_intr_t xtalk_intr)
- {
- iopaddr_t addr = xtalk_intr_addr_get(xtalk_intr);
- xtalk_intr_vector_t vect = xtalk_intr_vector_get(xtalk_intr);
- bridgereg_t *int_addr = (bridgereg_t *)
- xtalk_intr_sfarg_get(xtalk_intr);
- #ifdef CONFIG_IA64_SGI_SN2
- *int_addr = ((BRIDGE_INT_ADDR_HOST & (addr >> 26)) |
- (BRIDGE_INT_ADDR_FLD & vect));
- #elif CONFIG_IA64_SGI_SN1
- *int_addr = ((BRIDGE_INT_ADDR_HOST & (addr >> 30)) |
- (BRIDGE_INT_ADDR_FLD & vect));
- #endif
- }
- /*ARGSUSED */
- int
- pcibr_intr_connect(pcibr_intr_t pcibr_intr)
- {
- pcibr_soft_t pcibr_soft = pcibr_intr->bi_soft;
- bridge_t *bridge = pcibr_soft->bs_base;
- unsigned pcibr_int_bits = pcibr_intr->bi_ibits;
- unsigned pcibr_int_bit;
- bridgereg_t b_int_enable;
- unsigned long s;
- if (pcibr_intr == NULL)
- return -1;
- #if DEBUG && INTR_DEBUG
- printk("%v: pcibr_intr_connectn",
- pcibr_intr->bi_dev);
- #endif
- *((volatile unsigned *)&pcibr_intr->bi_flags) |= PCIIO_INTR_CONNECTED;
- /*
- * For each PCI interrupt line requested, figure
- * out which Bridge PCI Interrupt Line it maps
- * to, and make sure there are xtalk resources
- * allocated for it.
- */
- for (pcibr_int_bit = 0; pcibr_int_bit < 8; pcibr_int_bit++)
- if (pcibr_int_bits & (1 << pcibr_int_bit)) {
- xtalk_intr_t xtalk_intr;
- xtalk_intr = pcibr_soft->bs_intr[pcibr_int_bit].bsi_xtalk_intr;
- /*
- * If this interrupt line is being shared and the connect has
- * already been done, no need to do it again.
- */
- if (pcibr_soft->bs_intr[pcibr_int_bit].bsi_pcibr_intr_wrap.iw_connected)
- continue;
- /*
- * Use the pcibr wrapper function to handle all Bridge interrupts
- * regardless of whether the interrupt line is shared or not.
- */
- xtalk_intr_connect(xtalk_intr, (xtalk_intr_setfunc_t) pcibr_setpciint,
- (void *)&(bridge->b_int_addr[pcibr_int_bit].addr));
- pcibr_soft->bs_intr[pcibr_int_bit].bsi_pcibr_intr_wrap.iw_connected = 1;
- #if DEBUG && INTR_DEBUG
- printk("%v bridge bit %d wrapper connectedn",
- pcibr_intr->bi_dev, pcibr_int_bit);
- #endif
- }
- s = pcibr_lock(pcibr_soft);
- b_int_enable = bridge->b_int_enable;
- b_int_enable |= pcibr_int_bits;
- bridge->b_int_enable = b_int_enable;
- bridge->b_wid_tflush; /* wait until Bridge PIO complete */
- pcibr_unlock(pcibr_soft, s);
- return 0;
- }
- /*ARGSUSED */
- void
- pcibr_intr_disconnect(pcibr_intr_t pcibr_intr)
- {
- pcibr_soft_t pcibr_soft = pcibr_intr->bi_soft;
- bridge_t *bridge = pcibr_soft->bs_base;
- unsigned pcibr_int_bits = pcibr_intr->bi_ibits;
- unsigned pcibr_int_bit;
- bridgereg_t b_int_enable;
- unsigned long s;
- /* Stop calling the function. Now.
- */
- *((volatile unsigned *)&pcibr_intr->bi_flags) &= ~PCIIO_INTR_CONNECTED;
- /*
- * For each PCI interrupt line requested, figure
- * out which Bridge PCI Interrupt Line it maps
- * to, and disconnect the interrupt.
- */
- /* don't disable interrupts for lines that
- * are shared between devices.
- */
- for (pcibr_int_bit = 0; pcibr_int_bit < 8; pcibr_int_bit++)
- if ((pcibr_int_bits & (1 << pcibr_int_bit)) &&
- (pcibr_soft->bs_intr[pcibr_int_bit].bsi_pcibr_intr_wrap.iw_shared))
- pcibr_int_bits &= ~(1 << pcibr_int_bit);
- if (!pcibr_int_bits)
- return;
- s = pcibr_lock(pcibr_soft);
- b_int_enable = bridge->b_int_enable;
- b_int_enable &= ~pcibr_int_bits;
- bridge->b_int_enable = b_int_enable;
- bridge->b_wid_tflush; /* wait until Bridge PIO complete */
- pcibr_unlock(pcibr_soft, s);
- for (pcibr_int_bit = 0; pcibr_int_bit < 8; pcibr_int_bit++)
- if (pcibr_int_bits & (1 << pcibr_int_bit)) {
- /* if the interrupt line is now shared,
- * do not disconnect it.
- */
- if (pcibr_soft->bs_intr[pcibr_int_bit].bsi_pcibr_intr_wrap.iw_shared)
- continue;
- xtalk_intr_disconnect(pcibr_soft->bs_intr[pcibr_int_bit].bsi_xtalk_intr);
- pcibr_soft->bs_intr[pcibr_int_bit].bsi_pcibr_intr_wrap.iw_connected = 0;
- #if DEBUG && INTR_DEBUG
- printk("%s: xtalk disconnect done for Bridge bit %dn",
- pcibr_soft->bs_name, pcibr_int_bit);
- #endif
- /* if we are sharing the interrupt line,
- * connect us up; this closes the hole
- * where the another pcibr_intr_alloc()
- * was in progress as we disconnected.
- */
- if (!pcibr_soft->bs_intr[pcibr_int_bit].bsi_pcibr_intr_wrap.iw_shared)
- continue;
- xtalk_intr_connect(pcibr_soft->bs_intr[pcibr_int_bit].bsi_xtalk_intr,
- (xtalk_intr_setfunc_t)pcibr_setpciint,
- (void *) &(bridge->b_int_addr[pcibr_int_bit].addr));
- }
- }
- /*ARGSUSED */
- devfs_handle_t
- pcibr_intr_cpu_get(pcibr_intr_t pcibr_intr)
- {
- pcibr_soft_t pcibr_soft = pcibr_intr->bi_soft;
- unsigned pcibr_int_bits = pcibr_intr->bi_ibits;
- unsigned pcibr_int_bit;
- for (pcibr_int_bit = 0; pcibr_int_bit < 8; pcibr_int_bit++)
- if (pcibr_int_bits & (1 << pcibr_int_bit))
- return xtalk_intr_cpu_get(pcibr_soft->bs_intr[pcibr_int_bit].bsi_xtalk_intr);
- return 0;
- }
- /* =====================================================================
- * INTERRUPT HANDLING
- */
- LOCAL void
- pcibr_clearwidint(bridge_t *bridge)
- {
- bridge->b_wid_int_upper = 0;
- bridge->b_wid_int_lower = 0;
- }
- LOCAL void
- pcibr_setwidint(xtalk_intr_t intr)
- {
- xwidgetnum_t targ = xtalk_intr_target_get(intr);
- iopaddr_t addr = xtalk_intr_addr_get(intr);
- xtalk_intr_vector_t vect = xtalk_intr_vector_get(intr);
- widgetreg_t NEW_b_wid_int_upper, NEW_b_wid_int_lower;
- widgetreg_t OLD_b_wid_int_upper, OLD_b_wid_int_lower;
- bridge_t *bridge = (bridge_t *)xtalk_intr_sfarg_get(intr);
- NEW_b_wid_int_upper = ( (0x000F0000 & (targ << 16)) |
- XTALK_ADDR_TO_UPPER(addr));
- NEW_b_wid_int_lower = XTALK_ADDR_TO_LOWER(addr);
- OLD_b_wid_int_upper = bridge->b_wid_int_upper;
- OLD_b_wid_int_lower = bridge->b_wid_int_lower;
- /* Verify that all interrupts from this Bridge are using a single PI */
- if ((OLD_b_wid_int_upper != 0) && (OLD_b_wid_int_lower != 0)) {
- /*
- * Once set, these registers shouldn't change; they should
- * be set multiple times with the same values.
- *
- * If we're attempting to change these registers, it means
- * that our heuristics for allocating interrupts in a way
- * appropriate for IP35 have failed, and the admin needs to
- * explicitly direct some interrupts (or we need to make the
- * heuristics more clever).
- *
- * In practice, we hope this doesn't happen very often, if
- * at all.
- */
- if ((OLD_b_wid_int_upper != NEW_b_wid_int_upper) ||
- (OLD_b_wid_int_lower != NEW_b_wid_int_lower)) {
- printk(KERN_WARNING "Interrupt allocation is too complex.n");
- printk(KERN_WARNING "Use explicit administrative interrupt targetting.n");
- printk(KERN_WARNING "bridge=0x%lx targ=0x%xn", (unsigned long)bridge, targ);
- printk(KERN_WARNING "NEW=0x%x/0x%x OLD=0x%x/0x%xn",
- NEW_b_wid_int_upper, NEW_b_wid_int_lower,
- OLD_b_wid_int_upper, OLD_b_wid_int_lower);
- PRINT_PANIC("PCI Bridge interrupt targetting errorn");
- }
- }
- bridge->b_wid_int_upper = NEW_b_wid_int_upper;
- bridge->b_wid_int_lower = NEW_b_wid_int_lower;
- bridge->b_int_host_err = vect;
- }
- /*
- * pcibr_intr_preset: called during mlreset time
- * if the platform specific code needs to route
- * one of the Bridge's xtalk interrupts before the
- * xtalk infrastructure is available.
- */
- void
- pcibr_xintr_preset(void *which_widget,
- int which_widget_intr,
- xwidgetnum_t targ,
- iopaddr_t addr,
- xtalk_intr_vector_t vect)
- {
- bridge_t *bridge = (bridge_t *) which_widget;
- if (which_widget_intr == -1) {
- /* bridge widget error interrupt */
- bridge->b_wid_int_upper = ( (0x000F0000 & (targ << 16)) |
- XTALK_ADDR_TO_UPPER(addr));
- bridge->b_wid_int_lower = XTALK_ADDR_TO_LOWER(addr);
- bridge->b_int_host_err = vect;
- /* turn on all interrupts except
- * the PCI interrupt requests,
- * at least at heart.
- */
- bridge->b_int_enable |= ~BRIDGE_IMR_INT_MSK;
- } else {
- /* routing a PCI device interrupt.
- * targ and low 38 bits of addr must
- * be the same as the already set
- * value for the widget error interrupt.
- */
- bridge->b_int_addr[which_widget_intr].addr =
- ((BRIDGE_INT_ADDR_HOST & (addr >> 30)) |
- (BRIDGE_INT_ADDR_FLD & vect));
- /*
- * now bridge can let it through;
- * NB: still should be blocked at
- * xtalk provider end, until the service
- * function is set.
- */
- bridge->b_int_enable |= 1 << vect;
- }
- bridge->b_wid_tflush; /* wait until Bridge PIO complete */
- }
- /*
- * pcibr_intr_func()
- *
- * This is the pcibr interrupt "wrapper" function that is called,
- * in interrupt context, to initiate the interrupt handler(s) registered
- * (via pcibr_intr_alloc/connect) for the occuring interrupt. Non-threaded
- * handlers will be called directly, and threaded handlers will have their
- * thread woken up.
- */
- void
- pcibr_intr_func(intr_arg_t arg)
- {
- pcibr_intr_wrap_t wrap = (pcibr_intr_wrap_t) arg;
- reg_p wrbf;
- pcibr_intr_t intr;
- pcibr_intr_list_t list;
- int clearit;
- int do_nonthreaded = 1;
- int is_threaded = 0;
- int x = 0;
- /*
- * If any handler is still running from a previous interrupt
- * just return. If there's a need to call the handler(s) again,
- * another interrupt will be generated either by the device or by
- * pcibr_force_interrupt().
- */
- if (wrap->iw_hdlrcnt) {
- return;
- }
- /*
- * Call all interrupt handlers registered.
- * First, the pcibr_intrd threads for any threaded handlers will be
- * awoken, then any non-threaded handlers will be called sequentially.
- */
-
- clearit = 1;
- while (do_nonthreaded) {
- for (list = wrap->iw_list; list != NULL; list = list->il_next) {
- if ((intr = list->il_intr) &&
- (intr->bi_flags & PCIIO_INTR_CONNECTED)) {
- /*
- * This device may have initiated write
- * requests since the bridge last saw
- * an edge on this interrupt input; flushing
- * the buffer prior to invoking the handler
- * should help but may not be sufficient if we
- * get more requests after the flush, followed
- * by the card deciding it wants service, before
- * the interrupt handler checks to see if things need
- * to be done.
- *
- * There is a similar race condition if
- * an interrupt handler loops around and
- * notices further service is required.
- * Perhaps we need to have an explicit
- * call that interrupt handlers need to
- * do between noticing that DMA to memory
- * has completed, but before observing the
- * contents of memory?
- */
- if ((do_nonthreaded) && (!is_threaded)) {
- /* Non-threaded.
- * Call the interrupt handler at interrupt level
- */
- /* Only need to flush write buffers if sharing */
- if ((wrap->iw_shared) && (wrbf = list->il_wrbf)) {
- if ((x = *wrbf)) /* write request buffer flush */
- #ifdef SUPPORT_PRINTING_V_FORMAT
- printk(KERN_ALERT "pcibr_intr_func %v: n"
- "write buffer flush failed, wrbf=0x%xn",
- list->il_intr->bi_dev, wrbf);
- #else
- printk(KERN_ALERT "pcibr_intr_func %p: n"
- "write buffer flush failed, wrbf=0x%lxn",
- (void *)list->il_intr->bi_dev, (long) wrbf);
- #endif
- }
- }
- clearit = 0;
- }
- }
- do_nonthreaded = 0;
- /*
- * If the non-threaded handler was the last to complete,
- * (i.e., no threaded handlers still running) force an
- * interrupt to avoid a potential deadlock situation.
- */
- if (wrap->iw_hdlrcnt == 0) {
- pcibr_force_interrupt(wrap);
- }
- }
- /* If there were no handlers,
- * disable the interrupt and return.
- * It will get enabled again after
- * a handler is connected.
- * If we don't do this, we would
- * sit here and spin through the
- * list forever.
- */
- if (clearit) {
- pcibr_soft_t pcibr_soft = wrap->iw_soft;
- bridge_t *bridge = pcibr_soft->bs_base;
- bridgereg_t b_int_enable;
- bridgereg_t mask = 1 << wrap->iw_intr;
- unsigned long s;
- s = pcibr_lock(pcibr_soft);
- b_int_enable = bridge->b_int_enable;
- b_int_enable &= ~mask;
- bridge->b_int_enable = b_int_enable;
- bridge->b_wid_tflush; /* wait until Bridge PIO complete */
- pcibr_unlock(pcibr_soft, s);
- return;
- }
- }
- /* =====================================================================
- * CONFIGURATION MANAGEMENT
- */
- /*ARGSUSED */
- void
- pcibr_provider_startup(devfs_handle_t pcibr)
- {
- }
- /*ARGSUSED */
- void
- pcibr_provider_shutdown(devfs_handle_t pcibr)
- {
- }
- int
- pcibr_reset(devfs_handle_t conn)
- {
- pciio_info_t pciio_info = pciio_info_get(conn);
- pciio_slot_t pciio_slot = pciio_info_slot_get(pciio_info);
- pcibr_soft_t pcibr_soft = (pcibr_soft_t) pciio_info_mfast_get(pciio_info);
- bridge_t *bridge = pcibr_soft->bs_base;
- bridgereg_t ctlreg;
- unsigned cfgctl[8];
- unsigned long s;
- int f, nf;
- pcibr_info_h pcibr_infoh;
- pcibr_info_t pcibr_info;
- int win;
- if (pcibr_soft->bs_slot[pciio_slot].has_host) {
- pciio_slot = pcibr_soft->bs_slot[pciio_slot].host_slot;
- pcibr_info = pcibr_soft->bs_slot[pciio_slot].bss_infos[0];
- }
- if (pciio_slot < 4) {
- s = pcibr_lock(pcibr_soft);
- nf = pcibr_soft->bs_slot[pciio_slot].bss_ninfo;
- pcibr_infoh = pcibr_soft->bs_slot[pciio_slot].bss_infos;
- for (f = 0; f < nf; ++f)
- if (pcibr_infoh[f])
- cfgctl[f] = bridge->b_type0_cfg_dev[pciio_slot].f[f].l[PCI_CFG_COMMAND / 4];
- ctlreg = bridge->b_wid_control;
- bridge->b_wid_control = ctlreg | BRIDGE_CTRL_RST(pciio_slot);
- /* XXX delay? */
- bridge->b_wid_control = ctlreg;
- /* XXX delay? */
- for (f = 0; f < nf; ++f)
- if ((pcibr_info = pcibr_infoh[f]))
- for (win = 0; win < 6; ++win)
- if (pcibr_info->f_window[win].w_base != 0)
- bridge->b_type0_cfg_dev[pciio_slot].f[f].l[PCI_CFG_BASE_ADDR(win) / 4] =
- pcibr_info->f_window[win].w_base;
- for (f = 0; f < nf; ++f)
- if (pcibr_infoh[f])
- bridge->b_type0_cfg_dev[pciio_slot].f[f].l[PCI_CFG_COMMAND / 4] = cfgctl[f];
- pcibr_unlock(pcibr_soft, s);
- return 0;
- }
- #ifdef SUPPORT_PRINTING_V_FORMAT
- printk(KERN_WARNING "%v: pcibr_reset unimplemented for slot %dn",
- conn, pciio_slot);
- #endif
- return -1;
- }
- pciio_endian_t
- pcibr_endian_set(devfs_handle_t pconn_vhdl,
- pciio_endian_t device_end,
- pciio_endian_t desired_end)
- {
- pciio_info_t pciio_info = pciio_info_get(pconn_vhdl);
- pciio_slot_t pciio_slot = pciio_info_slot_get(pciio_info);
- pcibr_soft_t pcibr_soft = (pcibr_soft_t) pciio_info_mfast_get(pciio_info);
- bridgereg_t devreg;
- unsigned long s;
- /*
- * Bridge supports hardware swapping; so we can always
- * arrange for the caller's desired endianness.
- */
- s = pcibr_lock(pcibr_soft);
- devreg = pcibr_soft->bs_slot[pciio_slot].bss_device;
- if (device_end != desired_end)
- devreg |= BRIDGE_DEV_SWAP_BITS;
- else
- devreg &= ~BRIDGE_DEV_SWAP_BITS;
- /* NOTE- if we ever put SWAP bits
- * onto the disabled list, we will
- * have to change the logic here.
- */
- if (pcibr_soft->bs_slot[pciio_slot].bss_device != devreg) {
- bridge_t *bridge = pcibr_soft->bs_base;
- bridge->b_device[pciio_slot].reg = devreg;
- pcibr_soft->bs_slot[pciio_slot].bss_device = devreg;
- bridge->b_wid_tflush; /* wait until Bridge PIO complete */
- }
- pcibr_unlock(pcibr_soft, s);
- #if DEBUG && PCIBR_DEV_DEBUG
- printk("pcibr Device(%d): 0x%pn", pciio_slot, bridge->b_device[pciio_slot].reg);
- #endif
- return desired_end;
- }
- /* This (re)sets the GBR and REALTIME bits and also keeps track of how
- * many sets are outstanding. Reset succeeds only if the number of outstanding
- * sets == 1.
- */
- int
- pcibr_priority_bits_set(pcibr_soft_t pcibr_soft,
- pciio_slot_t pciio_slot,
- pciio_priority_t device_prio)
- {
- unsigned long s;
- int *counter;
- bridgereg_t rtbits = 0;
- bridgereg_t devreg;
- int rc = PRIO_SUCCESS;
- /* in dual-slot configurations, the host and the
- * guest have separate DMA resources, so they
- * have separate requirements for priority bits.
- */
- counter = &(pcibr_soft->bs_slot[pciio_slot].bss_pri_uctr);
- /*
- * Bridge supports PCI notions of LOW and HIGH priority
- * arbitration rings via a "REAL_TIME" bit in the per-device
- * Bridge register. The "GBR" bit controls access to the GBR
- * ring on the xbow. These two bits are (re)set together.
- *
- * XXX- Bug in Rev B Bridge Si:
- * Symptom: Prefetcher starts operating incorrectly. This happens
- * due to corruption of the address storage ram in the prefetcher
- * when a non-real time PCI request is pulled and a real-time one is
- * put in it's place. Workaround: Use only a single arbitration ring
- * on PCI bus. GBR and RR can still be uniquely used per
- * device. NETLIST MERGE DONE, WILL BE FIXED IN REV C.
- */
- if (pcibr_soft->bs_rev_num != BRIDGE_PART_REV_B)
- rtbits |= BRIDGE_DEV_RT;
- /* NOTE- if we ever put DEV_RT or DEV_GBR on
- * the disabled list, we will have to take
- * it into account here.
- */
- s = pcibr_lock(pcibr_soft);
- devreg = pcibr_soft->bs_slot[pciio_slot].bss_device;
- if (device_prio == PCI_PRIO_HIGH) {
- if ((++*counter == 1)) {
- if (rtbits)
- devreg |= rtbits;
- else
- rc = PRIO_FAIL;
- }
- } else if (device_prio == PCI_PRIO_LOW) {
- if (*counter <= 0)
- rc = PRIO_FAIL;
- else if (--*counter == 0)
- if (rtbits)
- devreg &= ~rtbits;
- }
- if (pcibr_soft->bs_slot[pciio_slot].bss_device != devreg) {
- bridge_t *bridge = pcibr_soft->bs_base;
- bridge->b_device[pciio_slot].reg = devreg;
- pcibr_soft->bs_slot[pciio_slot].bss_device = devreg;
- bridge->b_wid_tflush; /* wait until Bridge PIO complete */
- }
- pcibr_unlock(pcibr_soft, s);
- return rc;
- }
- pciio_priority_t
- pcibr_priority_set(devfs_handle_t pconn_vhdl,
- pciio_priority_t device_prio)
- {
- pciio_info_t pciio_info = pciio_info_get(pconn_vhdl);
- pciio_slot_t pciio_slot = pciio_info_slot_get(pciio_info);
- pcibr_soft_t pcibr_soft = (pcibr_soft_t) pciio_info_mfast_get(pciio_info);
- (void) pcibr_priority_bits_set(pcibr_soft, pciio_slot, device_prio);
- return device_prio;
- }
- /*
- * Interfaces to allow special (e.g. SGI) drivers to set/clear
- * Bridge-specific device flags. Many flags are modified through
- * PCI-generic interfaces; we don't allow them to be directly
- * manipulated here. Only flags that at this point seem pretty
- * Bridge-specific can be set through these special interfaces.
- * We may add more flags as the need arises, or remove flags and
- * create PCI-generic interfaces as the need arises.
- *
- * Returns 0 on failure, 1 on success
- */
- int
- pcibr_device_flags_set(devfs_handle_t pconn_vhdl,
- pcibr_device_flags_t flags)
- {
- pciio_info_t pciio_info = pciio_info_get(pconn_vhdl);
- pciio_slot_t pciio_slot = pciio_info_slot_get(pciio_info);
- pcibr_soft_t pcibr_soft = (pcibr_soft_t) pciio_info_mfast_get(pciio_info);
- bridgereg_t set = 0;
- bridgereg_t clr = 0;
- ASSERT((flags & PCIBR_DEVICE_FLAGS) == flags);
- if (flags & PCIBR_WRITE_GATHER)
- set |= BRIDGE_DEV_PMU_WRGA_EN;
- if (flags & PCIBR_NOWRITE_GATHER)
- clr |= BRIDGE_DEV_PMU_WRGA_EN;
- if (flags & PCIBR_WRITE_GATHER)
- set |= BRIDGE_DEV_DIR_WRGA_EN;
- if (flags & PCIBR_NOWRITE_GATHER)
- clr |= BRIDGE_DEV_DIR_WRGA_EN;
- if (flags & PCIBR_PREFETCH)
- set |= BRIDGE_DEV_PREF;
- if (flags & PCIBR_NOPREFETCH)
- clr |= BRIDGE_DEV_PREF;
- if (flags & PCIBR_PRECISE)
- set |= BRIDGE_DEV_PRECISE;
- if (flags & PCIBR_NOPRECISE)
- clr |= BRIDGE_DEV_PRECISE;
- if (flags & PCIBR_BARRIER)
- set |= BRIDGE_DEV_BARRIER;
- if (flags & PCIBR_NOBARRIER)
- clr |= BRIDGE_DEV_BARRIER;
- if (flags & PCIBR_64BIT)
- set |= BRIDGE_DEV_DEV_SIZE;
- if (flags & PCIBR_NO64BIT)
- clr |= BRIDGE_DEV_DEV_SIZE;
- if (set || clr) {
- bridgereg_t devreg;
- unsigned long s;
- s = pcibr_lock(pcibr_soft);
- devreg = pcibr_soft->bs_slot[pciio_slot].bss_device;
- devreg = (devreg & ~clr) | set;
- if (pcibr_soft->bs_slot[pciio_slot].bss_device != devreg) {
- bridge_t *bridge = pcibr_soft->bs_base;
- bridge->b_device[pciio_slot].reg = devreg;
- pcibr_soft->bs_slot[pciio_slot].bss_device = devreg;
- bridge->b_wid_tflush; /* wait until Bridge PIO complete */
- }
- pcibr_unlock(pcibr_soft, s);
- #if DEBUG && PCIBR_DEV_DEBUG
- printk("pcibr Device(%d): %Rn", pciio_slot, bridge->b_device[pciio_slot].regbridge->b_device[pciio_slot].reg, device_bits);
- #endif
- }
- return (1);
- }
- #ifdef LITTLE_ENDIAN
- /*
- * on sn-ia we need to twiddle the the addresses going out
- * the pci bus because we use the unswizzled synergy space
- * (the alternative is to use the swizzled synergy space
- * and byte swap the data)
- */
- #define CB(b,r) (((volatile uint8_t *) b)[((r)^4)])
- #define CS(b,r) (((volatile uint16_t *) b)[((r^4)/2)])
- #define CW(b,r) (((volatile uint32_t *) b)[((r^4)/4)])
- #else
- #define CB(b,r) (((volatile uint8_t *) cfgbase)[(r)^3])
- #define CS(b,r) (((volatile uint16_t *) cfgbase)[((r)/2)^1])
- #define CW(b,r) (((volatile uint32_t *) cfgbase)[(r)/4])
- #endif /* LITTLE_ENDIAN */
- LOCAL cfg_p
- pcibr_config_addr(devfs_handle_t conn,
- unsigned reg)
- {
- pcibr_info_t pcibr_info;
- pciio_slot_t pciio_slot;
- pciio_function_t pciio_func;
- pcibr_soft_t pcibr_soft;
- bridge_t *bridge;
- cfg_p cfgbase = (cfg_p)0;
- pcibr_info = pcibr_info_get(conn);
- pciio_slot = pcibr_info->f_slot;
- if (pciio_slot == PCIIO_SLOT_NONE)
- pciio_slot = PCI_TYPE1_SLOT(reg);
- pciio_func = pcibr_info->f_func;
- if (pciio_func == PCIIO_FUNC_NONE)
- pciio_func = PCI_TYPE1_FUNC(reg);
- pcibr_soft = (pcibr_soft_t) pcibr_info->f_mfast;
- bridge = pcibr_soft->bs_base;
- cfgbase = bridge->b_type0_cfg_dev[pciio_slot].f[pciio_func].l;
- return cfgbase;
- }
- uint64_t
- pcibr_config_get(devfs_handle_t conn,
- unsigned reg,
- unsigned size)
- {
- return do_pcibr_config_get(pcibr_config_addr(conn, reg),
- PCI_TYPE1_REG(reg), size);
- }
- LOCAL uint64_t
- do_pcibr_config_get(
- cfg_p cfgbase,
- unsigned reg,
- unsigned size)
- {
- unsigned value;
-
- value = CW(cfgbase, reg);
- if (reg & 3)
- value >>= 8 * (reg & 3);
- if (size < 4)
- value &= (1 << (8 * size)) - 1;
- return value;
- }
- void
- pcibr_config_set(devfs_handle_t conn,
- unsigned reg,
- unsigned size,
- uint64_t value)
- {
- do_pcibr_config_set(pcibr_config_addr(conn, reg),
- PCI_TYPE1_REG(reg), size, value);
- }
- LOCAL void
- do_pcibr_config_set(cfg_p cfgbase,
- unsigned reg,
- unsigned size,
- uint64_t value)
- {
- switch (size) {
- case 1:
- CB(cfgbase, reg) = value;
- break;
- case 2:
- if (reg & 1) {
- CB(cfgbase, reg) = value;
- CB(cfgbase, reg + 1) = value >> 8;
- } else
- CS(cfgbase, reg) = value;
- break;
- case 3:
- if (reg & 1) {
- CB(cfgbase, reg) = value;
- CS(cfgbase, (reg + 1)) = value >> 8;
- } else {
- CS(cfgbase, reg) = value;
- CB(cfgbase, reg + 2) = value >> 16;
- }
- break;
- case 4:
- CW(cfgbase, reg) = value;
- break;
- }
- }
- pciio_provider_t pcibr_provider =
- {
- (pciio_piomap_alloc_f *) pcibr_piomap_alloc,
- (pciio_piomap_free_f *) pcibr_piomap_free,
- (pciio_piomap_addr_f *) pcibr_piomap_addr,
- (pciio_piomap_done_f *) pcibr_piomap_done,
- (pciio_piotrans_addr_f *) pcibr_piotrans_addr,
- (pciio_piospace_alloc_f *) pcibr_piospace_alloc,
- (pciio_piospace_free_f *) pcibr_piospace_free,
- (pciio_dmamap_alloc_f *) pcibr_dmamap_alloc,
- (pciio_dmamap_free_f *) pcibr_dmamap_free,
- (pciio_dmamap_addr_f *) pcibr_dmamap_addr,
- (pciio_dmamap_list_f *) pcibr_dmamap_list,
- (pciio_dmamap_done_f *) pcibr_dmamap_done,
- (pciio_dmatrans_addr_f *) pcibr_dmatrans_addr,
- (pciio_dmatrans_list_f *) pcibr_dmatrans_list,
- (pciio_dmamap_drain_f *) pcibr_dmamap_drain,
- (pciio_dmaaddr_drain_f *) pcibr_dmaaddr_drain,
- (pciio_dmalist_drain_f *) pcibr_dmalist_drain,
- (pciio_intr_alloc_f *) pcibr_intr_alloc,
- (pciio_intr_free_f *) pcibr_intr_free,
- (pciio_intr_connect_f *) pcibr_intr_connect,
- (pciio_intr_disconnect_f *) pcibr_intr_disconnect,
- (pciio_intr_cpu_get_f *) pcibr_intr_cpu_get,
- (pciio_provider_startup_f *) pcibr_provider_startup,
- (pciio_provider_shutdown_f *) pcibr_provider_shutdown,
- (pciio_reset_f *) pcibr_reset,
- (pciio_write_gather_flush_f *) pcibr_write_gather_flush,
- (pciio_endian_set_f *) pcibr_endian_set,
- (pciio_priority_set_f *) pcibr_priority_set,
- (pciio_config_get_f *) pcibr_config_get,
- (pciio_config_set_f *) pcibr_config_set,
- (pciio_error_devenable_f *) 0,
- (pciio_error_extract_f *) 0,
- #ifdef LATER
- (pciio_driver_reg_callback_f *) pcibr_driver_reg_callback,
- (pciio_driver_unreg_callback_f *) pcibr_driver_unreg_callback,
- #else
- (pciio_driver_reg_callback_f *) 0,
- (pciio_driver_unreg_callback_f *) 0,
- #endif
- (pciio_device_unregister_f *) pcibr_device_unregister,
- (pciio_dma_enabled_f *) pcibr_dma_enabled,
- };
- LOCAL pcibr_hints_t
- pcibr_hints_get(devfs_handle_t xconn_vhdl, int alloc)
- {
- arbitrary_info_t ainfo = 0;
- graph_error_t rv;
- pcibr_hints_t hint;
- rv = hwgraph_info_get_LBL(xconn_vhdl, INFO_LBL_PCIBR_HINTS, &ainfo);
- if (alloc && (rv != GRAPH_SUCCESS)) {
- NEW(hint);
- hint->rrb_alloc_funct = NULL;
- hint->ph_intr_bits = NULL;
- rv = hwgraph_info_add_LBL(xconn_vhdl,
- INFO_LBL_PCIBR_HINTS,
- (arbitrary_info_t) hint);
- if (rv != GRAPH_SUCCESS)
- goto abnormal_exit;
- rv = hwgraph_info_get_LBL(xconn_vhdl, INFO_LBL_PCIBR_HINTS, &ainfo);
-
- if (rv != GRAPH_SUCCESS)
- goto abnormal_exit;
- if (ainfo != (arbitrary_info_t) hint)
- goto abnormal_exit;
- }
- return (pcibr_hints_t) ainfo;
- abnormal_exit:
- #ifdef LATER
- printf("SHOULD NOT BE HEREn");
- #endif
- DEL(hint);
- return(NULL);
- }
- void
- pcibr_hints_fix_some_rrbs(devfs_handle_t xconn_vhdl, unsigned mask)
- {
- pcibr_hints_t hint = pcibr_hints_get(xconn_vhdl, 1);
- if (hint)
- hint->ph_rrb_fixed = mask;
- #if DEBUG
- else
- printk("pcibr_hints_fix_rrbs: pcibr_hints_get failed atn"
- "t%pn", xconn_vhdl);
- #endif
- }
- void
- pcibr_hints_fix_rrbs(devfs_handle_t xconn_vhdl)
- {
- pcibr_hints_fix_some_rrbs(xconn_vhdl, 0xFF);
- }
- void
- pcibr_hints_dualslot(devfs_handle_t xconn_vhdl,
- pciio_slot_t host,
- pciio_slot_t guest)
- {
- pcibr_hints_t hint = pcibr_hints_get(xconn_vhdl, 1);
- if (hint)
- hint->ph_host_slot[guest] = host + 1;
- #if DEBUG
- else
- printk("pcibr_hints_dualslot: pcibr_hints_get failed atn"
- "t%pn", xconn_vhdl);
- #endif
- }
- void
- pcibr_hints_intr_bits(devfs_handle_t xconn_vhdl,
- pcibr_intr_bits_f *xxx_intr_bits)
- {
- pcibr_hints_t hint = pcibr_hints_get(xconn_vhdl, 1);
- if (hint)
- hint->ph_intr_bits = xxx_intr_bits;
- #if DEBUG
- else
- printk("pcibr_hints_intr_bits: pcibr_hints_get failed atn"
- "t%pn", xconn_vhdl);
- #endif
- }
- void
- pcibr_set_rrb_callback(devfs_handle_t xconn_vhdl, rrb_alloc_funct_t rrb_alloc_funct)
- {
- pcibr_hints_t hint = pcibr_hints_get(xconn_vhdl, 1);
- if (hint)
- hint->rrb_alloc_funct = rrb_alloc_funct;
- }
- void
- pcibr_hints_handsoff(devfs_handle_t xconn_vhdl)
- {
- pcibr_hints_t hint = pcibr_hints_get(xconn_vhdl, 1);
- if (hint)
- hint->ph_hands_off = 1;
- #if DEBUG
- else
- printk("pcibr_hints_handsoff: pcibr_hints_get failed atn"
- "t%pn", xconn_vhdl);
- #endif
- }
- void
- pcibr_hints_subdevs(devfs_handle_t xconn_vhdl,
- pciio_slot_t slot,
- uint64_t subdevs)
- {
- arbitrary_info_t ainfo = 0;
- char sdname[16];
- devfs_handle_t pconn_vhdl = GRAPH_VERTEX_NONE;
- sprintf(sdname, "pci/%d", slot);
- (void) hwgraph_path_add(xconn_vhdl, sdname, &pconn_vhdl);
- if (pconn_vhdl == GRAPH_VERTEX_NONE) {
- #if DEBUG
- printk("pcibr_hints_subdevs: hwgraph_path_create failed atn"
- "t%p (seeking %s)n", xconn_vhdl, sdname);
- #endif
- return;
- }
- hwgraph_info_get_LBL(pconn_vhdl, INFO_LBL_SUBDEVS, &ainfo);
- if (ainfo == 0) {
- uint64_t *subdevp;
- NEW(subdevp);
- if (!subdevp) {
- #if DEBUG
- printk("pcibr_hints_subdevs: subdev ptr alloc failed atn"
- "t%pn", pconn_vhdl);
- #endif
- return;
- }
- *subdevp = subdevs;
- hwgraph_info_add_LBL(pconn_vhdl, INFO_LBL_SUBDEVS, (arbitrary_info_t) subdevp);
- hwgraph_info_get_LBL(pconn_vhdl, INFO_LBL_SUBDEVS, &ainfo);
- if (ainfo == (arbitrary_info_t) subdevp)
- return;
- DEL(subdevp);
- if (ainfo == (arbitrary_info_t) NULL) {
- #if DEBUG
- printk("pcibr_hints_subdevs: null subdevs ptr atn"
- "t%pn", pconn_vhdl);
- #endif
- return;
- }
- #if DEBUG
- printk("pcibr_subdevs_get: dup subdev add_LBL atn"
- "t%pn", pconn_vhdl);
- #endif
- }
- *(uint64_t *) ainfo = subdevs;
- }
- #ifdef LATER
- #include <sys/idbg.h>
- #include <sys/idbgentry.h>
- char *pci_space[] = {"NONE",
- "ROM",
- "IO",
- "",
- "MEM",
- "MEM32",
- "MEM64",
- "CFG",
- "WIN0",
- "WIN1",
- "WIN2",
- "WIN3",
- "WIN4",
- "WIN5",
- "",
- "BAD"};
- void
- idbg_pss_func(pcibr_info_h pcibr_infoh, int func)
- {
- pcibr_info_t pcibr_info = pcibr_infoh[func];
- char name[MAXDEVNAME];
- int win;
-
- if (!pcibr_info)
- return;
- qprintf("Per-slot Function Infon");
- #ifdef SUPPORT_PRINTING_V_FORMAT
- sprintf(name, "%v", pcibr_info->f_vertex);
- #endif
- qprintf("tSlot Name : %sn",name);
- qprintf("tPCI Bus : %d ",pcibr_info->f_bus);
- qprintf("Slot : %d ", pcibr_info->f_slot);
- qprintf("Function : %d ", pcibr_info->f_func);
- qprintf("VendorId : 0x%x " , pcibr_info->f_vendor);
- qprintf("DeviceId : 0x%xn", pcibr_info->f_device);
- #ifdef SUPPORT_PRINTING_V_FORMAT
- sprintf(name, "%v", pcibr_info->f_master);
- #endif
- qprintf("tBus provider : %sn",name);
- qprintf("tProvider Fns : 0x%x ", pcibr_info->f_pops);
- qprintf("Error Handler : 0x%x Arg 0x%xn",
- pcibr_info->f_efunc,pcibr_info->f_einfo);
- for(win = 0 ; win < 6 ; win++)
- qprintf("tBase Reg #%d space %s base 0x%x size 0x%xn",
- win,pci_space[pcibr_info->f_window[win].w_space],
- pcibr_info->f_window[win].w_base,
- pcibr_info->f_window[win].w_size);
- qprintf("tRom base 0x%x size 0x%xn",
- pcibr_info->f_rbase,pcibr_info->f_rsize);
- qprintf("tInterrupt Bit Mapn");
- qprintf("ttPCI Int#tBridge Pin#n");
- for (win = 0 ; win < 4; win++)
- qprintf("ttINT%ctt%dn",win+'A',pcibr_info->f_ibit[win]);
- qprintf("n");
- }
- void
- idbg_pss_info(pcibr_soft_t pcibr_soft, pciio_slot_t slot)
- {
- pcibr_soft_slot_t pss;
- char slot_conn_name[MAXDEVNAME];
- int func;
- pss = &pcibr_soft->bs_slot[slot];
- qprintf("PCI INFRASTRUCTURAL INFO FOR SLOT %dn", slot);
- qprintf("tHost Present ? %s ", pss->has_host ? "yes" : "no");
- qprintf("tHost Slot : %dn",pss->host_slot);
- sprintf(slot_conn_name, "%v", pss->slot_conn);
- qprintf("tSlot Conn : %sn",slot_conn_name);
- qprintf("t#Functions : %dn",pss->bss_ninfo);
- for (func = 0; func < pss->bss_ninfo; func++)
- idbg_pss_func(pss->bss_infos,func);
- qprintf("tSpace : %s ",pci_space[pss->bss_devio.bssd_space]);
- qprintf("tBase : 0x%x ", pss->bss_devio.bssd_base);
- qprintf("tShadow Devreg : 0x%xn", pss->bss_device);
- qprintf("tUsage counts : pmu %d d32 %d d64 %dn",
- pss->bss_pmu_uctr,pss->bss_d32_uctr,pss->bss_d64_uctr);
-
- qprintf("tDirect Trans Info : d64_base 0x%x d64_flags 0x%x"
- "d32_base 0x%x d32_flags 0x%xn",
- pss->bss_d64_base, pss->bss_d64_flags,
- pss->bss_d32_base, pss->bss_d32_flags);
-
- qprintf("tExt ATEs active ? %s",
- atomic_read(&pss->bss_ext_ates_active) ? "yes" : "no");
- qprintf(" Command register : 0x%x ", pss->bss_cmd_pointer);
- qprintf(" Shadow command val : 0x%xn", pss->bss_cmd_shadow);
- qprintf("tRRB Info : Valid %d+%d Reserved %dn",
- pcibr_soft->bs_rrb_valid[slot],
- pcibr_soft->bs_rrb_valid[slot + PCIBR_RRB_SLOT_VIRTUAL],
- pcibr_soft->bs_rrb_res[slot]);
-
- }
- int ips = 0;
- void
- idbg_pss(pcibr_soft_t pcibr_soft)
- {
- pciio_slot_t slot;
-
- if (ips >= 0 && ips < 8)
- idbg_pss_info(pcibr_soft,ips);
- else if (ips < 0)
- for (slot = 0; slot < 8; slot++)
- idbg_pss_info(pcibr_soft,slot);
- else
- qprintf("Invalid ips %dn",ips);
- }
- #endif /* LATER */
- int
- pcibr_dma_enabled(devfs_handle_t pconn_vhdl)
- {
- pciio_info_t pciio_info = pciio_info_get(pconn_vhdl);
- pcibr_soft_t pcibr_soft = (pcibr_soft_t) pciio_info_mfast_get(pciio_info);
-
- return xtalk_dma_enabled(pcibr_soft->bs_conn);
- }