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 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 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 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 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 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 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 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 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 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 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 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