[IA64] SN: Add support for CPU disable
[linux-2.6] / arch / ia64 / sn / pci / pcibr / pcibr_ate.c
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
15 int 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  */
20 static 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  */
35 static 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                 } else
58                         index++;        /* Try next ate */
59         }
60
61         return -1;
62 }
63
64 /*
65  * free_ate_resource:  Free the requested number of ATEs.
66  */
67 static inline void free_ate_resource(struct ate_resource *ate_resource,
68                                      int start)
69 {
70         mark_ate(ate_resource, start, ate_resource->ate[start], 0);
71         if ((ate_resource->lowest_free_index > start) ||
72             (ate_resource->lowest_free_index < 0))
73                 ate_resource->lowest_free_index = start;
74 }
75
76 /*
77  * alloc_ate_resource:  Allocate the requested number of ATEs.
78  */
79 static inline int alloc_ate_resource(struct ate_resource *ate_resource,
80                                      int ate_needed)
81 {
82         int start_index;
83
84         /*
85          * Check for ate exhaustion.
86          */
87         if (ate_resource->lowest_free_index < 0)
88                 return -1;
89
90         /*
91          * Find the required number of free consecutive ates.
92          */
93         start_index =
94             find_free_ate(ate_resource, ate_resource->lowest_free_index,
95                           ate_needed);
96         if (start_index >= 0)
97                 mark_ate(ate_resource, start_index, ate_needed, ate_needed);
98
99         ate_resource->lowest_free_index =
100             find_free_ate(ate_resource, ate_resource->lowest_free_index, 1);
101
102         return start_index;
103 }
104
105 /*
106  * Allocate "count" contiguous Bridge Address Translation Entries
107  * on the specified bridge to be used for PCI to XTALK mappings.
108  * Indices in rm map range from 1..num_entries.  Indices returned
109  * to caller range from 0..num_entries-1.
110  *
111  * Return the start index on success, -1 on failure.
112  */
113 int pcibr_ate_alloc(struct pcibus_info *pcibus_info, int count)
114 {
115         int status;
116         unsigned long flags;
117
118         spin_lock_irqsave(&pcibus_info->pbi_lock, flags);
119         status = alloc_ate_resource(&pcibus_info->pbi_int_ate_resource, count);
120         spin_unlock_irqrestore(&pcibus_info->pbi_lock, flags);
121
122         return status;
123 }
124
125 /*
126  * Setup an Address Translation Entry as specified.  Use either the Bridge
127  * internal maps or the external map RAM, as appropriate.
128  */
129 static inline u64 __iomem *pcibr_ate_addr(struct pcibus_info *pcibus_info,
130                                        int ate_index)
131 {
132         if (ate_index < pcibus_info->pbi_int_ate_size) {
133                 return pcireg_int_ate_addr(pcibus_info, ate_index);
134         }
135         panic("pcibr_ate_addr: invalid ate_index 0x%x", ate_index);
136 }
137
138 /*
139  * Update the ate.
140  */
141 void inline
142 ate_write(struct pcibus_info *pcibus_info, int ate_index, int count,
143           volatile u64 ate)
144 {
145         while (count-- > 0) {
146                 if (ate_index < pcibus_info->pbi_int_ate_size) {
147                         pcireg_int_ate_set(pcibus_info, ate_index, ate);
148                 } else {
149                         panic("ate_write: invalid ate_index 0x%x", ate_index);
150                 }
151                 ate_index++;
152                 ate += IOPGSIZE;
153         }
154
155         pcireg_tflush_get(pcibus_info); /* wait until Bridge PIO complete */
156 }
157
158 void pcibr_ate_free(struct pcibus_info *pcibus_info, int index)
159 {
160
161         volatile u64 ate;
162         int count;
163         unsigned long flags;
164
165         if (pcibr_invalidate_ate) {
166                 /* For debugging purposes, clear the valid bit in the ATE */
167                 ate = *pcibr_ate_addr(pcibus_info, index);
168                 count = pcibus_info->pbi_int_ate_resource.ate[index];
169                 ate_write(pcibus_info, index, count, (ate & ~PCI32_ATE_V));
170         }
171
172         spin_lock_irqsave(&pcibus_info->pbi_lock, flags);
173         free_ate_resource(&pcibus_info->pbi_int_ate_resource, index);
174         spin_unlock_irqrestore(&pcibus_info->pbi_lock, flags);
175 }