1 /* $NetBSD: bus_space.c,v 1.14 2023/12/20 06:36:02 thorpej Exp $ */
2 /* NetBSD: bus_machdep.c,v 1.1 2000/01/26 18:48:00 drochner Exp */
3
4 /*-
5 * Copyright (c) 1996, 1997, 1998 The NetBSD Foundation, Inc.
6 * All rights reserved.
7 *
8 * This code is derived from software contributed to The NetBSD Foundation
9 * by Charles M. Hannum and by Jason R. Thorpe of the Numerical Aerospace
10 * Simulation Facility, NASA Ames Research Center.
11 *
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
14 * are met:
15 * 1. Redistributions of source code must retain the above copyright
16 * notice, this list of conditions and the following disclaimer.
17 * 2. Redistributions in binary form must reproduce the above copyright
18 * notice, this list of conditions and the following disclaimer in the
19 * documentation and/or other materials provided with the distribution.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
22 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
23 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
24 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
25 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31 * POSSIBILITY OF SUCH DAMAGE.
32 */
33
34 #include <sys/cdefs.h>
35 __KERNEL_RCSID(0, "$NetBSD: bus_space.c,v 1.14 2023/12/20 06:36:02 thorpej Exp $");
36
37 #include <sys/param.h>
38 #include <sys/systm.h>
39 #include <sys/vmem_impl.h>
40
41 #include <uvm/uvm_extern.h>
42
43 #include <sys/bus.h>
44
45 /*
46 * uintN_t bus_space_read_N(bus_space_tag_t tag,
47 * bus_space_handle_t bsh, bus_size_t offset);
48 *
49 * Read a 1, 2, 4, or 8 byte quantity from bus space
50 * described by tag/handle/offset.
51 */
52
53 #define bus_space_read(BYTES,BITS) \
54 __CONCAT3(uint,BITS,_t) \
55 __CONCAT(bus_space_read_,BYTES)(bus_space_tag_t bst, \
56 bus_space_handle_t bsh, bus_size_t offset) \
57 { \
58 return (*(volatile __CONCAT3(uint,BITS,_t) *) \
59 (bsh + (offset << __CONCAT(bst->bs_stride_,BYTES)))); \
60 }
61
62 bus_space_read(1,8)
63 bus_space_read(2,16)
64 bus_space_read(4,32)
65 bus_space_read(8,64)
66
67 /*
68 * void bus_space_read_multi_N(bus_space_tag_t tag,
69 * bus_space_handle_t bsh, bus_size_t offset,
70 * uintN_t *addr, size_t count);
71 *
72 * Read `count' 1, 2, 4, or 8 byte quantities from bus space
73 * described by tag/handle/offset and copy into buffer provided.
74 */
75
76 #define bus_space_read_multi(BYTES,BITS) \
77 void \
78 __CONCAT(bus_space_read_multi_,BYTES)(bus_space_tag_t bst, \
79 bus_space_handle_t bsh, bus_size_t offset, \
80 __CONCAT3(uint,BITS,_t) *datap, bus_size_t count) \
81 { \
82 volatile __CONCAT3(uint,BITS,_t) *p = \
83 (volatile __CONCAT3(uint,BITS,_t) *) \
84 (bsh + (offset << __CONCAT(bst->bs_stride_,BYTES))); \
85 \
86 for (; count > 0; --count) \
87 *datap++ = *p; \
88 }
89
90 bus_space_read_multi(1,8)
91 bus_space_read_multi(2,16)
92 bus_space_read_multi(4,32)
93 bus_space_read_multi(8,64)
94
95 /*
96 * void bus_space_read_region_N(bus_space_tag_t tag,
97 * bus_space_handle_t bsh, bus_size_t offset,
98 * uintN_t *addr, size_t count);
99 *
100 * Read `count' 1, 2, 4, or 8 byte quantities from bus space
101 * described by tag/handle and starting at `offset' and copy into
102 * buffer provided.
103 */
104
105 #define bus_space_read_region(BYTES,BITS) \
106 void \
107 __CONCAT(bus_space_read_region_,BYTES)(bus_space_tag_t bst, \
108 bus_space_handle_t bsh, bus_size_t offset, \
109 __CONCAT3(uint,BITS,_t) *datap, bus_size_t count) \
110 { \
111 int stride = 1 << __CONCAT(bst->bs_stride_,BYTES); \
112 volatile __CONCAT3(uint,BITS,_t) *p = \
113 (volatile __CONCAT3(uint,BITS,_t) *) \
114 (bsh + (offset << __CONCAT(bst->bs_stride_,BYTES))); \
115 \
116 for (; count > 0; --count) { \
117 *datap++ = *p; \
118 p += stride; \
119 } \
120 }
121
122 bus_space_read_region(1,8)
123 bus_space_read_region(2,16)
124 bus_space_read_region(4,32)
125 bus_space_read_region(8,64)
126
127 /*
128 * void bus_space_write_N(bus_space_tag_t tag,
129 * bus_space_handle_t bsh, bus_size_t offset,
130 * uintN_t value);
131 *
132 * Write the 1, 2, 4, or 8 byte value `value' to bus space
133 * described by tag/handle/offset.
134 */
135
136 #define bus_space_write(BYTES,BITS) \
137 void \
138 __CONCAT(bus_space_write_,BYTES)(bus_space_tag_t bst, \
139 bus_space_handle_t bsh, \
140 bus_size_t offset, __CONCAT3(uint,BITS,_t) data) \
141 { \
142 *(volatile __CONCAT3(uint,BITS,_t) *) \
143 (bsh + (offset << __CONCAT(bst->bs_stride_,BYTES))) = data; \
144 }
145
146 bus_space_write(1,8)
147 bus_space_write(2,16)
148 bus_space_write(4,32)
149 bus_space_write(8,64)
150
151 /*
152 * void bus_space_write_multi_N(bus_space_tag_t tag,
153 * bus_space_handle_t bsh, bus_size_t offset,
154 * const uintN_t *addr, size_t count);
155 *
156 * Write `count' 1, 2, 4, or 8 byte quantities from the buffer
157 * provided to bus space described by tag/handle/offset.
158 */
159
160 #define bus_space_write_multi(BYTES,BITS) \
161 void \
162 __CONCAT(bus_space_write_multi_,BYTES)(bus_space_tag_t bst, \
163 bus_space_handle_t bsh, bus_size_t offset, \
164 const __CONCAT3(uint,BITS,_t) *datap, bus_size_t count) \
165 { \
166 volatile __CONCAT3(uint,BITS,_t) *p = \
167 (volatile __CONCAT3(uint,BITS,_t) *) \
168 (bsh + (offset << __CONCAT(bst->bs_stride_,BYTES))); \
169 \
170 for (; count > 0; --count) \
171 *p = *datap++; \
172 }
173
174 bus_space_write_multi(1,8)
175 bus_space_write_multi(2,16)
176 bus_space_write_multi(4,32)
177 bus_space_write_multi(8,64)
178
179 /*
180 * void bus_space_write_region_N(bus_space_tag_t tag,
181 * bus_space_handle_t bsh, bus_size_t offset,
182 * const uintN_t *addr, size_t count);
183 *
184 * Write `count' 1, 2, 4, or 8 byte quantities from the buffer provided
185 * to bus space described by tag/handle starting at `offset'.
186 */
187
188 #define bus_space_write_region(BYTES,BITS) \
189 void \
190 __CONCAT(bus_space_write_region_,BYTES)(bus_space_tag_t bst, \
191 bus_space_handle_t bsh, bus_size_t offset, \
192 const __CONCAT3(uint,BITS,_t) *datap, bus_size_t count) \
193 { \
194 int stride = 1 << __CONCAT(bst->bs_stride_,BYTES); \
195 volatile __CONCAT3(uint,BITS,_t) *p = \
196 (volatile __CONCAT3(uint,BITS,_t) *) \
197 (bsh + (offset << __CONCAT(bst->bs_stride_,BYTES))); \
198 \
199 for (; count > 0; --count) { \
200 *p = *datap++; \
201 p += stride; \
202 } \
203 }
204
205 bus_space_write_region(1,8)
206 bus_space_write_region(2,16)
207 bus_space_write_region(4,32)
208 bus_space_write_region(8,64)
209
210 /*
211 * void bus_space_set_multi_N(bus_space_tag_t tag,
212 * bus_space_handle_t bsh, bus_size_t offset, uintN_t val,
213 * size_t count);
214 *
215 * Write the 1, 2, 4, or 8 byte value `val' to bus space described
216 * by tag/handle/offset `count' times.
217 */
218
219 #define bus_space_set_multi(BYTES,BITS) \
220 void \
221 __CONCAT(bus_space_set_multi_,BYTES)(bus_space_tag_t bst, \
222 bus_space_handle_t bsh, bus_size_t offset, \
223 const __CONCAT3(uint,BITS,_t) data, bus_size_t count) \
224 { \
225 volatile __CONCAT3(uint,BITS,_t) *p = \
226 (volatile __CONCAT3(uint,BITS,_t) *) \
227 (bsh + (offset << __CONCAT(bst->bs_stride_,BYTES))); \
228 \
229 for (; count > 0; --count) \
230 *p = data; \
231 }
232
233 bus_space_set_multi(1,8)
234 bus_space_set_multi(2,16)
235 bus_space_set_multi(4,32)
236 bus_space_set_multi(8,64)
237
238 /*
239 * void bus_space_set_region_N(bus_space_tag_t tag,
240 * bus_space_handle_t bsh, bus_size_t offset, uintN_t val,
241 * size_t count);
242 *
243 * Write `count' 1, 2, 4, or 8 byte value `val' to bus space described
244 * by tag/handle starting at `offset'.
245 */
246
247 #define bus_space_set_region(BYTES,BITS) \
248 void \
249 __CONCAT(bus_space_set_region_,BYTES)(bus_space_tag_t bst, \
250 bus_space_handle_t bsh, bus_size_t offset, \
251 __CONCAT3(uint,BITS,_t) data, bus_size_t count) \
252 { \
253 int stride = 1 << __CONCAT(bst->bs_stride_,BYTES); \
254 volatile __CONCAT3(uint,BITS,_t) *p = \
255 (volatile __CONCAT3(uint,BITS,_t) *) \
256 (bsh + (offset << __CONCAT(bst->bs_stride_,BYTES))); \
257 \
258 for (; count > 0; --count) { \
259 *p = data; \
260 p += stride; \
261 } \
262 }
263
264 bus_space_set_region(1,8)
265 bus_space_set_region(2,16)
266 bus_space_set_region(4,32)
267 bus_space_set_region(8,64)
268
269 /*
270 * void bus_space_copy_region_N(bus_space_tag_t tag,
271 * bus_space_handle_t bsh1, bus_size_t off1,
272 * bus_space_handle_t bsh2, bus_size_t off2,
273 * size_t count);
274 *
275 * Copy `count' 1, 2, 4, or 8 byte values from bus space starting
276 * at tag/bsh1/off1 to bus space starting at tag/bsh2/off2.
277 */
278
279 #define bus_space_copy_region(BYTES,BITS) \
280 void \
281 __CONCAT(bus_space_copy_region_,BYTES)(bus_space_tag_t bst, \
282 bus_space_handle_t srcbsh, bus_size_t srcoffset, \
283 bus_space_handle_t dstbsh, bus_size_t dstoffset, bus_size_t count) \
284 { \
285 int stride = 1 << __CONCAT(bst->bs_stride_,BYTES); \
286 volatile __CONCAT3(uint,BITS,_t) *srcp = \
287 (volatile __CONCAT3(uint,BITS,_t) *) \
288 (srcbsh + (srcoffset << __CONCAT(bst->bs_stride_,BYTES))); \
289 volatile __CONCAT3(uint,BITS,_t) *dstp = \
290 (volatile __CONCAT3(uint,BITS,_t) *) \
291 (dstbsh + (dstoffset << __CONCAT(bst->bs_stride_,BYTES))); \
292 bus_size_t offset; \
293 \
294 if (srcp >= dstp) { \
295 /* src after dest: copy forward */ \
296 for (offset = 0; count > 0; --count, offset += stride) \
297 dstp[offset] = srcp[offset]; \
298 } else { \
299 /* dest after src: copy backward */ \
300 offset = (count << __CONCAT(bst->bs_stride_,BYTES)) \
301 - stride; \
302 for (; count > 0; --count, offset -= stride) \
303 dstp[offset] = srcp[offset]; \
304 } \
305 }
306
307 bus_space_copy_region(1,8)
308 bus_space_copy_region(2,16)
309 bus_space_copy_region(4,32)
310 bus_space_copy_region(8,64)
311
312 void
arc_bus_space_init(bus_space_tag_t bst,const char * name,paddr_t paddr,vaddr_t vaddr,bus_addr_t start,bus_size_t size)313 arc_bus_space_init(bus_space_tag_t bst, const char *name, paddr_t paddr,
314 vaddr_t vaddr, bus_addr_t start, bus_size_t size)
315 {
316
317 bst->bs_name = name;
318 bst->bs_arena = NULL;
319 bst->bs_start = start;
320 bst->bs_size = size;
321 bst->bs_pbase = paddr;
322 bst->bs_vbase = vaddr;
323 bst->bs_compose_handle = arc_bus_space_compose_handle;
324 bst->bs_dispose_handle = arc_bus_space_dispose_handle;
325 bst->bs_paddr = arc_bus_space_paddr;
326 bst->bs_map = arc_bus_space_map;
327 bst->bs_unmap = arc_bus_space_unmap;
328 bst->bs_subregion = arc_bus_space_subregion;
329 bst->bs_mmap = arc_bus_space_mmap;
330 bst->bs_alloc = arc_bus_space_alloc;
331 bst->bs_free = arc_bus_space_free;
332 bst->bs_aux = NULL;
333 bst->bs_stride_1 = 0;
334 bst->bs_stride_2 = 0;
335 bst->bs_stride_4 = 0;
336 bst->bs_stride_8 = 0;
337 }
338
339 void
arc_bus_space_init_arena(bus_space_tag_t bst,struct vmem * arena_store,struct vmem_btag * btag_store,unsigned int btag_count)340 arc_bus_space_init_arena(bus_space_tag_t bst, struct vmem *arena_store,
341 struct vmem_btag *btag_store, unsigned int btag_count)
342 {
343 int error __diagused;
344
345 bst->bs_arena = vmem_init(arena_store,
346 bst->bs_name, /* name */
347 0, /* addr */
348 0, /* size */
349 1, /* quantum */
350 NULL, /* importfn */
351 NULL, /* releasefn */
352 NULL, /* source */
353 0, /* qcache_max */
354 VM_NOSLEEP | VM_PRIVTAGS,
355 IPL_NONE);
356 KASSERT(bst->bs_arena != NULL);
357
358 vmem_add_bts(bst->bs_arena, btag_store, btag_count);
359 error = vmem_add(bst->bs_arena, bst->bs_start, bst->bs_size,
360 VM_NOSLEEP);
361 KASSERT(error == 0);
362 }
363
364 void
arc_bus_space_set_aligned_stride(bus_space_tag_t bst,unsigned int alignment_shift)365 arc_bus_space_set_aligned_stride(bus_space_tag_t bst,
366 unsigned int alignment_shift)
367 {
368
369 bst->bs_stride_1 = alignment_shift;
370 if (alignment_shift > 0)
371 --alignment_shift;
372 bst->bs_stride_2 = alignment_shift;
373 if (alignment_shift > 0)
374 --alignment_shift;
375 bst->bs_stride_4 = alignment_shift;
376 if (alignment_shift > 0)
377 --alignment_shift;
378 bst->bs_stride_8 = alignment_shift;
379 }
380
381 int
arc_bus_space_compose_handle(bus_space_tag_t bst,bus_addr_t addr,bus_size_t size,int flags,bus_space_handle_t * bshp)382 arc_bus_space_compose_handle(bus_space_tag_t bst, bus_addr_t addr,
383 bus_size_t size, int flags, bus_space_handle_t *bshp)
384 {
385 bus_space_handle_t bsh = bst->bs_vbase + (addr - bst->bs_start);
386
387 /*
388 * Since all buses can be linearly mappable, we don't have to check
389 * BUS_SPACE_MAP_LINEAR and BUS_SPACE_MAP_PREFETCHABLE.
390 */
391 if ((flags & BUS_SPACE_MAP_CACHEABLE) == 0) {
392 *bshp = bsh;
393 return 0;
394 }
395 if (bsh < MIPS_KSEG1_START) /* KUSEG or KSEG0 */
396 panic("arc_bus_space_compose_handle: bad address 0x%x", bsh);
397 if (bsh < MIPS_KSEG2_START) { /* KSEG1 */
398 *bshp = MIPS_PHYS_TO_KSEG0(MIPS_KSEG1_TO_PHYS(bsh));
399 return 0;
400 }
401 /*
402 * KSEG2:
403 * Do not make the page cacheable in this case, since:
404 * - the page which this bus_space belongs might include
405 * other bus_spaces.
406 * or
407 * - this bus might be mapped by wired TLB, in that case,
408 * we cannot manupulate cacheable attribute with page granularity.
409 */
410 #ifdef DIAGNOSTIC
411 printf("arc_bus_space_compose_handle: ignore cacheable 0x%x\n", bsh);
412 #endif
413 *bshp = bsh;
414 return 0;
415 }
416
417 int
arc_bus_space_dispose_handle(bus_space_tag_t bst,bus_space_handle_t bsh,bus_size_t size)418 arc_bus_space_dispose_handle(bus_space_tag_t bst, bus_space_handle_t bsh,
419 bus_size_t size)
420 {
421
422 return 0;
423 }
424
425 int
arc_bus_space_paddr(bus_space_tag_t bst,bus_space_handle_t bsh,paddr_t * pap)426 arc_bus_space_paddr(bus_space_tag_t bst, bus_space_handle_t bsh, paddr_t *pap)
427 {
428
429 if (bsh < MIPS_KSEG0_START) /* KUSEG */
430 panic("arc_bus_space_paddr(0x%qx): bad address",
431 (unsigned long long) bsh);
432 else if (bsh < MIPS_KSEG1_START) /* KSEG0 */
433 *pap = MIPS_KSEG0_TO_PHYS(bsh);
434 else if (bsh < MIPS_KSEG2_START) /* KSEG1 */
435 *pap = MIPS_KSEG1_TO_PHYS(bsh);
436 else { /* KSEG2 */
437 /*
438 * Since this region may be mapped by wired TLB,
439 * kvtophys() is not always available.
440 */
441 *pap = bst->bs_pbase + (bsh - bst->bs_vbase);
442 }
443 return 0;
444 }
445
446 int
arc_bus_space_map(bus_space_tag_t bst,bus_addr_t addr,bus_size_t size,int flags,bus_space_handle_t * bshp)447 arc_bus_space_map(bus_space_tag_t bst, bus_addr_t addr, bus_size_t size,
448 int flags, bus_space_handle_t *bshp)
449 {
450 int err;
451
452 if (addr < bst->bs_start || addr + size > bst->bs_start + bst->bs_size)
453 return EINVAL;
454
455 if (bst->bs_arena != NULL) {
456 err = vmem_xalloc_addr(bst->bs_arena, addr, size, VM_NOSLEEP);
457 if (err)
458 return err;
459 }
460
461 return bus_space_compose_handle(bst, addr, size, flags, bshp);
462 }
463
464 void
arc_bus_space_unmap(bus_space_tag_t bst,bus_space_handle_t bsh,bus_size_t size)465 arc_bus_space_unmap(bus_space_tag_t bst, bus_space_handle_t bsh,
466 bus_size_t size)
467 {
468
469 if (bst->bs_arena != NULL) {
470 paddr_t pa;
471 bus_addr_t addr;
472 int err;
473
474 /* bus_space_paddr() becomes unavailable after unmapping */
475 err = bus_space_paddr(bst, bsh, &pa);
476 if (err)
477 panic("arc_bus_space_unmap: %s va 0x%qx: error %d",
478 bst->bs_name, (unsigned long long) bsh, err);
479 addr = (bus_size_t)(pa - bst->bs_pbase) + bst->bs_start;
480 vmem_xfree(bst->bs_arena, addr, size);
481 }
482 bus_space_dispose_handle(bst, bsh, size);
483 }
484
485 int
arc_bus_space_subregion(bus_space_tag_t bst,bus_space_handle_t bsh,bus_size_t offset,bus_size_t size,bus_space_handle_t * nbshp)486 arc_bus_space_subregion(bus_space_tag_t bst, bus_space_handle_t bsh,
487 bus_size_t offset, bus_size_t size, bus_space_handle_t *nbshp)
488 {
489
490 *nbshp = bsh + offset;
491 return 0;
492 }
493
494 paddr_t
arc_bus_space_mmap(bus_space_tag_t bst,bus_addr_t addr,off_t off,int prot,int flags)495 arc_bus_space_mmap(bus_space_tag_t bst, bus_addr_t addr, off_t off, int prot,
496 int flags)
497 {
498
499 /*
500 * XXX We do not disallow mmap'ing of EISA/PCI I/O space here,
501 * XXX which we should be doing.
502 */
503
504 if (addr < bst->bs_start ||
505 (addr + off) >= (bst->bs_start + bst->bs_size))
506 return -1;
507
508 return mips_btop(bst->bs_pbase + (addr - bst->bs_start) + off);
509 }
510
511 int
arc_bus_space_alloc(bus_space_tag_t bst,bus_addr_t start,bus_addr_t end,bus_size_t size,bus_size_t align,bus_size_t boundary,int flags,bus_addr_t * addrp,bus_space_handle_t * bshp)512 arc_bus_space_alloc(bus_space_tag_t bst, bus_addr_t start, bus_addr_t end,
513 bus_size_t size, bus_size_t align, bus_size_t boundary, int flags,
514 bus_addr_t *addrp, bus_space_handle_t *bshp)
515 {
516 vmem_addr_t addr;
517 int err;
518
519 if (bst->bs_arena == NULL)
520 panic("arc_bus_space_alloc: vmem arena %s not available",
521 bst->bs_name);
522
523 if (start < bst->bs_start ||
524 start + size > bst->bs_start + bst->bs_size)
525 return EINVAL;
526
527 err = vmem_xalloc(bst->bs_arena, size,
528 align, /* align */
529 0, /* phase */
530 boundary, /* nocross */
531 start, /* minaddr */
532 end, /* maxaddr */
533 VM_BESTFIT | VM_NOSLEEP,
534 &addr);
535 if (err)
536 return err;
537
538 *addrp = addr;
539 return bus_space_compose_handle(bst, addr, size, flags, bshp);
540 }
541