iseries_dma.c

来自「是关于linux2.5.1的完全源码」· C语言 代码 · 共 1,050 行 · 第 1/2 页

C
1,050
字号
/* * iSeries_dma.c * Copyright (C) 2001 Mike Corrigan  IBM Corporation * * Dynamic DMA mapping support. *  * Manages the TCE space assigned to this partition *  * modeled from pci-dma.c * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. *  * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the * GNU General Public License for more details. *  * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA */#include <linux/init.h>#include <linux/types.h>#include <linux/slab.h>#include <linux/mm.h>#include <linux/spinlock.h>#include <linux/string.h>#include <linux/pci.h>#include <asm/io.h>#include <asm/iSeries/HvCallXm.h>#include <asm/iSeries/LparData.h>#include <asm/iSeries/iSeries_dma.h>#include <asm/iSeries/iSeries_pci.h>struct pci_dev  * iSeries_veth_dev = NULL;struct pci_dev  * iSeries_vio_dev  = NULL;struct TceTable   virtBusTceTable;	/* Tce table for virtual bus */struct TceTable * tceTables[256];	// Tce tables for 256 busses					// Bus 255 is the virtual bus					// zero indicates no bus defined		  // allocates a contiguous range of tces (power-of-2 size)static long       alloc_tce_range( struct TceTable *, 				   unsigned order );		  // allocates a contiguous range of tces (power-of-2 size)		  // assumes lock already heldstatic long       alloc_tce_range_nolock( struct TceTable *, 					  unsigned order );		  // frees a contiguous range of tces (power-of-2 size)static void       free_tce_range( struct TceTable *, 				  long tcenum, 				  unsigned order );		  // frees a contiguous rnage of tces (power-of-2 size)		  // assumes lock already heldstatic void       free_tce_range_nolock( struct TceTable *, 				  	 long tcenum, 					 unsigned order );		  // allocates a range of tces and sets them to the 		  // pages static 		  dma_addr_t get_tces( struct TceTable *, 				       unsigned order, 				       void *page, 			    	       unsigned numPages,			    	       int tceType,			    	       int direction );static void       free_tces( struct TceTable *, 			     dma_addr_t tce, 			     unsigned order,			     unsigned numPages );static long       test_tce_range( struct TceTable *, 				  long tcenum, 				  unsigned order );static unsigned   fill_scatterlist_sg( struct scatterlist *sg, int nents, 		        	       dma_addr_t dma_addr, unsigned long numTces );static unsigned long num_tces_sg( struct scatterlist *sg, 				  int nents );	static dma_addr_t create_tces_sg( struct TceTable *tbl, 				  struct scatterlist *sg, 			 	  int nents, 				  unsigned numTces,				  int tceType, 				  int direction );static unsigned __inline__ count_leading_zeros32( unsigned long x ){	unsigned lz;	asm("cntlzw %0,%1" : "=r"(lz) : "r"(x));	return lz;}static void __inline__ build_tce( struct TceTable * tbl, long tcenum, 			     unsigned long uaddr, int tceType, int direction ){	union Tce tce;		tce.wholeTce = 0;	tce.tceBits.rpn = (virt_to_absolute(uaddr)) >> PAGE_SHIFT;	// If for virtual bus	if ( tceType == TCE_VB ) {		tce.tceBits.valid = 1;		tce.tceBits.allIo = 1;		if ( direction != PCI_DMA_TODEVICE )			tce.tceBits.readWrite = 1;	}	// If for PCI bus	else {		tce.tceBits.readWrite = 1; // Read allowed 		if ( direction != PCI_DMA_TODEVICE )			tce.tceBits.pciWrite = 1;	}	HvCallXm_setTce( (u64)tbl->index, (u64)tcenum, tce.wholeTce );}// Build a TceTable structure.  This contains a multi-level bit map which// is used to manage allocation of the tce space.struct TceTable * build_tce_table( struct HvTceTableManagerCB * tceTableParms,				   struct TceTable * tbl ){	unsigned long bits, bytes, totalBytes;	unsigned long numBits[NUM_TCE_LEVELS], numBytes[NUM_TCE_LEVELS];	unsigned i, k, m;	unsigned char * pos, * p, b;	tbl->size = tceTableParms->size;	tbl->busNumber = tceTableParms->busNumber;	tbl->startOffset = tceTableParms->startOffset;	tbl->index = tceTableParms->index;	spin_lock_init( &(tbl->lock) );		tbl->mlbm.maxLevel = 0;	// Compute number of bits and bytes for each level of the	// multi-level bit map	// 	totalBytes = 0;	bits = tbl->size * (PAGE_SIZE / sizeof( union Tce ));		for ( i=0; i<NUM_TCE_LEVELS; ++i ) {		bytes = (bits+7)/8;#ifdef DEBUG_TCE		printk("build_tce_table: level %d bits=%ld, bytes=%ld\n", i, bits, bytes );#endif		numBits[i] = bits;		numBytes[i] = bytes;		bits /= 2;		/* we need extra space at the end that's a multiple of 8 bytes */		/* for the cntlzw algorithm to work correctly. */		/* but we don't want the bits turned on or used, so we don't muck with numBytes[i] */		totalBytes += (bytes + 7) & ~7;	}#ifdef DEBUG_TCE	printk("build_tce_table: totalBytes=%ld\n", totalBytes );#endif		pos = (char *)__get_free_pages( GFP_ATOMIC, get_order( totalBytes )); 	if ( !pos )		return NULL;	memset( pos, 0, totalBytes );		// For each level, fill in the pointer to the bit map,	// and turn on the last bit in the bit map (if the	// number of bits in the map is odd).  The highest	// level will get all of its bits turned on.		for (i=0; i<NUM_TCE_LEVELS; ++i) {		if ( numBytes[i] ) {			tbl->mlbm.level[i].map = pos;			tbl->mlbm.maxLevel = i;			if ( numBits[i] & 1 ) {				p = pos + numBytes[i] - 1;				m = (( numBits[i] % 8) - 1) & 7;				*p = 0x80 >> m;#ifdef DEBUG_TCE				printk("build_tce_table: level %d last bit %x\n", i, 0x80>>m );#endif							}		}		else			tbl->mlbm.level[i].map = 0;		/* see the comment up above on the totalBytes calculation for why we do this. */		pos += (numBytes[i] + 7) & ~7;		tbl->mlbm.level[i].numBits = numBits[i];		tbl->mlbm.level[i].numBytes = numBytes[i];			}	// For the highest level, turn on all the bits		i = tbl->mlbm.maxLevel;	p = tbl->mlbm.level[i].map;	m = numBits[i];#ifdef DEBUG_TCE	printk("build_tce_table: highest level (%d) has all bits set\n", i);#endif		for (k=0; k<numBytes[i]; ++k) {		if ( m >= 8 ) {			// handle full bytes			*p++ = 0xff;			m -= 8;		}		else {			// handle the last partial byte			b = 0x80;			*p = 0;			while (m) {				*p |= b;				b >>= 1;				--m;			}		}	}	return tbl;	}static long alloc_tce_range( struct TceTable *tbl, unsigned order ){	long retval;	unsigned long flags;		// Lock the tce allocation bitmap	spin_lock_irqsave( &(tbl->lock), flags );	// Do the actual work	retval = alloc_tce_range_nolock( tbl, order );		// Unlock the tce allocation bitmap	spin_unlock_irqrestore( &(tbl->lock), flags );	return retval;}static long alloc_tce_range_nolock( struct TceTable *tbl, unsigned order ){	unsigned long numBits, numBytes;	unsigned long i, bit, block, mask;	long tcenum;	u32 * map;	// If the order (power of 2 size) requested is larger than our	// biggest, indicate failure	if ( order > tbl->mlbm.maxLevel ) {#ifdef DEBUG_TCE		printk("alloc_tce_range_nolock: invalid order requested, order = %d\n", order );#endif		return -1;	}		numBits =  tbl->mlbm.level[order].numBits;	numBytes = tbl->mlbm.level[order].numBytes;	map =      (u32 *)(tbl->mlbm.level[order].map);	// Initialize return value to -1 (failure)	tcenum = -1;	// Loop through the bytes of the bitmap	for (i=0; i<numBytes/4; ++i) {		if ( *map ) {			// A free block is found, compute the block			// number (of this size)			bit = count_leading_zeros32( *map );			block = (i * 32) + bit;			// turn off the bit in the map to indicate			// that the block is now in use			mask = 0xffffffff ^ (0x80000000 >> bit);			*map &= mask;			// compute the index into our tce table for			// the first tce in the block#ifdef DEBUG_TCE			printk("alloc_tce_range_nolock: allocating block %ld, (byte=%ld, bit=%ld) order %d\n", block, i, bit, order );#endif			tcenum = block << order;			break;		}		++map;	}#ifdef DEBUG_TCE	if ( tcenum == -1 ) {		printk("alloc_tce_range_nolock: no available blocks of order = %d\n", order );		if ( order < tbl->mlbm.maxLevel )			printk("alloc_tce_range_nolock: trying next bigger size\n" );		else			printk("alloc_tce_range_nolock: maximum size reached...failing\n");	}#endif			// If no block of the requested size was found, try the next	// size bigger.  If one of those is found, return the second	// half of the block to freespace and keep the first half	if ( ( tcenum == -1 ) && ( order < tbl->mlbm.maxLevel  ) ) {		tcenum = alloc_tce_range_nolock( tbl, order+1 );		if ( tcenum != -1 ) {			free_tce_range_nolock( tbl, tcenum+(1<<order), order );		}	}		// Return the index of the first tce in the block	// (or -1 if we failed)	return tcenum;	}static void	free_tce_range( struct TceTable *tbl, long tcenum, unsigned order ){	unsigned long flags;	// Lock the tce allocation bitmap	spin_lock_irqsave( &(tbl->lock), flags );	// Do the actual work	free_tce_range_nolock( tbl, tcenum, order );		// Unlock the tce allocation bitmap	spin_unlock_irqrestore( &(tbl->lock), flags );}static void	free_tce_range_nolock( struct TceTable *tbl, long tcenum, unsigned order ){	unsigned long block;	unsigned byte, bit, mask, b;	unsigned char  * map, * bytep;		if ( order > tbl->mlbm.maxLevel ) {		printk("free_tce_range: order too large, order = %d\n", order );		return;	}		block = tcenum >> order;	if ( tcenum != (block << order ) ) {		printk("free_tce_range: tcenum %lx is not on appropriate boundary for order %x\n", tcenum, order );		return;	}	if ( block >= tbl->mlbm.level[order].numBits ) {		printk("free_tce_range: tcenum %lx is outside the range of this map (order %x, numBits %lx\n", tcenum, order, tbl->mlbm.level[order].numBits );		return;	}#ifdef DEBUG_TCE		if ( test_tce_range( tbl, tcenum, order ) ) {		printk("free_tce_range: freeing range not completely allocated.\n");		printk("free_tce_range:   TceTable %p, tcenum %lx, order %x\n", tbl, tcenum, order );	}#endif	map = tbl->mlbm.level[order].map;	byte  = block / 8;	bit   = block % 8;	mask  = 0x80 >> bit;	bytep = map + byte;#ifdef DEBUG_TCE	printk("free_tce_range_nolock: freeing block %ld (byte=%d, bit=%d) of order %d\n",block, byte, bit, order);	if ( *bytep & mask )		printk("free_tce_range: already free: TceTable %p, tcenum %lx, order %x\n", tbl, tcenum, order );#endif		*bytep |= mask;	// If there is a higher level in the bit map than this we may be	// able to buddy up this block with its partner.	//   If this is the highest level we can't buddy up	//   If this level has an odd number of bits and	//      we are freeing the last block we can't buddy up	// don't buddy up if it's in the first 1/4 of the bits	  if ( ( order < tbl->mlbm.maxLevel ) &&	       ( ( 0 == ( tbl->mlbm.level[order].numBits & 1 ) ) ||		 ( block < tbl->mlbm.level[order].numBits-1 ) )  &&	       ( block > (tbl->mlbm.level[order].numBits/4) ) ) {			// See if we can buddy up the block we just freed		bit  &= 6;		// get to the first of the buddy bits		mask  = 0xc0 >> bit;	// build two bit mask		b     = *bytep & mask;	// Get the two bits		if ( 0 == (b ^ mask) ) { // If both bits are on			// both of the buddy blocks are free we can combine them			*bytep ^= mask;	// turn off the two bits			block = ( byte * 8 ) + bit; // block of first of buddies			tcenum = block << order;			// free the buddied block#ifdef DEBUG_TCE			printk("free_tce_range: buddying up block %ld and block %ld\n", block, block+1);#endif						free_tce_range_nolock( tbl, tcenum, order+1 ); 		}		}}static long	test_tce_range( struct TceTable *tbl, long tcenum, unsigned order ){	unsigned long block;	unsigned byte, bit, mask, b;	long	retval, retLeft, retRight;	unsigned char  * map;		map = tbl->mlbm.level[order].map;	block = tcenum >> order;	byte = block / 8;		// Byte within bitmap	bit  = block % 8;		// Bit within byte	mask = 0x80 >> bit;			b    = (*(map+byte) & mask );	// 0 if block is allocated, else free	if ( b ) 		retval = 1;		// 1 == block is free	else		retval = 0;		// 0 == block is allocated	// Test bits at all levels below this to ensure that all agree	if (order) {		retLeft  = test_tce_range( tbl, tcenum, order-1 );		retRight = test_tce_range( tbl, tcenum+(1<<(order-1)), order-1 );		if ( retLeft || retRight ) {			retval = 2;				}	}	// Test bits at all levels above this to ensure that all agree		return retval;}static dma_addr_t get_tces( struct TceTable *tbl, unsigned order, void *page, unsigned numPages, int tceType, int direction ){	long tcenum;	unsigned long uaddr;	unsigned i;	dma_addr_t retTce = NO_TCE;	uaddr = (unsigned long)page & PAGE_MASK;		// Allocate a range of tces	tcenum = alloc_tce_range( tbl, order );	if ( tcenum != -1 ) {		// We got the tces we wanted		tcenum += tbl->startOffset;	// Offset into real TCE table		retTce = tcenum << PAGE_SHIFT;	// Set the return dma address		// Setup a tce for each page		for (i=0; i<numPages; ++i) {			build_tce( tbl, tcenum, uaddr, tceType, direction );			++tcenum;			uaddr += PAGE_SIZE;		}	}#ifdef DEBUG_TCE	else		printk("alloc_tce_range failed\n");#endif		return retTce; }static void free_tces( struct TceTable *tbl, dma_addr_t dma_addr, unsigned order, unsigned numPages ){	long tcenum, freeTce, maxTcenum;	unsigned i;	union Tce tce;	maxTcenum = (tbl->size * (PAGE_SIZE / sizeof(union Tce))) - 1;		tcenum = dma_addr >> PAGE_SHIFT;	tcenum -= tbl->startOffset;	if ( tcenum > maxTcenum ) {		printk("free_tces: tcenum > maxTcenum, tcenum = %ld, maxTcenum = %ld\n",				tcenum, maxTcenum );		printk("free_tces:    TCE Table at %16lx\n", (unsigned long)tbl );		printk("free_tces:      bus#     %lu\n", (unsigned long)tbl->busNumber );		printk("free_tces:      size     %lu\n", (unsigned long)tbl->size );		printk("free_tces:      startOff %lu\n", (unsigned long)tbl->startOffset );		printk("free_tces:      index    %lu\n", (unsigned long)tbl->index );		return;	}		freeTce = tcenum;	for (i=0; i<numPages; ++i) {		tce.wholeTce = 0;		HvCallXm_setTce( (u64)tbl->index, (u64)tcenum, tce.wholeTce );		++tcenum;	}	free_tce_range( tbl, freeTce, order );}void __init create_virtual_bus_tce_table(void){	struct TceTable * t;	struct HvTceTableManagerCB virtBusTceTableParms;	u64 absParmsPtr;	virtBusTceTableParms.busNumber = 255;	/* Bus 255 is the virtual bus */	virtBusTceTableParms.virtualBusFlag = 0xff; /* Ask for virtual bus */		absParmsPtr = virt_to_absolute( (u32)&virtBusTceTableParms );	HvCallXm_getTceTableParms( absParmsPtr );		t = build_tce_table( &virtBusTceTableParms, &virtBusTceTable );	if ( t ) {		tceTables[255] = t;		printk("Virtual Bus TCE table built successfully.\n");		printk("  TCE table size = %ld entries\n", 				(unsigned long)t->size*(PAGE_SIZE/sizeof(union Tce)) );		printk("  TCE table token = %d\n",				(unsigned)t->index );		printk("  TCE table start entry = 0x%lx\n",				(unsigned long)t->startOffset );	}	else 		printk("Virtual Bus TCE table failed.\n");}void __init create_pci_bus_tce_table( unsigned busNumber ){	struct TceTable * t;

⌨️ 快捷键说明

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