patch-2.4.19 linux-2.4.19/arch/ia64/sn/io/sn2/pcibr/pcibr_rrb.c

Next file: linux-2.4.19/arch/ia64/sn/io/sn2/pcibr/pcibr_slot.c
Previous file: linux-2.4.19/arch/ia64/sn/io/sn2/pcibr/pcibr_intr.c
Back to the patch index
Back to the overall index

diff -urN linux-2.4.18/arch/ia64/sn/io/sn2/pcibr/pcibr_rrb.c linux-2.4.19/arch/ia64/sn/io/sn2/pcibr/pcibr_rrb.c
@@ -0,0 +1,896 @@
+/*
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2001-2002 Silicon Graphics, Inc. All rights reserved.
+ */
+
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <asm/sn/sgi.h>
+#include <asm/sn/sn_cpuid.h>
+#include <asm/sn/addrs.h>
+#include <asm/sn/arch.h>
+#include <asm/sn/iograph.h>
+#include <asm/sn/invent.h>
+#include <asm/sn/hcl.h>
+#include <asm/sn/labelcl.h>
+#include <asm/sn/xtalk/xwidget.h>
+#include <asm/sn/pci/bridge.h>
+#include <asm/sn/pci/pciio.h>
+#include <asm/sn/pci/pcibr.h>
+#include <asm/sn/pci/pcibr_private.h>
+#include <asm/sn/pci/pci_defs.h>
+#include <asm/sn/prio.h>
+#include <asm/sn/xtalk/xbow.h>
+#include <asm/sn/ioc3.h>
+#include <asm/sn/eeprom.h>
+#include <asm/sn/io.h>
+#include <asm/sn/sn_private.h>
+
+void              do_pcibr_rrb_clear(bridge_t *, int);
+void              do_pcibr_rrb_flush(bridge_t *, int);
+int               do_pcibr_rrb_count_valid(bridge_t *, pciio_slot_t);
+int               do_pcibr_rrb_count_avail(bridge_t *, pciio_slot_t);
+int               do_pcibr_rrb_alloc(bridge_t *, pciio_slot_t, int);
+int               do_pcibr_rrb_free(bridge_t *, pciio_slot_t, int);
+
+void              do_pcibr_rrb_autoalloc(pcibr_soft_t, int, int);
+
+int		  pcibr_wrb_flush(devfs_handle_t);
+int               pcibr_rrb_alloc(devfs_handle_t, int *, int *);
+int               pcibr_rrb_check(devfs_handle_t, int *, int *, int *, int *);
+int               pcibr_alloc_all_rrbs(devfs_handle_t, int, int, int, int, int, int, int, int, int);
+void              pcibr_rrb_flush(devfs_handle_t);
+int		  pcibr_slot_initial_rrb_alloc(devfs_handle_t,pciio_slot_t);
+
+/* 
+ *    RRB Management
+ */
+
+#define LSBIT(word)		((word) &~ ((word)-1))
+
+void
+do_pcibr_rrb_clear(bridge_t *bridge, int rrb)
+{
+    bridgereg_t             status;
+
+    /* bridge_lock must be held;
+     * this RRB must be disabled.
+     */
+
+    /* wait until RRB has no outstanduing XIO packets. */
+    while ((status = bridge->b_resp_status) & BRIDGE_RRB_INUSE(rrb)) {
+	;				/* XXX- beats on bridge. bad idea? */
+    }
+
+    /* if the RRB has data, drain it. */
+    if (status & BRIDGE_RRB_VALID(rrb)) {
+	bridge->b_resp_clear = BRIDGE_RRB_CLEAR(rrb);
+
+	/* wait until RRB is no longer valid. */
+	while ((status = bridge->b_resp_status) & BRIDGE_RRB_VALID(rrb)) {
+	    ;				/* XXX- beats on bridge. bad idea? */
+	}
+    }
+}
+
+void
+do_pcibr_rrb_flush(bridge_t *bridge, int rrbn)
+{
+    reg_p                   rrbp = &bridge->b_rrb_map[rrbn & 1].reg;
+    bridgereg_t             rrbv;
+    int                     shft = 4 * (rrbn >> 1);
+    unsigned                ebit = BRIDGE_RRB_EN << shft;
+
+    rrbv = *rrbp;
+    if (rrbv & ebit)
+	*rrbp = rrbv & ~ebit;
+
+    do_pcibr_rrb_clear(bridge, rrbn);
+
+    if (rrbv & ebit)
+	*rrbp = rrbv;
+}
+
+/*
+ *    pcibr_rrb_count_valid: count how many RRBs are
+ *      marked valid for the specified PCI slot on this
+ *      bridge.
+ *
+ *      NOTE: The "slot" parameter for all pcibr_rrb
+ *      management routines must include the "virtual"
+ *      bit; when manageing both the normal and the
+ *      virtual channel, separate calls to these
+ *      routines must be made. To denote the virtual
+ *      channel, add PCIBR_RRB_SLOT_VIRTUAL to the slot
+ *      number.
+ *
+ *      IMPL NOTE: The obvious algorithm is to iterate
+ *      through the RRB fields, incrementing a count if
+ *      the RRB is valid and matches the slot. However,
+ *      it is much simpler to use an algorithm derived
+ *      from the "partitioned add" idea. First, XOR in a
+ *      pattern such that the fields that match this
+ *      slot come up "all ones" and all other fields
+ *      have zeros in the mismatching bits. Then AND
+ *      together the bits in the field, so we end up
+ *      with one bit turned on for each field that
+ *      matched. Now we need to count these bits. This
+ *      can be done either with a series of shift/add
+ *      instructions or by using "tmp % 15"; I expect
+ *      that the cascaded shift/add will be faster.
+ */
+
+int
+do_pcibr_rrb_count_valid(bridge_t *bridge,
+			 pciio_slot_t slot)
+{
+    bridgereg_t             tmp;
+
+    tmp = bridge->b_rrb_map[slot & 1].reg;
+    tmp ^= 0x11111111 * (7 - slot / 2);
+    tmp &= (0xCCCCCCCC & tmp) >> 2;
+    tmp &= (0x22222222 & tmp) >> 1;
+    tmp += tmp >> 4;
+    tmp += tmp >> 8;
+    tmp += tmp >> 16;
+    return tmp & 15;
+}
+
+/*
+ *    do_pcibr_rrb_count_avail: count how many RRBs are
+ *      available to be allocated for the specified slot.
+ *
+ *      IMPL NOTE: similar to the above, except we are
+ *      just counting how many fields have the valid bit
+ *      turned off.
+ */
+int
+do_pcibr_rrb_count_avail(bridge_t *bridge,
+			 pciio_slot_t slot)
+{
+    bridgereg_t             tmp;
+
+    tmp = bridge->b_rrb_map[slot & 1].reg;
+    tmp = (0x88888888 & ~tmp) >> 3;
+    tmp += tmp >> 4;
+    tmp += tmp >> 8;
+    tmp += tmp >> 16;
+    return tmp & 15;
+}
+
+/*
+ *    do_pcibr_rrb_alloc: allocate some additional RRBs
+ *      for the specified slot. Returns -1 if there were
+ *      insufficient free RRBs to satisfy the request,
+ *      or 0 if the request was fulfilled.
+ *
+ *      Note that if a request can be partially filled,
+ *      it will be, even if we return failure.
+ *
+ *      IMPL NOTE: again we avoid iterating across all
+ *      the RRBs; instead, we form up a word containing
+ *      one bit for each free RRB, then peel the bits
+ *      off from the low end.
+ */
+int
+do_pcibr_rrb_alloc(bridge_t *bridge,
+		   pciio_slot_t slot,
+		   int more)
+{
+    int                     rv = 0;
+    bridgereg_t             reg, tmp, bit;
+
+    reg = bridge->b_rrb_map[slot & 1].reg;
+    tmp = (0x88888888 & ~reg) >> 3;
+    while (more-- > 0) {
+	bit = LSBIT(tmp);
+	if (!bit) {
+	    rv = -1;
+	    break;
+	}
+	tmp &= ~bit;
+	reg = ((reg & ~(bit * 15)) | (bit * (8 + slot / 2)));
+    }
+    bridge->b_rrb_map[slot & 1].reg = reg;
+    return rv;
+}
+
+/*
+ *    do_pcibr_rrb_free: release some of the RRBs that
+ *      have been allocated for the specified
+ *      slot. Returns zero for success, or negative if
+ *      it was unable to free that many RRBs.
+ *
+ *      IMPL NOTE: We form up a bit for each RRB
+ *      allocated to the slot, aligned with the VALID
+ *      bitfield this time; then we peel bits off one at
+ *      a time, releasing the corresponding RRB.
+ */
+int
+do_pcibr_rrb_free(bridge_t *bridge,
+		  pciio_slot_t slot,
+		  int less)
+{
+    int                     rv = 0;
+    bridgereg_t             reg, tmp, clr, bit;
+    int                     i;
+
+    clr = 0;
+    reg = bridge->b_rrb_map[slot & 1].reg;
+
+    /* This needs to be done otherwise the rrb's on the virtual channel
+     * for this slot won't be freed !!
+     */
+    tmp = reg & 0xbbbbbbbb;
+
+    tmp ^= (0x11111111 * (7 - slot / 2));
+    tmp &= (0x33333333 & tmp) << 2;
+    tmp &= (0x44444444 & tmp) << 1;
+    while (less-- > 0) {
+	bit = LSBIT(tmp);
+	if (!bit) {
+	    rv = -1;
+	    break;
+	}
+	tmp &= ~bit;
+	reg &= ~bit;
+	clr |= bit;
+    }
+    bridge->b_rrb_map[slot & 1].reg = reg;
+
+    for (i = 0; i < 8; i++)
+	if (clr & (8 << (4 * i)))
+	    do_pcibr_rrb_clear(bridge, (2 * i) + (slot & 1));
+
+    return rv;
+}
+
+void
+do_pcibr_rrb_autoalloc(pcibr_soft_t pcibr_soft,
+		       int slot,
+		       int more_rrbs)
+{
+    bridge_t               *bridge = pcibr_soft->bs_base;
+    int                     got;
+
+    for (got = 0; got < more_rrbs; ++got) {
+	if (pcibr_soft->bs_rrb_res[slot & 7] > 0)
+	    pcibr_soft->bs_rrb_res[slot & 7]--;
+	else if (pcibr_soft->bs_rrb_avail[slot & 1] > 0)
+	    pcibr_soft->bs_rrb_avail[slot & 1]--;
+	else
+	    break;
+	if (do_pcibr_rrb_alloc(bridge, slot, 1) < 0)
+	    break;
+#if PCIBR_RRB_DEBUG
+	printk("do_pcibr_rrb_autoalloc: add one to slot %d%s\n",
+		slot & 7, slot & 8 ? "v" : "");
+#endif
+	pcibr_soft->bs_rrb_valid[slot]++;
+    }
+#if PCIBR_RRB_DEBUG
+    printk("%s: %d+%d free RRBs. Allocation list:\n", pcibr_soft->bs_name,
+	    pcibr_soft->bs_rrb_avail[0],
+	    pcibr_soft->bs_rrb_avail[1]);
+    for (slot = 0; slot < 8; ++slot)
+	printk("\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]);
+    printk("\n");
+#endif
+}
+
+/*
+ * Device driver interface to flush the write buffers for a specified
+ * device hanging off the bridge.
+ */
+int
+pcibr_wrb_flush(devfs_handle_t pconn_vhdl)
+{
+    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);
+    bridge_t               *bridge = pcibr_soft->bs_base;
+    volatile bridgereg_t   *wrb_flush;
+
+    wrb_flush = &(bridge->b_wr_req_buf[pciio_slot].reg);
+    while (*wrb_flush);
+
+    return(0);
+}
+
+/*
+ * Device driver interface to request RRBs for a specified device
+ * hanging off a Bridge.  The driver requests the total number of
+ * RRBs it would like for the normal channel (vchan0) and for the
+ * "virtual channel" (vchan1).  The actual number allocated to each
+ * channel is returned.
+ *
+ * If we cannot allocate at least one RRB to a channel that needs
+ * at least one, return -1 (failure).  Otherwise, satisfy the request
+ * as best we can and return 0.
+ */
+int
+pcibr_rrb_alloc(devfs_handle_t pconn_vhdl,
+		int *count_vchan0,
+		int *count_vchan1)
+{
+    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);
+    bridge_t               *bridge = pcibr_soft->bs_base;
+    int                     desired_vchan0;
+    int                     desired_vchan1;
+    int                     orig_vchan0;
+    int                     orig_vchan1;
+    int                     delta_vchan0;
+    int                     delta_vchan1;
+    int                     final_vchan0;
+    int                     final_vchan1;
+    int                     avail_rrbs;
+    int                     res_rrbs;
+    unsigned long           s;
+    int                     error;
+
+    /*
+     * TBD: temper request with admin info about RRB allocation,
+     * and according to demand from other devices on this Bridge.
+     *
+     * One way of doing this would be to allocate two RRBs
+     * for each device on the bus, before any drivers start
+     * asking for extras. This has the weakness that one
+     * driver might not give back an "extra" RRB until after
+     * another driver has already failed to get one that
+     * it wanted.
+     */
+
+    s = pcibr_lock(pcibr_soft);
+
+    /* Save the boot-time RRB configuration for this slot */
+    if (pcibr_soft->bs_rrb_valid_dflt[pciio_slot] < 0) {
+        pcibr_soft->bs_rrb_valid_dflt[pciio_slot] =
+                pcibr_soft->bs_rrb_valid[pciio_slot]; 
+        pcibr_soft->bs_rrb_valid_dflt[pciio_slot + PCIBR_RRB_SLOT_VIRTUAL] =
+                pcibr_soft->bs_rrb_valid[pciio_slot + PCIBR_RRB_SLOT_VIRTUAL];
+        pcibr_soft->bs_rrb_res_dflt[pciio_slot] =
+                pcibr_soft->bs_rrb_res[pciio_slot];
+                  
+    }
+
+    /* How many RRBs do we own? */
+    orig_vchan0 = pcibr_soft->bs_rrb_valid[pciio_slot];
+    orig_vchan1 = pcibr_soft->bs_rrb_valid[pciio_slot + PCIBR_RRB_SLOT_VIRTUAL];
+
+    /* How many RRBs do we want? */
+    desired_vchan0 = count_vchan0 ? *count_vchan0 : orig_vchan0;
+    desired_vchan1 = count_vchan1 ? *count_vchan1 : orig_vchan1;
+
+    /* How many RRBs are free? */
+    avail_rrbs = pcibr_soft->bs_rrb_avail[pciio_slot & 1]
+	+ pcibr_soft->bs_rrb_res[pciio_slot];
+
+    /* Figure desired deltas */
+    delta_vchan0 = desired_vchan0 - orig_vchan0;
+    delta_vchan1 = desired_vchan1 - orig_vchan1;
+
+    /* Trim back deltas to something
+     * that we can actually meet, by
+     * decreasing the ending allocation
+     * for whichever channel wants
+     * more RRBs. If both want the same
+     * number, cut the second channel.
+     * NOTE: do not change the allocation for
+     * a channel that was passed as NULL.
+     */
+    while ((delta_vchan0 + delta_vchan1) > avail_rrbs) {
+	if (count_vchan0 &&
+	    (!count_vchan1 ||
+	     ((orig_vchan0 + delta_vchan0) >
+	      (orig_vchan1 + delta_vchan1))))
+	    delta_vchan0--;
+	else
+	    delta_vchan1--;
+    }
+
+    /* Figure final RRB allocations
+     */
+    final_vchan0 = orig_vchan0 + delta_vchan0;
+    final_vchan1 = orig_vchan1 + delta_vchan1;
+
+    /* If either channel wants RRBs but our actions
+     * would leave it with none, declare an error,
+     * but DO NOT change any RRB allocations.
+     */
+    if ((desired_vchan0 && !final_vchan0) ||
+	(desired_vchan1 && !final_vchan1)) {
+
+	error = -1;
+
+    } else {
+
+	/* Commit the allocations: free, then alloc.
+	 */
+	if (delta_vchan0 < 0)
+	    (void) do_pcibr_rrb_free(bridge, pciio_slot, -delta_vchan0);
+	if (delta_vchan1 < 0)
+	    (void) do_pcibr_rrb_free(bridge, PCIBR_RRB_SLOT_VIRTUAL + pciio_slot, -delta_vchan1);
+
+	if (delta_vchan0 > 0)
+	    (void) do_pcibr_rrb_alloc(bridge, pciio_slot, delta_vchan0);
+	if (delta_vchan1 > 0)
+	    (void) do_pcibr_rrb_alloc(bridge, PCIBR_RRB_SLOT_VIRTUAL + pciio_slot, delta_vchan1);
+
+	/* Return final values to caller.
+	 */
+	if (count_vchan0)
+	    *count_vchan0 = final_vchan0;
+	if (count_vchan1)
+	    *count_vchan1 = final_vchan1;
+
+	/* prevent automatic changes to this slot's RRBs
+	 */
+	pcibr_soft->bs_rrb_fixed |= 1 << pciio_slot;
+
+	/* Track the actual allocations, release
+	 * any further reservations, and update the
+	 * number of available RRBs.
+	 */
+
+	pcibr_soft->bs_rrb_valid[pciio_slot] = final_vchan0;
+	pcibr_soft->bs_rrb_valid[pciio_slot + PCIBR_RRB_SLOT_VIRTUAL] = final_vchan1;
+	pcibr_soft->bs_rrb_avail[pciio_slot & 1] =
+	    pcibr_soft->bs_rrb_avail[pciio_slot & 1]
+	    + pcibr_soft->bs_rrb_res[pciio_slot]
+	    - delta_vchan0
+	    - delta_vchan1;
+	pcibr_soft->bs_rrb_res[pciio_slot] = 0;
+
+        /*
+         * Reserve enough RRBs so this slot's RRB configuration can be
+         * reset to its boot-time default following a hot-plug shut-down
+         */
+        res_rrbs =   (pcibr_soft->bs_rrb_valid_dflt[pciio_slot] -
+                      pcibr_soft->bs_rrb_valid[pciio_slot]) 
+                   + (pcibr_soft->bs_rrb_valid_dflt[pciio_slot +
+                                                    PCIBR_RRB_SLOT_VIRTUAL] -
+                      pcibr_soft->bs_rrb_valid[pciio_slot +
+                                               PCIBR_RRB_SLOT_VIRTUAL])
+                   + (pcibr_soft->bs_rrb_res_dflt[pciio_slot] -
+                      pcibr_soft->bs_rrb_res[pciio_slot]);
+
+         if (res_rrbs > 0) {
+             pcibr_soft->bs_rrb_res[pciio_slot] = res_rrbs;
+             pcibr_soft->bs_rrb_avail[pciio_slot & 1] =
+                 pcibr_soft->bs_rrb_avail[pciio_slot & 1]
+                 - res_rrbs;
+         }
+ 
+#if PCIBR_RRB_DEBUG
+	printk("pcibr_rrb_alloc: slot %d set to %d+%d; %d+%d free\n",
+		pciio_slot, final_vchan0, final_vchan1,
+		pcibr_soft->bs_rrb_avail[0],
+		pcibr_soft->bs_rrb_avail[1]);
+	for (pciio_slot = 0; pciio_slot < 8; ++pciio_slot)
+	    printk("\t%d+%d+%d",
+		    0xFFF & pcibr_soft->bs_rrb_valid[pciio_slot],
+		    0xFFF & pcibr_soft->bs_rrb_valid[pciio_slot + PCIBR_RRB_SLOT_VIRTUAL],
+		    pcibr_soft->bs_rrb_res[pciio_slot]);
+	printk("\n");
+#endif
+
+	error = 0;
+    }
+
+    pcibr_unlock(pcibr_soft, s);
+
+    return error;
+}
+
+/*
+ * Device driver interface to check the current state
+ * of the RRB allocations.
+ *
+ *   pconn_vhdl is your PCI connection point (specifies which
+ *      PCI bus and which slot).
+ *
+ *   count_vchan0 points to where to return the number of RRBs
+ *      assigned to the primary DMA channel, used by all DMA
+ *      that does not explicitly ask for the alternate virtual
+ *      channel.
+ *
+ *   count_vchan1 points to where to return the number of RRBs
+ *      assigned to the secondary DMA channel, used when
+ *      PCIBR_VCHAN1 and PCIIO_DMA_A64 are specified.
+ *
+ *   count_reserved points to where to return the number of RRBs
+ *      that have been automatically reserved for your device at
+ *      startup, but which have not been assigned to a
+ *      channel. RRBs must be assigned to a channel to be used;
+ *      this can be done either with an explicit pcibr_rrb_alloc
+ *      call, or automatically by the infrastructure when a DMA
+ *      translation is constructed. Any call to pcibr_rrb_alloc
+ *      will release any unassigned reserved RRBs back to the
+ *      free pool.
+ *
+ *   count_pool points to where to return the number of RRBs
+ *      that are currently unassigned and unreserved. This
+ *      number can (and will) change as other drivers make calls
+ *      to pcibr_rrb_alloc, or automatically allocate RRBs for
+ *      DMA beyond their initial reservation.
+ *
+ * NULL may be passed for any of the return value pointers
+ * the caller is not interested in.
+ *
+ * The return value is "0" if all went well, or "-1" if
+ * there is a problem. Additionally, if the wrong vertex
+ * is passed in, one of the subsidiary support functions
+ * could panic with a "bad pciio fingerprint."
+ */
+
+int
+pcibr_rrb_check(devfs_handle_t pconn_vhdl,
+		int *count_vchan0,
+		int *count_vchan1,
+		int *count_reserved,
+		int *count_pool)
+{
+    pciio_info_t            pciio_info;
+    pciio_slot_t            pciio_slot;
+    pcibr_soft_t            pcibr_soft;
+    unsigned long           s;
+    int                     error = -1;
+
+    if ((pciio_info = pciio_info_get(pconn_vhdl)) &&
+	(pcibr_soft = (pcibr_soft_t) pciio_info_mfast_get(pciio_info)) &&
+	((pciio_slot = pciio_info_slot_get(pciio_info)) < 8)) {
+
+	s = pcibr_lock(pcibr_soft);
+
+	if (count_vchan0)
+	    *count_vchan0 =
+		pcibr_soft->bs_rrb_valid[pciio_slot];
+
+	if (count_vchan1)
+	    *count_vchan1 =
+		pcibr_soft->bs_rrb_valid[pciio_slot + PCIBR_RRB_SLOT_VIRTUAL];
+
+	if (count_reserved)
+	    *count_reserved =
+		pcibr_soft->bs_rrb_res[pciio_slot];
+
+	if (count_pool)
+	    *count_pool =
+		pcibr_soft->bs_rrb_avail[pciio_slot & 1];
+
+	error = 0;
+
+	pcibr_unlock(pcibr_soft, s);
+    }
+    return error;
+}
+
+/* pcibr_alloc_all_rrbs allocates all the rrbs available in the quantities
+ * requested for each of the devices.  The evn_odd argument indicates whether
+ * allocation is for the odd or even rrbs. The next group of four argument
+ * pairs indicate the amount of rrbs to be assigned to each device. The first
+ * argument of each pair indicate the total number of rrbs to allocate for that
+ * device. The second argument of each pair indicates how many rrb's from the
+ * first argument should be assigned to the virtual channel. The total of all
+ * of the first arguments should be <= 8. The second argument should be <= the
+ * first argument.
+ * if even_odd = 0 the devices in order are 0, 2, 4, 6 
+ * if even_odd = 1 the devices in order are 1, 3, 5, 7
+ * returns 0 if no errors else returns -1
+ */
+
+int
+pcibr_alloc_all_rrbs(devfs_handle_t vhdl, int even_odd,
+		     int dev_1_rrbs, int virt1, int dev_2_rrbs, int virt2,
+		     int dev_3_rrbs, int virt3, int dev_4_rrbs, int virt4)
+{
+    devfs_handle_t          pcibr_vhdl;
+    pcibr_soft_t            pcibr_soft = (pcibr_soft_t)0;
+    bridge_t               *bridge = NULL;
+
+    uint32_t                rrb_setting = 0;
+    int                     rrb_shift = 7;
+    uint32_t                cur_rrb;
+    int                     dev_rrbs[4];
+    int                     virt[4];
+    int                     i, j;
+    unsigned long           s;
+
+    if (GRAPH_SUCCESS ==
+	hwgraph_traverse(vhdl, EDGE_LBL_PCI, &pcibr_vhdl)) {
+	pcibr_soft = pcibr_soft_get(pcibr_vhdl);
+	if (pcibr_soft)
+	    bridge = pcibr_soft->bs_base;
+	hwgraph_vertex_unref(pcibr_vhdl);
+    }
+    if (bridge == NULL)
+	bridge = (bridge_t *) xtalk_piotrans_addr
+	    (vhdl, NULL, 0, sizeof(bridge_t), 0);
+
+    even_odd &= 1;
+
+    dev_rrbs[0] = dev_1_rrbs;
+    dev_rrbs[1] = dev_2_rrbs;
+    dev_rrbs[2] = dev_3_rrbs;
+    dev_rrbs[3] = dev_4_rrbs;
+
+    virt[0] = virt1;
+    virt[1] = virt2;
+    virt[2] = virt3;
+    virt[3] = virt4;
+
+    if ((dev_1_rrbs + dev_2_rrbs + dev_3_rrbs + dev_4_rrbs) > 8) {
+	return -1;
+    }
+    if ((dev_1_rrbs < 0) || (dev_2_rrbs < 0) || (dev_3_rrbs < 0) || (dev_4_rrbs < 0)) {
+	return -1;
+    }
+    /* walk through rrbs */
+    for (i = 0; i < 4; i++) {
+	if (virt[i]) {
+		for( j = 0; j < virt[i]; j++) {
+			cur_rrb = i | 0xc;
+			cur_rrb = cur_rrb << (rrb_shift * 4);
+			rrb_shift--;
+			rrb_setting = rrb_setting | cur_rrb;
+			dev_rrbs[i] = dev_rrbs[i] - 1;
+		}
+	}
+	for (j = 0; j < dev_rrbs[i]; j++) {
+	    cur_rrb = i | 0x8;
+	    cur_rrb = cur_rrb << (rrb_shift * 4);
+	    rrb_shift--;
+	    rrb_setting = rrb_setting | cur_rrb;
+	}
+    }
+
+    if (pcibr_soft)
+	s = pcibr_lock(pcibr_soft);
+
+    bridge->b_rrb_map[even_odd].reg = rrb_setting;
+
+    if (pcibr_soft) {
+
+	pcibr_soft->bs_rrb_fixed |= 0x55 << even_odd;
+
+	/* since we've "FIXED" the allocations
+	 * for these slots, we probably can dispense
+	 * with tracking avail/res/valid data, but
+	 * keeping it up to date helps debugging.
+	 */
+
+	pcibr_soft->bs_rrb_avail[even_odd] =
+	    8 - (dev_1_rrbs + dev_2_rrbs + dev_3_rrbs + dev_4_rrbs);
+
+	pcibr_soft->bs_rrb_res[even_odd + 0] = 0;
+	pcibr_soft->bs_rrb_res[even_odd + 2] = 0;
+	pcibr_soft->bs_rrb_res[even_odd + 4] = 0;
+	pcibr_soft->bs_rrb_res[even_odd + 6] = 0;
+
+	pcibr_soft->bs_rrb_valid[even_odd + 0] = dev_1_rrbs - virt1;
+	pcibr_soft->bs_rrb_valid[even_odd + 2] = dev_2_rrbs - virt2;
+	pcibr_soft->bs_rrb_valid[even_odd + 4] = dev_3_rrbs - virt3;
+	pcibr_soft->bs_rrb_valid[even_odd + 6] = dev_4_rrbs - virt4;
+
+	pcibr_soft->bs_rrb_valid[even_odd + 0 + PCIBR_RRB_SLOT_VIRTUAL] = virt1;
+	pcibr_soft->bs_rrb_valid[even_odd + 2 + PCIBR_RRB_SLOT_VIRTUAL] = virt2;
+	pcibr_soft->bs_rrb_valid[even_odd + 4 + PCIBR_RRB_SLOT_VIRTUAL] = virt3;
+	pcibr_soft->bs_rrb_valid[even_odd + 6 + PCIBR_RRB_SLOT_VIRTUAL] = virt4;
+
+	pcibr_unlock(pcibr_soft, s);
+    }
+    return 0;
+}
+
+/*
+ *    pcibr_rrb_flush: chase down all the RRBs assigned
+ *      to the specified connection point, and flush
+ *      them.
+ */
+void
+pcibr_rrb_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            pciio_slot = pciio_info_slot_get(pciio_info);
+    bridge_t               *bridge = pcibr_soft->bs_base;
+    unsigned long           s;
+    reg_p                   rrbp;
+    unsigned                rrbm;
+    int                     i;
+    int                     rrbn;
+    unsigned                sval;
+    unsigned                mask;
+
+    sval = BRIDGE_RRB_EN | (pciio_slot >> 1);
+    mask = BRIDGE_RRB_EN | BRIDGE_RRB_PDEV;
+    rrbn = pciio_slot & 1;
+    rrbp = &bridge->b_rrb_map[rrbn].reg;
+
+    s = pcibr_lock(pcibr_soft);
+    rrbm = *rrbp;
+    for (i = 0; i < 8; ++i) {
+	if ((rrbm & mask) == sval)
+	    do_pcibr_rrb_flush(bridge, rrbn);
+	rrbm >>= 4;
+	rrbn += 2;
+    }
+    pcibr_unlock(pcibr_soft, s);
+}
+
+/*
+ * pcibr_slot_initial_rrb_alloc
+ *	Allocate a default number of rrbs for this slot on 
+ * 	the two channels.  This is dictated by the rrb allocation
+ * 	strategy routine defined per platform.
+ */
+
+int
+pcibr_slot_initial_rrb_alloc(devfs_handle_t pcibr_vhdl,
+			     pciio_slot_t slot)
+{
+    pcibr_soft_t	 pcibr_soft;
+    pcibr_info_h	 pcibr_infoh;
+    pcibr_info_t	 pcibr_info;
+    bridge_t		*bridge;
+    int                  c0, c1, r;
+
+    pcibr_soft = pcibr_soft_get(pcibr_vhdl);
+
+    if (!pcibr_soft || !PCIBR_VALID_SLOT(slot))
+	return(EINVAL);
+
+    bridge = pcibr_soft->bs_base;
+
+    /* How may RRBs are on this slot?
+     */
+    c0 = do_pcibr_rrb_count_valid(bridge, slot);
+    c1 = do_pcibr_rrb_count_valid(bridge, slot + PCIBR_RRB_SLOT_VIRTUAL);
+
+#if PCIBR_RRB_DEBUG
+    printk(
+	    "pcibr_slot_initial_rrb_alloc: slot %d started with %d+%d\n",
+            slot, c0, c1);
+#endif
+
+    /* Do we really need any?
+     */
+    pcibr_infoh = pcibr_soft->bs_slot[slot].bss_infos;
+    pcibr_info = pcibr_infoh[0];
+    if ((pcibr_info->f_vendor == PCIIO_VENDOR_ID_NONE) &&
+	!pcibr_soft->bs_slot[slot].has_host) {
+	if (c0 > 0)
+	    do_pcibr_rrb_free(bridge, slot, c0);
+	if (c1 > 0)
+	    do_pcibr_rrb_free(bridge, slot + PCIBR_RRB_SLOT_VIRTUAL, c1);
+	pcibr_soft->bs_rrb_valid[slot] = 0x1000;
+	pcibr_soft->bs_rrb_valid[slot + PCIBR_RRB_SLOT_VIRTUAL] = 0x1000;
+	return(ENODEV);
+    }
+
+    pcibr_soft->bs_rrb_avail[slot & 1] -= c0 + c1;
+    pcibr_soft->bs_rrb_valid[slot] = c0;
+    pcibr_soft->bs_rrb_valid[slot + PCIBR_RRB_SLOT_VIRTUAL] = c1;
+
+    pcibr_soft->bs_rrb_avail[0] = do_pcibr_rrb_count_avail(bridge, 0);
+    pcibr_soft->bs_rrb_avail[1] = do_pcibr_rrb_count_avail(bridge, 1);
+
+    r = 3 - (c0 + c1);
+
+    if (r > 0) {
+	pcibr_soft->bs_rrb_res[slot] = r;
+	pcibr_soft->bs_rrb_avail[slot & 1] -= r;
+    }
+
+#if PCIBR_RRB_DEBUG
+    printk("\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]);
+    printk("\n");
+#endif
+
+    return(0);
+}
+
+/*
+ * pcibr_initial_rrb
+ *      Assign an equal total number of RRBs to all candidate slots, 
+ *      where the total is the sum of the number of RRBs assigned to
+ *      the normal channel, the number of RRBs assigned to the virtual
+ *      channel, and the number of RRBs assigned as reserved. 
+ *
+ *      A candidate slot is a populated slot on a non-SN1 system or 
+ *      any existing (populated or empty) slot on an SN1 system.
+ *      Empty SN1 slots need RRBs to support hot-plug operations.
+ */
+
+int
+pcibr_initial_rrb(devfs_handle_t pcibr_vhdl,
+			     pciio_slot_t first, pciio_slot_t last)
+{
+    pcibr_soft_t            pcibr_soft = pcibr_soft_get(pcibr_vhdl);
+    bridge_t               *bridge = pcibr_soft->bs_base;
+    pciio_slot_t            slot;
+    int                     c0, c1;
+    int                     have[2][3];
+    int                     res[2];
+    int                     eo;
+
+    have[0][0] = have[0][1] = have[0][2] = 0;
+    have[1][0] = have[1][1] = have[1][2] = 0;
+    res[0] = res[1] = 0;
+
+    for (slot = 0; slot < 8; ++slot) {
+        /* Initial RRB management; give back RRBs in all non-existent slots */
+        (void) pcibr_slot_initial_rrb_alloc(pcibr_vhdl, slot);
+
+        /* Base calculations only on existing slots */
+        if ((slot >= first) && (slot <= last)) {
+            c0 = pcibr_soft->bs_rrb_valid[slot];
+            c1 = pcibr_soft->bs_rrb_valid[slot + PCIBR_RRB_SLOT_VIRTUAL];
+            if ((c0 + c1) < 3)
+                have[slot & 1][c0 + c1]++;
+        }
+    }
+
+    /* Initialize even/odd slot available RRB counts */
+    pcibr_soft->bs_rrb_avail[0] = do_pcibr_rrb_count_avail(bridge, 0);
+    pcibr_soft->bs_rrb_avail[1] = do_pcibr_rrb_count_avail(bridge, 1);
+
+    /*
+     * Calculate reserved RRBs for slots based on current RRB usage
+     */
+    for (eo = 0; eo < 2; eo++) {
+        if ((3 * have[eo][0] + 2 * have[eo][1] + have[eo][2]) <= pcibr_soft->bs_rrb_avail[eo])
+            res[eo] = 3;
+        else if ((2 * have[eo][0] + have[eo][1]) <= pcibr_soft->bs_rrb_avail[eo])
+            res[eo] = 2;
+        else if (have[eo][0] <= pcibr_soft->bs_rrb_avail[eo])
+            res[eo] = 1;
+        else
+            res[eo] = 0;
+
+    }
+
+    /* Assign reserved RRBs to existing slots */
+    for (slot = first; slot <= last; ++slot) {
+        int                     r;
+
+        c0 = pcibr_soft->bs_rrb_valid[slot];
+        c1 = pcibr_soft->bs_rrb_valid[slot + PCIBR_RRB_SLOT_VIRTUAL];
+        r = res[slot & 1] - (c0 + c1);
+
+        if (r > 0) {
+            pcibr_soft->bs_rrb_res[slot] = r;
+            pcibr_soft->bs_rrb_avail[slot & 1] -= r;
+            }
+    }
+
+#if PCIBR_RRB_DEBUG
+    printk("%v RRB MANAGEMENT: %d+%d free\n",
+            pcibr_vhdl,
+            pcibr_soft->bs_rrb_avail[0],
+            pcibr_soft->bs_rrb_avail[1]);
+    for (slot = first; slot <= last; ++slot)
+        printk("\tslot %d: %d+%d+%d", slot,
+                0xFFF & pcibr_soft->bs_rrb_valid[slot],
+                0xFFF & pcibr_soft->bs_rrb_valid[slot + PCIBR_RRB_SLOT_VIRTUAL],
+                pcibr_soft->bs_rrb_res[slot]);
+    printk("\n");
+#endif
+
+    return 0;
+
+}
+

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)