pcibr_rrb.c

来自「优龙2410linux2.6.8内核源代码」· C语言 代码 · 共 888 行 · 第 1/2 页

C
888
字号
/* * 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 <asm/sn/sgi.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>void		pcibr_rrb_alloc_init(pcibr_soft_t, int, int, int);void		pcibr_rrb_alloc_more(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 *);int		pcibr_alloc_all_rrbs(vertex_hdl_t, int, int, int, int, 			     	     int, 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		      (0x8)  /* [BRIDGE | PIC]_RRB_EN */#define NUM_PDEV_BITS		      (1)#define NUMBER_VCHANNELS	      (4)#define SLOT_2_PDEV(slot)		((slot) >> 1)#define SLOT_2_RRB_REG(slot)  		((slot) & 0x1)#define RRB_VALID(rrb)		      (0x00010000 << (rrb))#define RRB_INUSE(rrb)		      (0x00000001 << (rrb))#define RRB_CLEAR(rrb)		      (0x00000001 << (rrb)) /* validate that the slot and virtual channel are valid */#define VALIDATE_SLOT_n_VCHAN(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. */ static intdo_pcibr_rrb_count_valid(pcibr_soft_t pcibr_soft,			 pciio_slot_t slot,			 int vchan){    uint64_t tmp;    uint16_t enable_bit, vchan_bits, pdev_bits, rrb_bits;    int rrb_index, cnt=0;    if (!VALIDATE_SLOT_n_VCHAN(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;    vchan_bits = vchan << NUM_PDEV_BITS;    pdev_bits = SLOT_2_PDEV(slot);    rrb_bits = enable_bit | vchan_bits | pdev_bits;        tmp = pcireg_rrb_get(pcibr_soft, SLOT_2_RRB_REG(slot));        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. */ static intdo_pcibr_rrb_count_avail(pcibr_soft_t pcibr_soft,			 pciio_slot_t slot){    uint64_t tmp;    uint16_t enable_bit;    int rrb_index, cnt=0;        if (!VALIDATE_SLOT_n_VCHAN(slot, 0)) {	printk(KERN_WARNING "do_pcibr_rrb_count_avail() invalid slot/vchan");	return 0;    }        enable_bit = RRB_ENABLE_BIT;        tmp = pcireg_rrb_get(pcibr_soft, SLOT_2_RRB_REG(slot));        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. */ static intdo_pcibr_rrb_alloc(pcibr_soft_t pcibr_soft,		   pciio_slot_t slot,		   int vchan,		   int more){    uint64_t reg, tmp = 0;    uint16_t enable_bit, vchan_bits, pdev_bits, rrb_bits;    int rrb_index;        if (!VALIDATE_SLOT_n_VCHAN(slot, vchan)) {	printk(KERN_WARNING "do_pcibr_rrb_alloc() invalid slot/vchan");	return -1;    }        enable_bit = RRB_ENABLE_BIT;    vchan_bits = vchan << NUM_PDEV_BITS;    pdev_bits = SLOT_2_PDEV(slot);    rrb_bits = enable_bit | vchan_bits | pdev_bits;        reg = tmp = pcireg_rrb_get(pcibr_soft, SLOT_2_RRB_REG(slot));        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);    }        pcireg_rrb_set(pcibr_soft, SLOT_2_RRB_REG(slot), reg);    return (more ? -1 : 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. */static voiddo_pcibr_rrb_clear(pcibr_soft_t pcibr_soft, int rrb){    uint64_t             status;    /* bridge_lock must be held;  this RRB must be disabled. */    /* wait until RRB has no outstanduing XIO packets. */    status = pcireg_rrb_status_get(pcibr_soft);    while (status & RRB_INUSE(rrb)) {	status = pcireg_rrb_status_get(pcibr_soft);    }    /* if the RRB has data, drain it. */    if (status & RRB_VALID(rrb)) {	pcireg_rrb_clear_set(pcibr_soft, RRB_CLEAR(rrb));	/* wait until RRB is no longer valid. */	status = pcireg_rrb_status_get(pcibr_soft);	while (status & RRB_VALID(rrb)) {	    status = pcireg_rrb_status_get(pcibr_soft);	}    }} /*   * 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. */ static intdo_pcibr_rrb_free(pcibr_soft_t pcibr_soft,		  pciio_slot_t slot,		  int vchan,		  int less){    uint64_t reg, tmp = 0, clr = 0;    uint16_t enable_bit, vchan_bits, pdev_bits, rrb_bits;    int rrb_index;        if (!VALIDATE_SLOT_n_VCHAN(slot, vchan)) {	printk(KERN_WARNING "do_pcibr_rrb_free() invalid slot/vchan");	return -1;    }        enable_bit = RRB_ENABLE_BIT;    vchan_bits = vchan << NUM_PDEV_BITS;    pdev_bits = SLOT_2_PDEV(slot);    rrb_bits = enable_bit | vchan_bits | pdev_bits;        reg = tmp = pcireg_rrb_get(pcibr_soft, SLOT_2_RRB_REG(slot));        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 << (RRB_SIZE * rrb_index));	    clr = clr | (enable_bit << (RRB_SIZE * rrb_index));	    less--;	}	tmp = (tmp >> RRB_SIZE);    }        pcireg_rrb_set(pcibr_soft, SLOT_2_RRB_REG(slot), 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(slot);	if (clr & (enable_bit << (RRB_SIZE * rrb_index)))	    do_pcibr_rrb_clear(pcibr_soft, (2 * rrb_index) + evn_odd);    }        return (less ? -1 : 0);} /*  * 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(). */static voiddo_pcibr_rrb_flush(pcibr_soft_t pcibr_soft, int rrbn){    uint64_t	rrbv;    int		shft = (RRB_SIZE * (rrbn >> 1));    uint64_t	ebit = RRB_ENABLE_BIT << shft;    rrbv = pcireg_rrb_get(pcibr_soft, (rrbn & 1));    if (rrbv & ebit) {	pcireg_rrb_set(pcibr_soft, (rrbn & 1), (rrbv & ~ebit));    }    do_pcibr_rrb_clear(pcibr_soft, rrbn);    if (rrbv & ebit) {	pcireg_rrb_set(pcibr_soft, (rrbn & 1), rrbv);    }}/*   * free all the rrbs (both the normal and virtual channels) for the * specified slot. */ voiddo_pcibr_rrb_free_all(pcibr_soft_t pcibr_soft,		      pciio_slot_t slot){    int vchan;    int vchan_total = NUMBER_VCHANNELS;        /* pretend we own all 8 rrbs and just ignore the return value */    for (vchan = 0; vchan < vchan_total; vchan++) {	    do_pcibr_rrb_free(pcibr_soft, slot, vchan, 8);	    pcibr_soft->bs_rrb_valid[slot][vchan] = 0;    }}/* * Initialize a slot with a given number of RRBs.  (this routine * will also give back RRBs if the slot has more than we want). */voidpcibr_rrb_alloc_init(pcibr_soft_t pcibr_soft,		     int slot,		     int vchan,		     int init_rrbs){    int			 had = pcibr_soft->bs_rrb_valid[slot][vchan];    int			 have = had;    int			 added = 0;    for (added = 0; have < init_rrbs; ++added, ++have) {	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(pcibr_soft, slot, vchan, 1) < 0)	    break;	pcibr_soft->bs_rrb_valid[slot][vchan]++;    }    /* Free any extra RRBs that the slot may have allocated to it */    while (have > init_rrbs) {	pcibr_soft->bs_rrb_avail[slot & 1]++;	pcibr_soft->bs_rrb_valid[slot][vchan]--;	do_pcibr_rrb_free(pcibr_soft, slot, vchan, 1);	added--;	have--;    }    PCIBR_DEBUG_ALWAYS((PCIBR_DEBUG_RRB, pcibr_soft->bs_vhdl,		"pcibr_rrb_alloc_init: had %d, added/removed %d, "		"(of requested %d) RRBs "		"to slot %d, vchan %d\n", had, added, init_rrbs,		PCIBR_DEVICE_TO_SLOT(pcibr_soft, slot), vchan));    pcibr_rrb_debug("pcibr_rrb_alloc_init", pcibr_soft);}/* * Allocate more RRBs to a given slot (if the RRBs are available). */voidpcibr_rrb_alloc_more(pcibr_soft_t pcibr_soft,		     int slot,		     int vchan, 		     int more_rrbs){    int			 added;    for (added = 0; added < more_rrbs; ++added) {	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(pcibr_soft, slot, vchan, 1) < 0)	    break;	pcibr_soft->bs_rrb_valid[slot][vchan]++;    }    PCIBR_DEBUG_ALWAYS((PCIBR_DEBUG_RRB, pcibr_soft->bs_vhdl,		"pcibr_rrb_alloc_more: added %d (of %d requested) RRBs "		"to slot %d, vchan %d\n", added, more_rrbs, 		PCIBR_DEVICE_TO_SLOT(pcibr_soft, slot), vchan));    pcibr_rrb_debug("pcibr_rrb_alloc_more", 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);    uint64_t tmp;    uint16_t enable_bit, pdev_bits, rrb_bits, rrb_mask;    int rrb_index;    unsigned long s;    enable_bit = RRB_ENABLE_BIT;    pdev_bits = SLOT_2_PDEV(slot);    rrb_bits = enable_bit | pdev_bits;    rrb_mask = enable_bit | ((NUM_PDEV_BITS << 1) - 1);    tmp = pcireg_rrb_get(pcibr_soft, SLOT_2_RRB_REG(slot));    s = pcibr_lock(pcibr_soft);    for (rrb_index = 0; rrb_index < 8; rrb_index++) {	int evn_odd = SLOT_2_RRB_REG(slot);	if ((tmp & rrb_mask) == rrb_bits)	    do_pcibr_rrb_flush(pcibr_soft, (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);    pcireg_wrb_flush_get(pcibr_soft, pciio_slot);    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,

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?