📄 pcibr_rrb.c
字号:
/* * * This file is subject to the terms and conditions of the GNU General Public * License. See the file "COPYING" in the main directory of this archive * for more details. * * Copyright (C) 2001-2003 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/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);int do_pcibr_rrb_count_avail(bridge_t *, pciio_slot_t);int do_pcibr_rrb_alloc(bridge_t *, pciio_slot_t, int, int);int do_pcibr_rrb_free(bridge_t *, pciio_slot_t, int, int);void do_pcibr_rrb_free_all(pcibr_soft_t, bridge_t *, pciio_slot_t);void do_pcibr_rrb_autoalloc(pcibr_soft_t, int, int, int);int pcibr_wrb_flush(vertex_hdl_t);int pcibr_rrb_alloc(vertex_hdl_t, int *, int *);int pcibr_rrb_check(vertex_hdl_t, int *, int *, int *, int *);void pcibr_rrb_flush(vertex_hdl_t);int pcibr_slot_initial_rrb_alloc(vertex_hdl_t,pciio_slot_t);void pcibr_rrb_debug(char *, pcibr_soft_t);/* * RRB Management * * All the do_pcibr_rrb_ routines manipulate the Read Response Buffer (rrb) * registers within the Bridge. Two 32 registers (b_rrb_map[2] also known * as the b_even_resp & b_odd_resp registers) are used to allocate the 16 * rrbs to devices. The b_even_resp register represents even num devices, * and b_odd_resp represent odd number devices. Each rrb is represented by * 4-bits within a register. * BRIDGE & XBRIDGE: 1 enable bit, 1 virtual channel bit, 2 device bits * PIC: 1 enable bit, 2 virtual channel bits, 1 device bit * PIC has 4 devices per bus, and 4 virtual channels (1 normal & 3 virtual) * per device. BRIDGE & XBRIDGE have 8 devices per bus and 2 virtual * channels (1 normal & 1 virtual) per device. See the BRIDGE and PIC ASIC * Programmers Reference guides for more information. */ #define RRB_MASK (0xf) /* mask a single rrb within reg */#define RRB_SIZE (4) /* sizeof rrb within reg (bits) */ #define RRB_ENABLE_BIT(bridge) (0x8) /* [BRIDGE | PIC]_RRB_EN */#define NUM_PDEV_BITS(bridge) (1)#define NUM_VDEV_BITS(bridge) (2)#define NUMBER_VCHANNELS(bridge) (4)#define SLOT_2_PDEV(bridge, slot) ((slot) >> 1)#define SLOT_2_RRB_REG(bridge, slot) ((slot) & 0x1) /* validate that the slot and virtual channel are valid for a given bridge */#define VALIDATE_SLOT_n_VCHAN(bridge, s, v) \ (((((s) != PCIIO_SLOT_NONE) && ((s) <= (pciio_slot_t)3)) && (((v) >= 0) && ((v) <= 3))) ? 1 : 0) /* * Count how many RRBs are marked valid for the specified PCI slot * and virtual channel. Return the count. */ intdo_pcibr_rrb_count_valid(bridge_t *bridge, pciio_slot_t slot, int vchan){ bridgereg_t tmp; uint16_t enable_bit, vchan_bits, pdev_bits, rrb_bits; int rrb_index, cnt=0; if (!VALIDATE_SLOT_n_VCHAN(bridge, slot, vchan)) { printk(KERN_WARNING "do_pcibr_rrb_count_valid() invalid slot/vchan [%d/%d]\n", slot, vchan); return 0; } enable_bit = RRB_ENABLE_BIT(bridge); vchan_bits = vchan << NUM_PDEV_BITS(bridge); pdev_bits = SLOT_2_PDEV(bridge, slot); rrb_bits = enable_bit | vchan_bits | pdev_bits; tmp = bridge->b_rrb_map[SLOT_2_RRB_REG(bridge, slot)].reg; for (rrb_index = 0; rrb_index < 8; rrb_index++) { if ((tmp & RRB_MASK) == rrb_bits) cnt++; tmp = (tmp >> RRB_SIZE); } return cnt;} /* * Count how many RRBs are available to be allocated to the specified * slot. Return the count. */ intdo_pcibr_rrb_count_avail(bridge_t *bridge, pciio_slot_t slot){ bridgereg_t tmp; uint16_t enable_bit; int rrb_index, cnt=0; if (!VALIDATE_SLOT_n_VCHAN(bridge, slot, 0)) { printk(KERN_WARNING "do_pcibr_rrb_count_avail() invalid slot/vchan"); return 0; } enable_bit = RRB_ENABLE_BIT(bridge); tmp = bridge->b_rrb_map[SLOT_2_RRB_REG(bridge, slot)].reg; for (rrb_index = 0; rrb_index < 8; rrb_index++) { if ((tmp & enable_bit) != enable_bit) cnt++; tmp = (tmp >> RRB_SIZE); } return cnt;} /* * Allocate some additional RRBs for the specified slot and the specified * virtual channel. 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. */ intdo_pcibr_rrb_alloc(bridge_t *bridge, pciio_slot_t slot, int vchan, int more){ bridgereg_t reg, tmp = (bridgereg_t)0; uint16_t enable_bit, vchan_bits, pdev_bits, rrb_bits; int rrb_index; if (!VALIDATE_SLOT_n_VCHAN(bridge, slot, vchan)) { printk(KERN_WARNING "do_pcibr_rrb_alloc() invalid slot/vchan"); return -1; } enable_bit = RRB_ENABLE_BIT(bridge); vchan_bits = vchan << NUM_PDEV_BITS(bridge); pdev_bits = SLOT_2_PDEV(bridge, slot); rrb_bits = enable_bit | vchan_bits | pdev_bits; reg = tmp = bridge->b_rrb_map[SLOT_2_RRB_REG(bridge, slot)].reg; for (rrb_index = 0; ((rrb_index < 8) && (more > 0)); rrb_index++) { if ((tmp & enable_bit) != enable_bit) { /* clear the rrb and OR in the new rrb into 'reg' */ reg = reg & ~(RRB_MASK << (RRB_SIZE * rrb_index)); reg = reg | (rrb_bits << (RRB_SIZE * rrb_index)); more--; } tmp = (tmp >> RRB_SIZE); } bridge->b_rrb_map[SLOT_2_RRB_REG(bridge, slot)].reg = reg; return (more ? -1 : 0);} /* * 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. * * Note that if a request can be partially fulfilled, it will be, even * if we return failure. */ intdo_pcibr_rrb_free(bridge_t *bridge, pciio_slot_t slot, int vchan, int less){ bridgereg_t reg, tmp = (bridgereg_t)0, clr = 0; uint16_t enable_bit, vchan_bits, pdev_bits, rrb_bits; int rrb_index; if (!VALIDATE_SLOT_n_VCHAN(bridge, slot, vchan)) { printk(KERN_WARNING "do_pcibr_rrb_free() invalid slot/vchan"); return -1; } enable_bit = RRB_ENABLE_BIT(bridge); vchan_bits = vchan << NUM_PDEV_BITS(bridge); pdev_bits = SLOT_2_PDEV(bridge, slot); rrb_bits = enable_bit | vchan_bits | pdev_bits; reg = tmp = bridge->b_rrb_map[SLOT_2_RRB_REG(bridge, slot)].reg; for (rrb_index = 0; ((rrb_index < 8) && (less > 0)); rrb_index++) { if ((tmp & RRB_MASK) == rrb_bits) { /* * the old do_pcibr_rrb_free() code only clears the enable bit * but I say we should clear the whole rrb (ie): * reg = reg & ~(RRB_MASK << (RRB_SIZE * rrb_index)); * But to be compatible with old code we'll only clear enable. */ reg = reg & ~(RRB_ENABLE_BIT(bridge) << (RRB_SIZE * rrb_index)); clr = clr | (enable_bit << (RRB_SIZE * rrb_index)); less--; } tmp = (tmp >> RRB_SIZE); } bridge->b_rrb_map[SLOT_2_RRB_REG(bridge, slot)].reg = reg; /* call do_pcibr_rrb_clear() for all the rrbs we've freed */ for (rrb_index = 0; rrb_index < 8; rrb_index++) { int evn_odd = SLOT_2_RRB_REG(bridge, slot); if (clr & (enable_bit << (RRB_SIZE * rrb_index))) do_pcibr_rrb_clear(bridge, (2 * rrb_index) + evn_odd); } return (less ? -1 : 0);} /* * free all the rrbs (both the normal and virtual channels) for the * specified slot. */ voiddo_pcibr_rrb_free_all(pcibr_soft_t pcibr_soft, bridge_t *bridge, pciio_slot_t slot){ int vchan; int vchan_total = NUMBER_VCHANNELS(bridge); /* pretend we own all 8 rrbs and just ignore the return value */ for (vchan = 0; vchan < vchan_total; vchan++) { (void)do_pcibr_rrb_free(bridge, slot, vchan, 8); pcibr_soft->bs_rrb_valid[slot][vchan] = 0; }} /* * Wait for the the specified rrb to have no outstanding XIO pkts * and for all data to be drained. Mark the rrb as no longer being * valid. */voiddo_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? */ } }}/* * Flush the specified rrb by calling do_pcibr_rrb_clear(). This * routine is just a wrapper to make sure the rrb is disabled * before calling do_pcibr_rrb_clear(). */voiddo_pcibr_rrb_flush(bridge_t *bridge, int rrbn){ reg_p rrbp = &bridge->b_rrb_map[rrbn & 1].reg; bridgereg_t rrbv; int shft = (RRB_SIZE * (rrbn >> 1)); unsigned long ebit = RRB_ENABLE_BIT(bridge) << shft; rrbv = *rrbp; if (rrbv & ebit) { *rrbp = rrbv & ~ebit; } do_pcibr_rrb_clear(bridge, rrbn); if (rrbv & ebit) { *rrbp = rrbv; }}voiddo_pcibr_rrb_autoalloc(pcibr_soft_t pcibr_soft, int slot, int vchan, 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] > 0) pcibr_soft->bs_rrb_res[slot]--; 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, vchan, 1) < 0) break; pcibr_soft->bs_rrb_valid[slot][vchan]++; } PCIBR_DEBUG_ALWAYS((PCIBR_DEBUG_RRB, pcibr_soft->bs_vhdl, "do_pcibr_rrb_autoalloc: added %d (of %d requested) RRBs " "to slot %d, vchan %d\n", got, more_rrbs, PCIBR_DEVICE_TO_SLOT(pcibr_soft, slot), vchan)); pcibr_rrb_debug("do_pcibr_rrb_autoalloc", pcibr_soft);}/* * Flush all the rrb's assigned to the specified connection point. */voidpcibr_rrb_flush(vertex_hdl_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 = PCIBR_INFO_SLOT_GET_INT(pciio_info); bridge_t *bridge = pcibr_soft->bs_base; bridgereg_t tmp; uint16_t enable_bit, pdev_bits, rrb_bits, rrb_mask; int rrb_index; unsigned long s; enable_bit = RRB_ENABLE_BIT(bridge); pdev_bits = SLOT_2_PDEV(bridge, slot); rrb_bits = enable_bit | pdev_bits; rrb_mask = enable_bit | ((NUM_PDEV_BITS(bridge) << 1) - 1); tmp = bridge->b_rrb_map[SLOT_2_RRB_REG(bridge, slot)].reg; s = pcibr_lock(pcibr_soft); for (rrb_index = 0; rrb_index < 8; rrb_index++) { int evn_odd = SLOT_2_RRB_REG(bridge, slot); if ((tmp & rrb_mask) == rrb_bits) do_pcibr_rrb_flush(bridge, (2 * rrb_index) + evn_odd); tmp = (tmp >> RRB_SIZE); } pcibr_unlock(pcibr_soft, s);}/* * Device driver interface to flush the write buffers for a specified * device hanging off the bridge. */intpcibr_wrb_flush(vertex_hdl_t pconn_vhdl){ pciio_info_t pciio_info = pciio_info_get(pconn_vhdl); pciio_slot_t pciio_slot = PCIBR_INFO_SLOT_GET_INT(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); if ( IS_PIC_SOFT(pcibr_soft) ) { 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. */intpcibr_rrb_alloc(vertex_hdl_t pconn_vhdl, int *count_vchan0, int *count_vchan1){ pciio_info_t pciio_info = pciio_info_get(pconn_vhdl); pciio_slot_t pciio_slot = PCIBR_INFO_SLOT_GET_INT(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; int vchan_total; int vchan; unsigned long s; int error; /* * TBD: temper request with admin info about RRB allocation, * and according to demand from other devices on this Bridge.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -