178 lines
		
	
	
		
			4.5 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			178 lines
		
	
	
		
			4.5 KiB
		
	
	
	
		
			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-2006 Silicon Graphics, Inc. All rights reserved.
 | 
						|
 */
 | 
						|
 | 
						|
#include <linux/types.h>
 | 
						|
#include <asm/sn/sn_sal.h>
 | 
						|
#include <asm/sn/pcibr_provider.h>
 | 
						|
#include <asm/sn/pcibus_provider_defs.h>
 | 
						|
#include <asm/sn/pcidev.h>
 | 
						|
 | 
						|
int pcibr_invalidate_ate;	/* by default don't invalidate ATE on free */
 | 
						|
 | 
						|
/*
 | 
						|
 * mark_ate: Mark the ate as either free or inuse.
 | 
						|
 */
 | 
						|
static void mark_ate(struct ate_resource *ate_resource, int start, int number,
 | 
						|
		     u64 value)
 | 
						|
{
 | 
						|
	u64 *ate = ate_resource->ate;
 | 
						|
	int index;
 | 
						|
	int length = 0;
 | 
						|
 | 
						|
	for (index = start; length < number; index++, length++)
 | 
						|
		ate[index] = value;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * find_free_ate:  Find the first free ate index starting from the given
 | 
						|
 *		   index for the desired consecutive count.
 | 
						|
 */
 | 
						|
static int find_free_ate(struct ate_resource *ate_resource, int start,
 | 
						|
			 int count)
 | 
						|
{
 | 
						|
	u64 *ate = ate_resource->ate;
 | 
						|
	int index;
 | 
						|
	int start_free;
 | 
						|
 | 
						|
	for (index = start; index < ate_resource->num_ate;) {
 | 
						|
		if (!ate[index]) {
 | 
						|
			int i;
 | 
						|
			int free;
 | 
						|
			free = 0;
 | 
						|
			start_free = index;	/* Found start free ate */
 | 
						|
			for (i = start_free; i < ate_resource->num_ate; i++) {
 | 
						|
				if (!ate[i]) {	/* This is free */
 | 
						|
					if (++free == count)
 | 
						|
						return start_free;
 | 
						|
				} else {
 | 
						|
					index = i + 1;
 | 
						|
					break;
 | 
						|
				}
 | 
						|
			}
 | 
						|
			if (i >= ate_resource->num_ate)
 | 
						|
				return -1;
 | 
						|
		} else
 | 
						|
			index++;	/* Try next ate */
 | 
						|
	}
 | 
						|
 | 
						|
	return -1;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * free_ate_resource:  Free the requested number of ATEs.
 | 
						|
 */
 | 
						|
static inline void free_ate_resource(struct ate_resource *ate_resource,
 | 
						|
				     int start)
 | 
						|
{
 | 
						|
	mark_ate(ate_resource, start, ate_resource->ate[start], 0);
 | 
						|
	if ((ate_resource->lowest_free_index > start) ||
 | 
						|
	    (ate_resource->lowest_free_index < 0))
 | 
						|
		ate_resource->lowest_free_index = start;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * alloc_ate_resource:  Allocate the requested number of ATEs.
 | 
						|
 */
 | 
						|
static inline int alloc_ate_resource(struct ate_resource *ate_resource,
 | 
						|
				     int ate_needed)
 | 
						|
{
 | 
						|
	int start_index;
 | 
						|
 | 
						|
	/*
 | 
						|
	 * Check for ate exhaustion.
 | 
						|
	 */
 | 
						|
	if (ate_resource->lowest_free_index < 0)
 | 
						|
		return -1;
 | 
						|
 | 
						|
	/*
 | 
						|
	 * Find the required number of free consecutive ates.
 | 
						|
	 */
 | 
						|
	start_index =
 | 
						|
	    find_free_ate(ate_resource, ate_resource->lowest_free_index,
 | 
						|
			  ate_needed);
 | 
						|
	if (start_index >= 0)
 | 
						|
		mark_ate(ate_resource, start_index, ate_needed, ate_needed);
 | 
						|
 | 
						|
	ate_resource->lowest_free_index =
 | 
						|
	    find_free_ate(ate_resource, ate_resource->lowest_free_index, 1);
 | 
						|
 | 
						|
	return start_index;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Allocate "count" contiguous Bridge Address Translation Entries
 | 
						|
 * on the specified bridge to be used for PCI to XTALK mappings.
 | 
						|
 * Indices in rm map range from 1..num_entries.  Indices returned
 | 
						|
 * to caller range from 0..num_entries-1.
 | 
						|
 *
 | 
						|
 * Return the start index on success, -1 on failure.
 | 
						|
 */
 | 
						|
int pcibr_ate_alloc(struct pcibus_info *pcibus_info, int count)
 | 
						|
{
 | 
						|
	int status;
 | 
						|
	unsigned long flags;
 | 
						|
 | 
						|
	spin_lock_irqsave(&pcibus_info->pbi_lock, flags);
 | 
						|
	status = alloc_ate_resource(&pcibus_info->pbi_int_ate_resource, count);
 | 
						|
	spin_unlock_irqrestore(&pcibus_info->pbi_lock, flags);
 | 
						|
 | 
						|
	return status;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Setup an Address Translation Entry as specified.  Use either the Bridge
 | 
						|
 * internal maps or the external map RAM, as appropriate.
 | 
						|
 */
 | 
						|
static inline u64 __iomem *pcibr_ate_addr(struct pcibus_info *pcibus_info,
 | 
						|
				       int ate_index)
 | 
						|
{
 | 
						|
	if (ate_index < pcibus_info->pbi_int_ate_size) {
 | 
						|
		return pcireg_int_ate_addr(pcibus_info, ate_index);
 | 
						|
	}
 | 
						|
	panic("pcibr_ate_addr: invalid ate_index 0x%x", ate_index);
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Update the ate.
 | 
						|
 */
 | 
						|
inline void
 | 
						|
ate_write(struct pcibus_info *pcibus_info, int ate_index, int count,
 | 
						|
	  volatile u64 ate)
 | 
						|
{
 | 
						|
	while (count-- > 0) {
 | 
						|
		if (ate_index < pcibus_info->pbi_int_ate_size) {
 | 
						|
			pcireg_int_ate_set(pcibus_info, ate_index, ate);
 | 
						|
		} else {
 | 
						|
			panic("ate_write: invalid ate_index 0x%x", ate_index);
 | 
						|
		}
 | 
						|
		ate_index++;
 | 
						|
		ate += IOPGSIZE;
 | 
						|
	}
 | 
						|
 | 
						|
	pcireg_tflush_get(pcibus_info);	/* wait until Bridge PIO complete */
 | 
						|
}
 | 
						|
 | 
						|
void pcibr_ate_free(struct pcibus_info *pcibus_info, int index)
 | 
						|
{
 | 
						|
 | 
						|
	volatile u64 ate;
 | 
						|
	int count;
 | 
						|
	unsigned long flags;
 | 
						|
 | 
						|
	if (pcibr_invalidate_ate) {
 | 
						|
		/* For debugging purposes, clear the valid bit in the ATE */
 | 
						|
		ate = *pcibr_ate_addr(pcibus_info, index);
 | 
						|
		count = pcibus_info->pbi_int_ate_resource.ate[index];
 | 
						|
		ate_write(pcibus_info, index, count, (ate & ~PCI32_ATE_V));
 | 
						|
	}
 | 
						|
 | 
						|
	spin_lock_irqsave(&pcibus_info->pbi_lock, flags);
 | 
						|
	free_ate_resource(&pcibus_info->pbi_int_ate_resource, index);
 | 
						|
	spin_unlock_irqrestore(&pcibus_info->pbi_lock, flags);
 | 
						|
}
 |