1/* 2 * This file is subject to the terms and conditions of the GNU General Public 3 * License. See the file "COPYING" in the main directory of this archive 4 * for more details. 5 * 6 * Copyright (C) 2001-2006 Silicon Graphics, Inc. All rights reserved. 7 */ 8 9#include <linux/types.h> 10#include <asm/sn/sn_sal.h> 11#include <asm/sn/pcibr_provider.h> 12#include <asm/sn/pcibus_provider_defs.h> 13#include <asm/sn/pcidev.h> 14 15int pcibr_invalidate_ate; /* by default don't invalidate ATE on free */ 16 17/* 18 * mark_ate: Mark the ate as either free or inuse. 19 */ 20static void mark_ate(struct ate_resource *ate_resource, int start, int number, 21 u64 value) 22{ 23 u64 *ate = ate_resource->ate; 24 int index; 25 int length = 0; 26 27 for (index = start; length < number; index++, length++) 28 ate[index] = value; 29} 30 31/* 32 * find_free_ate: Find the first free ate index starting from the given 33 * index for the desired consecutive count. 34 */ 35static int find_free_ate(struct ate_resource *ate_resource, int start, 36 int count) 37{ 38 u64 *ate = ate_resource->ate; 39 int index; 40 int start_free; 41 42 for (index = start; index < ate_resource->num_ate;) { 43 if (!ate[index]) { 44 int i; 45 int free; 46 free = 0; 47 start_free = index; /* Found start free ate */ 48 for (i = start_free; i < ate_resource->num_ate; i++) { 49 if (!ate[i]) { /* This is free */ 50 if (++free == count) 51 return start_free; 52 } else { 53 index = i + 1; 54 break; 55 } 56 } 57 if (i >= ate_resource->num_ate) 58 return -1; 59 } else 60 index++; /* Try next ate */ 61 } 62 63 return -1; 64} 65 66/* 67 * free_ate_resource: Free the requested number of ATEs. 68 */ 69static inline void free_ate_resource(struct ate_resource *ate_resource, 70 int start) 71{ 72 mark_ate(ate_resource, start, ate_resource->ate[start], 0); 73 if ((ate_resource->lowest_free_index > start) || 74 (ate_resource->lowest_free_index < 0)) 75 ate_resource->lowest_free_index = start; 76} 77 78/* 79 * alloc_ate_resource: Allocate the requested number of ATEs. 80 */ 81static inline int alloc_ate_resource(struct ate_resource *ate_resource, 82 int ate_needed) 83{ 84 int start_index; 85 86 /* 87 * Check for ate exhaustion. 88 */ 89 if (ate_resource->lowest_free_index < 0) 90 return -1; 91 92 /* 93 * Find the required number of free consecutive ates. 94 */ 95 start_index = 96 find_free_ate(ate_resource, ate_resource->lowest_free_index, 97 ate_needed); 98 if (start_index >= 0) 99 mark_ate(ate_resource, start_index, ate_needed, ate_needed); 100 101 ate_resource->lowest_free_index = 102 find_free_ate(ate_resource, ate_resource->lowest_free_index, 1); 103 104 return start_index; 105} 106 107/* 108 * Allocate "count" contiguous Bridge Address Translation Entries 109 * on the specified bridge to be used for PCI to XTALK mappings. 110 * Indices in rm map range from 1..num_entries. Indices returned 111 * to caller range from 0..num_entries-1. 112 * 113 * Return the start index on success, -1 on failure. 114 */ 115int pcibr_ate_alloc(struct pcibus_info *pcibus_info, int count) 116{ 117 int status; 118 unsigned long flags; 119 120 spin_lock_irqsave(&pcibus_info->pbi_lock, flags); 121 status = alloc_ate_resource(&pcibus_info->pbi_int_ate_resource, count); 122 spin_unlock_irqrestore(&pcibus_info->pbi_lock, flags); 123 124 return status; 125} 126 127/* 128 * Setup an Address Translation Entry as specified. Use either the Bridge 129 * internal maps or the external map RAM, as appropriate. 130 */ 131static inline u64 __iomem *pcibr_ate_addr(struct pcibus_info *pcibus_info, 132 int ate_index) 133{ 134 if (ate_index < pcibus_info->pbi_int_ate_size) { 135 return pcireg_int_ate_addr(pcibus_info, ate_index); 136 } 137 panic("pcibr_ate_addr: invalid ate_index 0x%x", ate_index); 138} 139 140/* 141 * Update the ate. 142 */ 143void inline 144ate_write(struct pcibus_info *pcibus_info, int ate_index, int count, 145 volatile u64 ate) 146{ 147 while (count-- > 0) { 148 if (ate_index < pcibus_info->pbi_int_ate_size) { 149 pcireg_int_ate_set(pcibus_info, ate_index, ate); 150 } else { 151 panic("ate_write: invalid ate_index 0x%x", ate_index); 152 } 153 ate_index++; 154 ate += IOPGSIZE; 155 } 156 157 pcireg_tflush_get(pcibus_info); /* wait until Bridge PIO complete */ 158} 159 160void pcibr_ate_free(struct pcibus_info *pcibus_info, int index) 161{ 162 163 volatile u64 ate; 164 int count; 165 unsigned long flags; 166 167 if (pcibr_invalidate_ate) { 168 /* For debugging purposes, clear the valid bit in the ATE */ 169 ate = *pcibr_ate_addr(pcibus_info, index); 170 count = pcibus_info->pbi_int_ate_resource.ate[index]; 171 ate_write(pcibus_info, index, count, (ate & ~PCI32_ATE_V)); 172 } 173 174 spin_lock_irqsave(&pcibus_info->pbi_lock, flags); 175 free_ate_resource(&pcibus_info->pbi_int_ate_resource, index); 176 spin_unlock_irqrestore(&pcibus_info->pbi_lock, flags); 177} 178