xbow.c

来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 1,021 行 · 第 1/2 页

C
1,021
字号
/* * This file is subject to the terms and conditions of the GNU General Public * License.  See the file "COPYING" in the main directory of this archive * for more details. * * Copyright (c) 1992-1997,2000-2003 Silicon Graphics, Inc. All rights reserved. */#include <linux/slab.h>#include <linux/module.h>#include <linux/interrupt.h>#include <linux/mm.h>#include <linux/delay.h>#include <asm/sn/sgi.h>#include <asm/sn/sn2/sn_private.h>#include <asm/sn/iograph.h>#include <asm/sn/simulator.h>#include <asm/sn/hcl.h>#include <asm/sn/hcl_util.h>#include <asm/sn/pci/pcibr_private.h>/* #define DEBUG		1 *//* #define XBOW_DEBUG	1 */#define kdebug 0/* * This file supports the Xbow chip.  Main functions: initializtion, * error handling. *//* * each vertex corresponding to an xbow chip * has a "fastinfo" pointer pointing at one * of these things. */struct xbow_soft_s {    vertex_hdl_t            conn;	/* our connection point */    vertex_hdl_t            vhdl;	/* xbow's private vertex */    vertex_hdl_t            busv;	/* the xswitch vertex */    xbow_t                 *base;	/* PIO pointer to crossbow chip */    char                   *name;	/* hwgraph name */    xbow_link_status_t      xbow_link_status[MAX_XBOW_PORTS];    widget_cfg_t	   *wpio[MAX_XBOW_PORTS];	/* cached PIO pointer */    /* Bandwidth allocation state. Bandwidth values are for the     * destination port since contention happens there.     * Implicit mapping from xbow ports (8..f) -> (0..7) array indices.     */    unsigned long long	    bw_hiwm[MAX_XBOW_PORTS];	/* hiwater mark values */    unsigned long long      bw_cur_used[MAX_XBOW_PORTS]; /* bw used currently */};#define xbow_soft_set(v,i)	hwgraph_fastinfo_set((v), (arbitrary_info_t)(i))#define xbow_soft_get(v)	((struct xbow_soft_s *)hwgraph_fastinfo_get((v)))/* * Function Table of Contents */int                     xbow_attach(vertex_hdl_t);int                     xbow_widget_present(xbow_t *, int);static int              xbow_link_alive(xbow_t *, int);vertex_hdl_t            xbow_widget_lookup(vertex_hdl_t, int);void                    xbow_intr_preset(void *, int, xwidgetnum_t, iopaddr_t, xtalk_intr_vector_t);static void		xbow_setwidint(xtalk_intr_t);xswitch_reset_link_f    xbow_reset_link;xswitch_provider_t      xbow_provider ={    xbow_reset_link,};static intxbow_mmap(struct file * file, struct vm_area_struct * vma){        unsigned long           phys_addr;        int                     error;        phys_addr = (unsigned long)file->private_data & ~0xc000000000000000; /* Mask out the Uncache bits */        vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);        vma->vm_flags |= VM_RESERVED | VM_IO;        error = io_remap_page_range(vma, vma->vm_start, phys_addr,                                   vma->vm_end-vma->vm_start,                                   vma->vm_page_prot);        return(error);}/* * This is the file operation table for the pcibr driver. * As each of the functions are implemented, put the * appropriate function name below. */struct file_operations xbow_fops = {        .owner		= THIS_MODULE,        .mmap		= xbow_mmap,};#ifdef XBRIDGE_REGS_SIM/*    xbow_set_simulated_regs: sets xbow regs as needed *	for powering through the boot */voidxbow_set_simulated_regs(xbow_t *xbow, int port){    /*     * turn on link     */    xbow->xb_link(port).link_status = (1<<31);    /*     * and give it a live widget too     */    xbow->xb_link(port).link_aux_status = XB_AUX_STAT_PRESENT;    /*     * zero the link control reg     */    xbow->xb_link(port).link_control = 0x0;}#endif /* XBRIDGE_REGS_SIM *//* *    xbow_attach: the crosstalk provider has *      determined that there is a crossbow widget *      present, and has handed us the connection *      point for that vertex. * *      We not only add our own vertex, but add *      some "xtalk switch" data to the switch *      vertex (at the connect point's parent) if *      it does not have any. *//*ARGSUSED */intxbow_attach(vertex_hdl_t conn){    /*REFERENCED */    vertex_hdl_t            vhdl;    vertex_hdl_t            busv;    xbow_t                  *xbow;    struct xbow_soft_s      *soft;    int                     port;    xswitch_info_t          info;    xtalk_intr_t            intr_hdl;    char                    devnm[MAXDEVNAME], *s;    xbowreg_t               id;    int                     rev;    int			    i;    int			    xbow_num;#if DEBUG && ATTACH_DEBUG    char		    name[MAXDEVNAME];#endif    static irqreturn_t xbow_errintr_handler(int, void *, struct pt_regs *);	#if DEBUG && ATTACH_DEBUG    printk("%s: xbow_attach\n", vertex_to_name(conn, name, MAXDEVNAME));#endif    /*     * Get a PIO pointer to the base of the crossbow     * chip.     */#ifdef XBRIDGE_REGS_SIM    printk("xbow_attach: XBRIDGE_REGS_SIM FIXME: allocating %ld bytes for xbow_s\n", sizeof(xbow_t));    xbow = (xbow_t *) kmalloc(sizeof(xbow_t), GFP_KERNEL);    if (!xbow)	    return -ENOMEM;    /*     * turn on ports e and f like in a real live ibrick     */    xbow_set_simulated_regs(xbow, 0xe);    xbow_set_simulated_regs(xbow, 0xf);#else    xbow = (xbow_t *) xtalk_piotrans_addr(conn, 0, 0, sizeof(xbow_t), 0);#endif /* XBRIDGE_REGS_SIM */    /*     * Locate the "switch" vertex: it is the parent     * of our connection point.     */    busv = hwgraph_connectpt_get(conn);#if DEBUG && ATTACH_DEBUG    printk("xbow_attach: Bus Vertex 0x%p, conn 0x%p, xbow register 0x%p wid= 0x%x\n", busv, conn, xbow, *(volatile u32 *)xbow);#endif    ASSERT(busv != GRAPH_VERTEX_NONE);    /*     * Create our private vertex, and connect our     * driver information to it. This makes it possible     * for diagnostic drivers to open the crossbow     * vertex for access to registers.     */    /*     * Register a xbow driver with hwgraph.     * file ops.     */    vhdl = hwgraph_register(conn, EDGE_LBL_XBOW, 0,	   0, 0, 0,	   S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP, 0, 0,	   (struct file_operations *)&xbow_fops, (void *)xbow);    if (!vhdl) {        printk(KERN_WARNING "xbow_attach: Unable to create char device for xbow conn %p\n",                (void *)conn);    }    /*     * Allocate the soft state structure and attach     * it to the xbow's vertex     */    soft = kmalloc(sizeof(*soft), GFP_KERNEL);    if (!soft)	    return -ENOMEM;    soft->conn = conn;    soft->vhdl = vhdl;    soft->busv = busv;    soft->base = xbow;    /* does the universe really need another macro?  */    /* xbow_soft_set(vhdl, (arbitrary_info_t) soft); */    /* hwgraph_fastinfo_set(vhdl, (arbitrary_info_t) soft); */#define XBOW_NUM_SUFFIX_FORMAT	"[xbow# %d]"    /* Add xbow number as a suffix to the hwgraph name of the xbow.     * This is helpful while looking at the error/warning messages.     */    xbow_num = 0;    /*     * get the name of this xbow vertex and keep the info.     * This is needed during errors and interupts, but as     * long as we have it, we can use it elsewhere.     */    s = dev_to_name(vhdl, devnm, MAXDEVNAME);    soft->name = kmalloc(strlen(s) + strlen(XBOW_NUM_SUFFIX_FORMAT) + 1, 			    GFP_KERNEL);    if (!soft->name) {	    kfree(soft);	    return -ENOMEM;    }    sprintf(soft->name,"%s"XBOW_NUM_SUFFIX_FORMAT, s,xbow_num);#ifdef XBRIDGE_REGS_SIM    /* my o200/ibrick has id=0x2d002049, but XXBOW_WIDGET_PART_NUM is defined     * as 0xd000, so I'm using that for the partnum bitfield.     */    printk("xbow_attach: XBRIDGE_REGS_SIM FIXME: need xb_wid_id value!!\n");    id = 0x2d000049;#else    id = xbow->xb_wid_id;#endif /* XBRIDGE_REGS_SIM */    rev = XWIDGET_PART_REV_NUM(id);#define XBOW_16_BIT_PORT_BW_MAX		(800 * 1000 * 1000)	/* 800 MB/s */    /* Set bandwidth hiwatermark and current values */    for (i = 0; i < MAX_XBOW_PORTS; i++) {	soft->bw_hiwm[i] = XBOW_16_BIT_PORT_BW_MAX;	/* for now */	soft->bw_cur_used[i] = 0;    }     /*      * attach the crossbow error interrupt.      */     intr_hdl = xtalk_intr_alloc(conn, (device_desc_t)0, vhdl);     ASSERT(intr_hdl != NULL);        {                int irq = ((hub_intr_t)intr_hdl)->i_bit;                int cpu = ((hub_intr_t)intr_hdl)->i_cpuid;                intr_unreserve_level(cpu, irq);                ((hub_intr_t)intr_hdl)->i_bit = SGI_XBOW_ERROR;        }      xtalk_intr_connect(intr_hdl,                        (intr_func_t) xbow_errintr_handler,                        (intr_arg_t) soft,                        (xtalk_intr_setfunc_t) xbow_setwidint,                        (void *) xbow);     request_irq(SGI_XBOW_ERROR, (void *)xbow_errintr_handler, SA_SHIRQ, "XBOW error",			(intr_arg_t) soft);     /*     * Enable xbow error interrupts     */    xbow->xb_wid_control = (XB_WID_CTRL_REG_ACC_IE | XB_WID_CTRL_XTALK_IE);    /*     * take a census of the widgets present,     * leaving notes at the switch vertex.     */    info = xswitch_info_new(busv);    for (port = MAX_PORT_NUM - MAX_XBOW_PORTS;	 port < MAX_PORT_NUM; ++port) {	if (!xbow_link_alive(xbow, port)) {#if DEBUG && XBOW_DEBUG	    printk(KERN_INFO "0x%p link %d is not alive\n",		    (void *)busv, port);#endif	    continue;	}	if (!xbow_widget_present(xbow, port)) {#if DEBUG && XBOW_DEBUG	    printk(KERN_INFO "0x%p link %d is alive but no widget is present\n", (void *)busv, port);#endif	    continue;	}#if DEBUG && XBOW_DEBUG	printk(KERN_INFO "0x%p link %d has a widget\n",		(void *)busv, port);#endif	xswitch_info_link_is_ok(info, port);	/*	 * Turn some error interrupts on	 * and turn others off. The PROM has	 * some things turned on we don't	 * want to see (bandwidth allocation	 * errors for instance); so if it	 * is not listed here, it is not on.	 */	xbow->xb_link(port).link_control =	    ( (xbow->xb_link(port).link_control	/*	 * Turn off these bits; they are non-fatal,	 * but we might want to save some statistics	 * on the frequency of these errors.	 * XXX FIXME XXX	 */	    & ~XB_CTRL_RCV_CNT_OFLOW_IE	    & ~XB_CTRL_XMT_CNT_OFLOW_IE	    & ~XB_CTRL_BNDWDTH_ALLOC_IE	    & ~XB_CTRL_RCV_IE)	/*	 * These are the ones we want to turn on.	 */	    | (XB_CTRL_ILLEGAL_DST_IE	    | XB_CTRL_OALLOC_IBUF_IE	    | XB_CTRL_XMT_MAX_RTRY_IE	    | XB_CTRL_MAXREQ_TOUT_IE	    | XB_CTRL_XMT_RTRY_IE	    | XB_CTRL_SRC_TOUT_IE) );    }    xswitch_provider_register(busv, &xbow_provider);    return 0;				/* attach successful */}/* * xbow_widget_present: See if a device is present * on the specified port of this crossbow. */intxbow_widget_present(xbow_t *xbow, int port){	if ( IS_RUNNING_ON_SIMULATOR() ) {		if ( (port == 14) || (port == 15) ) {			return 1;		}		else {			return 0;		}	}	else {		/* WAR: port 0xf on PIC is missing present bit */		if (XBOW_WAR_ENABLED(PV854827, xbow->xb_wid_id) &&					IS_PIC_XBOW(xbow->xb_wid_id) && port==0xf) {			return 1;		}		else if ( IS_PIC_XBOW(xbow->xb_wid_id) && port==0xb ) {			/* for opus the present bit doesn't work on port 0xb */			return 1;		}		return xbow->xb_link(port).link_aux_status & XB_AUX_STAT_PRESENT;	}}static intxbow_link_alive(xbow_t * xbow, int port){    xbwX_stat_t             xbow_linkstat;    xbow_linkstat.linkstatus = xbow->xb_link(port).link_status;    return (xbow_linkstat.link_alive);}/* * xbow_widget_lookup *      Lookup the edges connected to the xbow specified, and *      retrieve the handle corresponding to the widgetnum *      specified. *      If not found, return 0. */vertex_hdl_txbow_widget_lookup(vertex_hdl_t vhdl,		   int widgetnum){    xswitch_info_t          xswitch_info;    vertex_hdl_t            conn;    xswitch_info = xswitch_info_get(vhdl);    conn = xswitch_info_vhdl_get(xswitch_info, widgetnum);    return conn;}/* * xbow_setwidint: called when xtalk * is establishing or migrating our * interrupt service. */static voidxbow_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);    xbow_t                 *xbow = (xbow_t *) xtalk_intr_sfarg_get(intr);    xbow_intr_preset((void *) xbow, 0, targ, addr, vect);}/* * xbow_intr_preset: called during mlreset time * if the platform specific code needs to route * an xbow interrupt before the xtalk infrastructure * is available for use. * * Also called from xbow_setwidint, so we don't * replicate the guts of the routine. * * XXX- probably should be renamed xbow_wid_intr_set or * something to reduce confusion. *//*ARGSUSED3 */voidxbow_intr_preset(void *which_widget,		 int which_widget_intr,		 xwidgetnum_t targ,		 iopaddr_t addr,		 xtalk_intr_vector_t vect){    xbow_t                 *xbow = (xbow_t *) which_widget;    xbow->xb_wid_int_upper = ((0xFF000000 & (vect << 24)) |			      (0x000F0000 & (targ << 16)) |			      XTALK_ADDR_TO_UPPER(addr));    xbow->xb_wid_int_lower = XTALK_ADDR_TO_LOWER(addr);}#define	XEM_ADD_STR(s)		printk("%s", (s))#define	XEM_ADD_NVAR(n,v)	printk("\t%20s: 0x%llx\n", (n), ((unsigned long long)v))#define	XEM_ADD_VAR(v)		XEM_ADD_NVAR(#v,(v))#define XEM_ADD_IOEF(p,n)	if (IOERROR_FIELDVALID(ioe,n)) {	\				    IOERROR_GETVALUE(p,ioe,n);		\				    XEM_ADD_NVAR("ioe." #n, p);		\				}intxbow_xmit_retry_error(struct xbow_soft_s *soft,		      int port){    xswitch_info_t          info;    vertex_hdl_t            vhdl;    widget_cfg_t           *wid;    widgetreg_t             id;    int                     part;    int                     mfgr;    wid = soft->wpio[port - BASE_XBOW_PORT];    if (wid == NULL) {	/* If we can't track down a PIO	 * pointer to our widget yet,	 * leave our caller knowing that	 * we are interested in this	 * interrupt if it occurs in	 * the future.	 */	info = xswitch_info_get(soft->busv);	if (!info)	    return 1;	vhdl = xswitch_info_vhdl_get(info, port);	if (vhdl == GRAPH_VERTEX_NONE)	    return 1;	wid = (widget_cfg_t *) xtalk_piotrans_addr	    (vhdl, 0, 0, sizeof *wid, 0);	if (!wid)	    return 1;	soft->wpio[port - BASE_XBOW_PORT] = wid;    }    id = wid->w_id;    part = XWIDGET_PART_NUM(id);    mfgr = XWIDGET_MFG_NUM(id);    return 0;}

⌨️ 快捷键说明

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