]>
Commit | Line | Data |
---|---|---|
af3ea169 | 1 | /* |
2 | * Copyright (C) 2017 Hisilicon Limited, All Rights Reserved. | |
3 | * Author: Zhichang Yuan <yuanzhichang@hisilicon.com> | |
4 | * | |
5 | * This program is free software; you can redistribute it and/or modify | |
6 | * it under the terms of the GNU General Public License version 2 as | |
7 | * published by the Free Software Foundation. | |
8 | * | |
9 | * This program is distributed in the hope that it will be useful, | |
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 | * GNU General Public License for more details. | |
13 | * | |
14 | * You should have received a copy of the GNU General Public License | |
15 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
16 | */ | |
17 | ||
18 | #include <linux/of.h> | |
19 | #include <linux/io.h> | |
20 | #include <linux/mm.h> | |
21 | #include <linux/rculist.h> | |
22 | #include <linux/sizes.h> | |
23 | #include <linux/slab.h> | |
24 | ||
25 | /* A list of all the IO hosts registered. ONLY THE HOST nodes. */ | |
26 | static LIST_HEAD(io_range_list); | |
27 | static DEFINE_MUTEX(io_range_mutex); | |
28 | ||
29 | /* | |
30 | * allocate a free range for this registration. | |
31 | * | |
32 | * @new_range: point to the node awaiting this registration. | |
33 | * part of the fields are as input parameters. This node | |
34 | * is allocated and initialized by caller; | |
35 | * @prev: points to the last node before the return; | |
36 | * | |
37 | * return 0 for success, other are fail. | |
38 | */ | |
39 | static int libio_alloc_range(struct libio_range *new_range, | |
40 | struct list_head **prev) | |
41 | { | |
42 | struct libio_range *entry; | |
43 | unsigned long align = 1; | |
44 | unsigned long tmp_start; | |
45 | unsigned long idle_start, idle_end; | |
46 | ||
47 | if (new_range->flags & IO_CPU_MMIO) | |
48 | align = PAGE_SIZE; | |
49 | idle_start = 0; | |
50 | *prev = &io_range_list; | |
51 | list_for_each_entry_rcu(entry, &io_range_list, list) { | |
52 | if (idle_start > entry->io_start) { | |
53 | WARN(1, "skip an invalid io range during traversal!\n"); | |
54 | goto nextentry; | |
55 | } | |
56 | /* set the end edge. */ | |
57 | if (idle_start == entry->io_start) { | |
58 | struct libio_range *next; | |
59 | ||
60 | idle_start = entry->io_start + entry->size; | |
61 | next = list_next_or_null_rcu(&io_range_list, | |
62 | &entry->list, struct libio_range, list); | |
63 | if (next) { | |
64 | entry = next; | |
65 | } else { | |
66 | *prev = &entry->list; | |
67 | break; | |
68 | } | |
69 | } | |
70 | idle_end = entry->io_start - 1; | |
71 | ||
72 | /* contiguous range... */ | |
73 | if (idle_start > idle_end) | |
74 | goto nextentry; | |
75 | ||
76 | tmp_start = idle_start; | |
77 | idle_start = ALIGN(idle_start, align); | |
78 | if (idle_start >= tmp_start && | |
79 | idle_start + new_range->size <= idle_end) { | |
80 | new_range->io_start = idle_start; | |
81 | *prev = &entry->list; | |
82 | return 0; | |
83 | } | |
84 | ||
85 | nextentry: | |
86 | idle_start = entry->io_start + entry->size; | |
87 | *prev = &entry->list; | |
88 | } | |
89 | /* check the last free gap... */ | |
90 | idle_end = IO_SPACE_LIMIT; | |
91 | ||
92 | tmp_start = idle_start; | |
93 | idle_start = ALIGN(idle_start, align); | |
94 | if (idle_start >= tmp_start && | |
95 | idle_start + new_range->size <= idle_end) { | |
96 | new_range->io_start = idle_start; | |
97 | return 0; | |
98 | } | |
99 | ||
100 | return -EBUSY; | |
101 | } | |
102 | ||
103 | /* | |
104 | * traverse the io_range_list to find the registered node whose device node | |
105 | * and/or physical IO address match to. | |
106 | */ | |
107 | struct libio_range *find_io_range_from_fwnode(struct fwnode_handle *fwnode) | |
108 | { | |
109 | struct libio_range *range; | |
110 | ||
111 | list_for_each_entry_rcu(range, &io_range_list, list) { | |
112 | if (range->node == fwnode) | |
113 | return range; | |
114 | } | |
115 | return NULL; | |
116 | } | |
117 | ||
118 | /* | |
119 | * Search a io_range registered which match the fwnode and addr. | |
120 | * | |
121 | * @fwnode: the host fwnode which must be valid; | |
122 | * @start: the start hardware address of this search; | |
123 | * @end: the end hardware address of this search. can be equal to @start; | |
124 | * | |
125 | * return NULL when there is no matched node; IS_ERR() means ERROR; | |
126 | * valid virtual address represent a matched node was found. | |
127 | */ | |
128 | static struct libio_range * | |
129 | libio_find_range_byaddr(struct fwnode_handle *fwnode, | |
130 | resource_size_t start, resource_size_t end) | |
131 | { | |
132 | struct libio_range *entry; | |
133 | ||
134 | list_for_each_entry_rcu(entry, &io_range_list, list) { | |
135 | if (entry->node != fwnode) | |
136 | continue; | |
137 | /* without any overlap with current range */ | |
138 | if (start >= entry->hw_start + entry->size || | |
139 | end < entry->hw_start) | |
140 | continue; | |
141 | /* overlap is not supported now. */ | |
142 | if (start < entry->hw_start || | |
143 | end >= entry->hw_start + entry->size) | |
144 | return ERR_PTR(-EBUSY); | |
145 | /* had been registered. */ | |
146 | return entry; | |
147 | } | |
148 | ||
149 | return NULL; | |
150 | } | |
151 | ||
152 | /* | |
153 | * register a io range node in the io range list. | |
154 | * | |
155 | * @newrange: pointer to the io range to be registered. | |
156 | * | |
157 | * return 'newrange' when success, ERR_VALUE() is for failures. | |
158 | * specially, return a valid pointer which is not equal to 'newrange' when | |
159 | * the io range had been registered before. | |
160 | */ | |
161 | struct libio_range *register_libio_range(struct libio_range *newrange) | |
162 | { | |
163 | int err; | |
164 | struct libio_range *range; | |
165 | struct list_head *prev; | |
166 | ||
167 | if (!newrange || !newrange->node || !newrange->size) | |
168 | return ERR_PTR(-EINVAL); | |
169 | ||
170 | mutex_lock(&io_range_mutex); | |
171 | range = libio_find_range_byaddr(newrange->node, newrange->hw_start, | |
172 | newrange->hw_start + newrange->size - 1); | |
173 | if (range) { | |
174 | if (!IS_ERR(range)) | |
175 | pr_info("the request IO range had been registered!\n"); | |
176 | else | |
177 | pr_err("registering IO[%pa - sz%pa) got failed!\n", | |
178 | &newrange->hw_start, &newrange->size); | |
179 | return range; | |
180 | } | |
181 | ||
182 | err = libio_alloc_range(newrange, &prev); | |
183 | if (!err) | |
184 | /* the bus IO range list is ordered by pio. */ | |
185 | list_add_rcu(&newrange->list, prev); | |
186 | else | |
187 | pr_err("can't find free %pa logical IO range!\n", | |
188 | &newrange->size); | |
189 | ||
190 | mutex_unlock(&io_range_mutex); | |
191 | return err ? ERR_PTR(err) : newrange; | |
192 | } | |
193 | ||
194 | /* | |
195 | * Translate the input logical pio to the corresponding hardware address. | |
196 | * The input pio should be unique in the whole logical PIO space. | |
197 | */ | |
198 | resource_size_t libio_to_hwaddr(unsigned long pio) | |
199 | { | |
200 | struct libio_range *range; | |
201 | ||
202 | list_for_each_entry_rcu(range, &io_range_list, list) { | |
203 | if (pio < range->io_start) | |
204 | break; | |
205 | ||
206 | if (pio < range->io_start + range->size) | |
207 | return pio - range->io_start + range->hw_start; | |
208 | } | |
209 | ||
210 | return -1; | |
211 | } | |
212 | ||
213 | /* | |
214 | * This function is generic for translating a hardware address to logical PIO. | |
215 | * @hw_addr: the hardware address of host, can be CPU address or host-local | |
216 | * address; | |
217 | */ | |
218 | unsigned long | |
219 | libio_translate_hwaddr(struct fwnode_handle *fwnode, resource_size_t addr) | |
220 | { | |
221 | struct libio_range *range; | |
222 | ||
223 | range = libio_find_range_byaddr(fwnode, addr, addr); | |
224 | if (!range) | |
225 | return -1; | |
226 | ||
227 | return addr - range->hw_start + range->io_start; | |
228 | } | |
229 | ||
230 | unsigned long | |
231 | libio_translate_cpuaddr(resource_size_t addr) | |
232 | { | |
233 | struct libio_range *range; | |
234 | ||
235 | list_for_each_entry_rcu(range, &io_range_list, list) { | |
236 | if (!(range->flags & IO_CPU_MMIO)) | |
237 | continue; | |
238 | if (addr >= range->hw_start && | |
239 | addr < range->hw_start + range->size) | |
240 | return addr - range->hw_start + range->io_start; | |
241 | } | |
242 | return -1; | |
243 | } | |
244 | ||
245 | #ifdef PCI_IOBASE | |
246 | static struct libio_range *find_io_range(unsigned long pio) | |
247 | { | |
248 | struct libio_range *range; | |
249 | ||
250 | list_for_each_entry_rcu(range, &io_range_list, list) { | |
251 | if (range->io_start > pio) | |
252 | return NULL; | |
253 | if (pio < range->io_start + range->size) | |
254 | return range; | |
255 | } | |
256 | return NULL; | |
257 | } | |
258 | ||
259 | #define BUILD_IO(bw, type) \ | |
260 | type libio_in##bw(unsigned long addr) \ | |
261 | { \ | |
262 | struct libio_range *entry = find_io_range(addr); \ | |
263 | \ | |
264 | if (entry && entry->ops) \ | |
265 | return entry->ops->pfin(entry->devpara, \ | |
266 | addr, sizeof(type)); \ | |
267 | return read##bw(PCI_IOBASE + addr); \ | |
268 | } \ | |
269 | \ | |
270 | void libio_out##bw(type value, unsigned long addr) \ | |
271 | { \ | |
272 | struct libio_range *entry = find_io_range(addr); \ | |
273 | \ | |
274 | if (entry && entry->ops) \ | |
275 | entry->ops->pfout(entry->devpara, \ | |
276 | addr, value, sizeof(type)); \ | |
277 | else \ | |
278 | write##bw(value, PCI_IOBASE + addr); \ | |
279 | } \ | |
280 | \ | |
281 | void libio_ins##bw(unsigned long addr, void *buffer, unsigned int count)\ | |
282 | { \ | |
283 | struct libio_range *entry = find_io_range(addr); \ | |
284 | \ | |
285 | if (entry && entry->ops) \ | |
286 | entry->ops->pfins(entry->devpara, \ | |
287 | addr, buffer, sizeof(type), count); \ | |
288 | else \ | |
289 | reads##bw(PCI_IOBASE + addr, buffer, count); \ | |
290 | } \ | |
291 | \ | |
292 | void libio_outs##bw(unsigned long addr, const void *buffer, \ | |
293 | unsigned int count) \ | |
294 | { \ | |
295 | struct libio_range *entry = find_io_range(addr); \ | |
296 | \ | |
297 | if (entry && entry->ops) \ | |
298 | entry->ops->pfouts(entry->devpara, \ | |
299 | addr, buffer, sizeof(type), count); \ | |
300 | else \ | |
301 | writes##bw(PCI_IOBASE + addr, buffer, count); \ | |
302 | } | |
303 | ||
304 | BUILD_IO(b, u8) | |
305 | ||
306 | EXPORT_SYMBOL(libio_inb); | |
307 | EXPORT_SYMBOL(libio_outb); | |
308 | EXPORT_SYMBOL(libio_insb); | |
309 | EXPORT_SYMBOL(libio_outsb); | |
310 | ||
311 | BUILD_IO(w, u16) | |
312 | ||
313 | EXPORT_SYMBOL(libio_inw); | |
314 | EXPORT_SYMBOL(libio_outw); | |
315 | EXPORT_SYMBOL(libio_insw); | |
316 | EXPORT_SYMBOL(libio_outsw); | |
317 | ||
318 | BUILD_IO(l, u32) | |
319 | ||
320 | EXPORT_SYMBOL(libio_inl); | |
321 | EXPORT_SYMBOL(libio_outl); | |
322 | EXPORT_SYMBOL(libio_insl); | |
323 | EXPORT_SYMBOL(libio_outsl); | |
324 | #endif /* PCI_IOBASE */ |