📄 io.c
字号:
/* $Id: io.c,v 1.1.1.1 2004/02/04 12:55:32 laputa Exp $ * * 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 Silicon Graphics, Inc. * Copyright (C) 2000 by Colin Ngam */#include <linux/types.h>#include <linux/config.h>#include <linux/slab.h>#include <asm/sn/types.h>#include <asm/sn/sgi.h>#include <asm/sn/iobus.h>#include <asm/sn/iograph.h>#include <asm/param.h>#include <asm/sn/pio.h>#include <asm/sn/xtalk/xwidget.h>#include <asm/sn/sn_private.h>#include <asm/sn/addrs.h>#include <asm/sn/invent.h>#include <asm/sn/hcl.h>#include <asm/sn/hcl_util.h>#include <asm/sn/agent.h>#include <asm/sn/intr.h>#include <asm/sn/xtalk/xtalkaddrs.h>#include <asm/sn/klconfig.h>#include <asm/sn/io.h>#include <asm/sn/sn_cpuid.h>extern xtalk_provider_t hub_provider;/* * Perform any initializations needed to support hub-based I/O. * Called once during startup. */voidhubio_init(void){#ifdef LATER /* This isn't needed unless we port the entire sio driver ... */ extern void early_brl1_port_init( void ); early_brl1_port_init();#endif}/* * Implementation of hub iobus operations. * * Hub provides a crosstalk "iobus" on IP27 systems. These routines * provide a platform-specific implementation of xtalk used by all xtalk * cards on IP27 systems. * * Called from corresponding xtalk_* routines. *//* PIO MANAGEMENT *//* For mapping system virtual address space to xtalk space on a specified widget *//* * Setup pio structures needed for a particular hub. */static voidhub_pio_init(devfs_handle_t hubv){ xwidgetnum_t widget; hubinfo_t hubinfo; nasid_t nasid; int bigwin; hub_piomap_t hub_piomap; hubinfo_get(hubv, &hubinfo); nasid = hubinfo->h_nasid; /* Initialize small window piomaps for this hub */ for (widget=0; widget <= HUB_WIDGET_ID_MAX; widget++) { hub_piomap = hubinfo_swin_piomap_get(hubinfo, (int)widget); hub_piomap->hpio_xtalk_info.xp_target = widget; hub_piomap->hpio_xtalk_info.xp_xtalk_addr = 0; hub_piomap->hpio_xtalk_info.xp_mapsz = SWIN_SIZE; hub_piomap->hpio_xtalk_info.xp_kvaddr = (caddr_t)NODE_SWIN_BASE(nasid, widget); hub_piomap->hpio_hub = hubv; hub_piomap->hpio_flags = HUB_PIOMAP_IS_VALID; } /* Initialize big window piomaps for this hub */ for (bigwin=0; bigwin < HUB_NUM_BIG_WINDOW; bigwin++) { hub_piomap = hubinfo_bwin_piomap_get(hubinfo, bigwin); hub_piomap->hpio_xtalk_info.xp_mapsz = BWIN_SIZE; hub_piomap->hpio_hub = hubv; hub_piomap->hpio_holdcnt = 0; hub_piomap->hpio_flags = HUB_PIOMAP_IS_BIGWINDOW; IIO_ITTE_DISABLE(nasid, bigwin); } hub_set_piomode(nasid, HUB_PIO_CONVEYOR); mutex_spinlock_init(&hubinfo->h_bwlock);/* * If this lock can be acquired from interrupts or bh's, add SV_INTS or SV_BHS, * respectively, to the flags here. */ sv_init(&hubinfo->h_bwwait, &hubinfo->h_bwlock, SV_ORDER_FIFO | SV_MON_SPIN); }/* * Create a caddr_t-to-xtalk_addr mapping. * * Use a small window if possible (that's the usual case), but * manage big windows if needed. Big window mappings can be * either FIXED or UNFIXED -- we keep at least 1 big window available * for UNFIXED mappings. * * Returns an opaque pointer-sized type which can be passed to * other hub_pio_* routines on success, or NULL if the request * cannot be satisfied. *//* ARGSUSED */hub_piomap_thub_piomap_alloc(devfs_handle_t dev, /* set up mapping for this device */ device_desc_t dev_desc, /* device descriptor */ iopaddr_t xtalk_addr, /* map for this xtalk_addr range */ size_t byte_count, size_t byte_count_max, /* maximum size of a mapping */ unsigned flags) /* defined in sys/pio.h */{ xwidget_info_t widget_info = xwidget_info_get(dev); xwidgetnum_t widget = xwidget_info_id_get(widget_info); devfs_handle_t hubv = xwidget_info_master_get(widget_info); hubinfo_t hubinfo; hub_piomap_t bw_piomap; int bigwin, free_bw_index; nasid_t nasid; volatile hubreg_t junk; unsigned long s; /* sanity check */ if (byte_count_max > byte_count) return(NULL); hubinfo_get(hubv, &hubinfo); /* If xtalk_addr range is mapped by a small window, we don't have * to do much */ if (xtalk_addr + byte_count <= SWIN_SIZE) return(hubinfo_swin_piomap_get(hubinfo, (int)widget)); /* We need to use a big window mapping. */ /* * TBD: Allow requests that would consume multiple big windows -- * split the request up and use multiple mapping entries. * For now, reject requests that span big windows. */ if ((xtalk_addr % BWIN_SIZE) + byte_count > BWIN_SIZE) return(NULL); /* Round xtalk address down for big window alignement */ xtalk_addr = xtalk_addr & ~(BWIN_SIZE-1); /* * Check to see if an existing big window mapping will suffice. */tryagain: free_bw_index = -1; s = mutex_spinlock(&hubinfo->h_bwlock); for (bigwin=0; bigwin < HUB_NUM_BIG_WINDOW; bigwin++) { bw_piomap = hubinfo_bwin_piomap_get(hubinfo, bigwin); /* If mapping is not valid, skip it */ if (!(bw_piomap->hpio_flags & HUB_PIOMAP_IS_VALID)) { free_bw_index = bigwin; continue; } /* * If mapping is UNFIXED, skip it. We don't allow sharing * of UNFIXED mappings, because this would allow starvation. */ if (!(bw_piomap->hpio_flags & HUB_PIOMAP_IS_FIXED)) continue; if ( xtalk_addr == bw_piomap->hpio_xtalk_info.xp_xtalk_addr && widget == bw_piomap->hpio_xtalk_info.xp_target) { bw_piomap->hpio_holdcnt++; mutex_spinunlock(&hubinfo->h_bwlock, s); return(bw_piomap); } } /* * None of the existing big window mappings will work for us -- * we need to establish a new mapping. */ /* Insure that we don't consume all big windows with FIXED mappings */ if (flags & PIOMAP_FIXED) { if (hubinfo->h_num_big_window_fixed < HUB_NUM_BIG_WINDOW-1) { ASSERT(free_bw_index >= 0); hubinfo->h_num_big_window_fixed++; } else { bw_piomap = NULL; goto done; } } else /* PIOMAP_UNFIXED */ { if (free_bw_index < 0) { if (flags & PIOMAP_NOSLEEP) { bw_piomap = NULL; goto done; } sv_wait(&hubinfo->h_bwwait, 0, 0); goto tryagain; } } /* OK! Allocate big window free_bw_index for this mapping. */ /* * The code below does a PIO write to setup an ITTE entry. * We need to prevent other CPUs from seeing our updated memory * shadow of the ITTE (in the piomap) until the ITTE entry is * actually set up; otherwise, another CPU might attempt a PIO * prematurely. * * Also, the only way we can know that an entry has been received * by the hub and can be used by future PIO reads/writes is by * reading back the ITTE entry after writing it. * * For these two reasons, we PIO read back the ITTE entry after * we write it. */ nasid = hubinfo->h_nasid; IIO_ITTE_PUT(nasid, free_bw_index, HUB_PIO_MAP_TO_MEM, widget, xtalk_addr); junk = HUB_L(IIO_ITTE_GET(nasid, free_bw_index)); bw_piomap = hubinfo_bwin_piomap_get(hubinfo, free_bw_index); bw_piomap->hpio_xtalk_info.xp_dev = dev; bw_piomap->hpio_xtalk_info.xp_target = widget; bw_piomap->hpio_xtalk_info.xp_xtalk_addr = xtalk_addr; bw_piomap->hpio_xtalk_info.xp_kvaddr = (caddr_t)NODE_BWIN_BASE(nasid, free_bw_index); bw_piomap->hpio_holdcnt++; bw_piomap->hpio_bigwin_num = free_bw_index; if (flags & PIOMAP_FIXED) bw_piomap->hpio_flags |= HUB_PIOMAP_IS_VALID | HUB_PIOMAP_IS_FIXED; else bw_piomap->hpio_flags |= HUB_PIOMAP_IS_VALID;done: mutex_spinunlock(&hubinfo->h_bwlock, s); return(bw_piomap);}/* * hub_piomap_free destroys a caddr_t-to-xtalk pio mapping and frees * any associated mapping resources. * * If this * piomap was handled with a small window, or if it was handled * in a big window that's still in use by someone else, then there's * nothing to do. On the other hand, if this mapping was handled * with a big window, AND if we were the final user of that mapping, * then destroy the mapping. */voidhub_piomap_free(hub_piomap_t hub_piomap){ devfs_handle_t hubv; hubinfo_t hubinfo; nasid_t nasid; unsigned long s; /* * Small windows are permanently mapped to corresponding widgets, * so there're no resources to free. */ if (!(hub_piomap->hpio_flags & HUB_PIOMAP_IS_BIGWINDOW)) return; ASSERT(hub_piomap->hpio_flags & HUB_PIOMAP_IS_VALID); ASSERT(hub_piomap->hpio_holdcnt > 0); hubv = hub_piomap->hpio_hub; hubinfo_get(hubv, &hubinfo); nasid = hubinfo->h_nasid; s = mutex_spinlock(&hubinfo->h_bwlock); /* * If this is the last hold on this mapping, free it. */ if (--hub_piomap->hpio_holdcnt == 0) { IIO_ITTE_DISABLE(nasid, hub_piomap->hpio_bigwin_num ); if (hub_piomap->hpio_flags & HUB_PIOMAP_IS_FIXED) { hub_piomap->hpio_flags &= ~(HUB_PIOMAP_IS_VALID | HUB_PIOMAP_IS_FIXED); hubinfo->h_num_big_window_fixed--; ASSERT(hubinfo->h_num_big_window_fixed >= 0); } else hub_piomap->hpio_flags &= ~HUB_PIOMAP_IS_VALID; (void)sv_signal(&hubinfo->h_bwwait); } mutex_spinunlock(&hubinfo->h_bwlock, s);}/* * Establish a mapping to a given xtalk address range using the resources * allocated earlier. */caddr_thub_piomap_addr(hub_piomap_t hub_piomap, /* mapping resources */ iopaddr_t xtalk_addr, /* map for this xtalk address */ size_t byte_count) /* map this many bytes */{ /* Verify that range can be mapped using the specified piomap */ if (xtalk_addr < hub_piomap->hpio_xtalk_info.xp_xtalk_addr) return(0); if (xtalk_addr + byte_count > ( hub_piomap->hpio_xtalk_info.xp_xtalk_addr + hub_piomap->hpio_xtalk_info.xp_mapsz)) return(0); if (hub_piomap->hpio_flags & HUB_PIOMAP_IS_VALID) return(hub_piomap->hpio_xtalk_info.xp_kvaddr + (xtalk_addr % hub_piomap->hpio_xtalk_info.xp_mapsz)); else return(0);}/* * Driver indicates that it's done with PIO's from an earlier piomap_addr. *//* ARGSUSED */voidhub_piomap_done(hub_piomap_t hub_piomap) /* done with these mapping resources */{ /* Nothing to do */}/* * For translations that require no mapping resources, supply a kernel virtual * address that maps to the specified xtalk address range. *//* ARGSUSED */caddr_thub_piotrans_addr( devfs_handle_t dev, /* translate to this device */ device_desc_t dev_desc, /* device descriptor */ iopaddr_t xtalk_addr, /* Crosstalk address */ size_t byte_count, /* map this many bytes */ unsigned flags) /* (currently unused) */{ xwidget_info_t widget_info = xwidget_info_get(dev); xwidgetnum_t widget = xwidget_info_id_get(widget_info); devfs_handle_t hubv = xwidget_info_master_get(widget_info); hub_piomap_t hub_piomap; hubinfo_t hubinfo; hubinfo_get(hubv, &hubinfo); if (xtalk_addr + byte_count <= SWIN_SIZE) { hub_piomap = hubinfo_swin_piomap_get(hubinfo, (int)widget); return(hub_piomap_addr(hub_piomap, xtalk_addr, byte_count)); } else return(0);}/* DMA MANAGEMENT *//* Mapping from crosstalk space to system physical space *//* * There's not really very much to do here, since crosstalk maps * directly to system physical space. It's quite possible that this * DMA layer will be bypassed in performance kernels. *//* ARGSUSED */static voidhub_dma_init(devfs_handle_t hubv){}/* * Allocate resources needed to set up DMA mappings up to a specified size * on a specified adapter. * * We don't actually use the adapter ID for anything. It's just the adapter * that the lower level driver plans to use for DMA. *//* ARGSUSED */hub_dmamap_thub_dmamap_alloc( devfs_handle_t dev, /* set up mappings for this device */ device_desc_t dev_desc, /* device descriptor */ size_t byte_count_max, /* max size of a mapping */ unsigned flags) /* defined in dma.h */{ hub_dmamap_t dmamap; xwidget_info_t widget_info = xwidget_info_get(dev); xwidgetnum_t widget = xwidget_info_id_get(widget_info); devfs_handle_t hubv = xwidget_info_master_get(widget_info); dmamap = kern_malloc(sizeof(struct hub_dmamap_s)); dmamap->hdma_xtalk_info.xd_dev = dev; dmamap->hdma_xtalk_info.xd_target = widget; dmamap->hdma_hub = hubv; dmamap->hdma_flags = HUB_DMAMAP_IS_VALID; if (flags & XTALK_FIXED) dmamap->hdma_flags |= HUB_DMAMAP_IS_FIXED; return(dmamap);}/* * Destroy a DMA mapping from crosstalk space to system address space. * There is no actual mapping hardware to destroy, but we at least mark * the dmamap INVALID and free the space that it took. */voidhub_dmamap_free(hub_dmamap_t hub_dmamap){ hub_dmamap->hdma_flags &= ~HUB_DMAMAP_IS_VALID; kern_free(hub_dmamap);}/* * Establish a DMA mapping using the resources allocated in a previous dmamap_alloc. * Return an appropriate crosstalk address range that maps to the specified physical
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -