xref: /netbsd-src/sys/arch/hpcmips/hpcmips/bus_space.c (revision 8b0f9554ff8762542c4defc4f70e1eb76fb508fa)
1 /*	$NetBSD: bus_space.c,v 1.25 2005/12/11 12:17:33 christos Exp $	*/
2 
3 /*-
4  * Copyright (c) 1998 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
9  * NASA Ames Research Center.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  * 3. All advertising materials mentioning features or use of this software
20  *    must display the following acknowledgement:
21  *	This product includes software developed by the NetBSD
22  *	Foundation, Inc. and its contributors.
23  * 4. Neither the name of The NetBSD Foundation nor the names of its
24  *    contributors may be used to endorse or promote products derived
25  *    from this software without specific prior written permission.
26  *
27  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
28  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
29  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
30  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
31  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
32  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
33  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
34  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
35  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
36  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
37  * POSSIBILITY OF SUCH DAMAGE.
38  */
39 
40 #include <sys/cdefs.h>
41 __KERNEL_RCSID(0, "$NetBSD: bus_space.c,v 1.25 2005/12/11 12:17:33 christos Exp $");
42 
43 #include <sys/param.h>
44 #include <sys/systm.h>
45 #include <sys/malloc.h>
46 #include <sys/extent.h>
47 
48 #include <uvm/uvm_extern.h>
49 
50 #include <mips/cache.h>
51 #include <mips/pte.h>
52 #include <machine/bus.h>
53 #include <machine/bus_space_hpcmips.h>
54 
55 #ifdef BUS_SPACE_DEBUG
56 #define	DPRINTF(arg) printf arg
57 #else
58 #define	DPRINTF(arg)
59 #endif
60 
61 #define MAX_BUSSPACE_TAG 10
62 
63 /* proto types */
64 bus_space_handle_t __hpcmips_cacheable(struct bus_space_tag_hpcmips*,
65     bus_addr_t, bus_size_t, int);
66 bus_space_protos(_);
67 bus_space_protos(bs_notimpl);
68 
69 /* variables */
70 static  struct bus_space_tag_hpcmips __bus_space[MAX_BUSSPACE_TAG];
71 static int __bus_space_index;
72 static struct bus_space_tag_hpcmips __sys_bus_space = {
73 	{
74 		NULL,
75 		{
76 			/* mapping/unmapping */
77 			__bs_map,
78 			__bs_unmap,
79 			__bs_subregion,
80 
81 			/* allocation/deallocation */
82 			__bs_alloc,
83 			__bs_free,
84 
85 			/* get kernel virtual address */
86 			bs_notimpl_bs_vaddr, /* there is no linear mapping */
87 
88 			/* Mmap bus space for user */
89 			bs_notimpl_bs_mmap,
90 
91 			/* barrier */
92 			__bs_barrier,
93 
94 			/* probe */
95 			__bs_peek,
96 			__bs_poke,
97 
98 			/* read (single) */
99 			__bs_r_1,
100 			__bs_r_2,
101 			__bs_r_4,
102 			bs_notimpl_bs_r_8,
103 
104 			/* read multiple */
105 			__bs_rm_1,
106 			__bs_rm_2,
107 			__bs_rm_4,
108 			bs_notimpl_bs_rm_8,
109 
110 			/* read region */
111 			__bs_rr_1,
112 			__bs_rr_2,
113 			__bs_rr_4,
114 			bs_notimpl_bs_rr_8,
115 
116 			/* write (single) */
117 			__bs_w_1,
118 			__bs_w_2,
119 			__bs_w_4,
120 			bs_notimpl_bs_w_8,
121 
122 			/* write multiple */
123 			__bs_wm_1,
124 			__bs_wm_2,
125 			__bs_wm_4,
126 			bs_notimpl_bs_wm_8,
127 
128 			/* write region */
129 			__bs_wr_1,
130 			__bs_wr_2,
131 			__bs_wr_4,
132 			bs_notimpl_bs_wr_8,
133 
134 			/* set multi */
135 			__bs_sm_1,
136 			__bs_sm_2,
137 			__bs_sm_4,
138 			bs_notimpl_bs_sm_8,
139 
140 			/* set region */
141 			__bs_sr_1,
142 			__bs_sr_2,
143 			__bs_sr_4,
144 			bs_notimpl_bs_sr_8,
145 
146 			/* copy */
147 			__bs_c_1,
148 			__bs_c_2,
149 			__bs_c_4,
150 			bs_notimpl_bs_c_8,
151 		},
152 	},
153 
154 	"whole bus space",	/* bus name */
155 	0,			/* extent base */
156 	0xffffffff,		/* extent size */
157 	NULL,			/* pointer for extent structure */
158 };
159 static bus_space_tag_t __sys_bus_space_tag = &__sys_bus_space.bst;
160 
161 bus_space_tag_t
162 hpcmips_system_bus_space()
163 {
164 
165 	return (__sys_bus_space_tag);
166 }
167 
168 struct bus_space_tag_hpcmips *
169 hpcmips_system_bus_space_hpcmips()
170 {
171 
172 	return (&__sys_bus_space);
173 }
174 
175 struct bus_space_tag_hpcmips *
176 hpcmips_alloc_bus_space_tag()
177 {
178 
179 	if (__bus_space_index >= MAX_BUSSPACE_TAG) {
180 		panic("hpcmips_internal_alloc_bus_space_tag: tag full.");
181 	}
182 
183 	return (&__bus_space[__bus_space_index++]);
184 }
185 
186 void
187 hpcmips_init_bus_space(struct bus_space_tag_hpcmips *t,
188     struct bus_space_tag_hpcmips *basetag,
189     const char *name, u_int32_t base, u_int32_t size)
190 {
191 	u_int32_t pa, endpa;
192 	vaddr_t va;
193 
194 	if (basetag != NULL)
195 		memcpy(t, basetag, sizeof(struct bus_space_tag_hpcmips));
196 	strncpy(t->name, name, sizeof(t->name));
197 	t->name[sizeof(t->name) - 1] = '\0';
198 	t->base = base;
199 	t->size = size;
200 
201 	/*
202 	 * If request physical address is greater than 512MByte,
203 	 * mapping it to kseg2.
204 	 */
205 	if (t->base >= 0x20000000) {
206 		pa = mips_trunc_page(t->base);
207 		endpa = mips_round_page(t->base + t->size);
208 
209 		if (!(va = uvm_km_alloc(kernel_map, endpa - pa, 0,
210 		    UVM_KMF_VAONLY))) {
211 			panic("hpcmips_init_bus_space_extent:"
212 			    "can't allocate kernel virtual");
213 		}
214 		DPRINTF(("pa:0x%08x -> kv:0x%08x+0x%08x",
215 		    (unsigned int)t->base, (unsigned int)va, t->size));
216 		t->base = va; /* kseg2 addr */
217 
218 		for (; pa < endpa; pa += PAGE_SIZE, va += PAGE_SIZE) {
219 			pmap_kenter_pa(va, pa, VM_PROT_READ | VM_PROT_WRITE);
220 		}
221 		pmap_update(pmap_kernel());
222 	}
223 
224 	t->extent = (void*)extent_create(t->name, t->base,
225 	    t->base + t->size, M_DEVBUF,
226 	    0, 0, EX_NOWAIT);
227 	if (!t->extent) {
228 		panic("hpcmips_init_bus_space_extent:"
229 		    "unable to allocate %s map", t->name);
230 	}
231 }
232 
233 bus_space_handle_t
234 __hpcmips_cacheable(struct bus_space_tag_hpcmips *t, bus_addr_t bpa,
235     bus_size_t size, int cacheable)
236 {
237 	vaddr_t va, endva;
238 	pt_entry_t *pte;
239 	u_int32_t opte, npte;
240 
241 	if (t->base >= MIPS_KSEG2_START) {
242 		va = mips_trunc_page(bpa);
243 		endva = mips_round_page(bpa + size);
244 		npte = CPUISMIPS3 ? MIPS3_PG_UNCACHED : MIPS1_PG_N;
245 
246 		mips_dcache_wbinv_range(va, endva - va);
247 
248 		for (; va < endva; va += PAGE_SIZE) {
249 			pte = kvtopte(va);
250 			opte = pte->pt_entry;
251 			if (cacheable) {
252 				opte &= ~npte;
253 			} else {
254 				opte |= npte;
255 			}
256 			pte->pt_entry = opte;
257 			/*
258 			 * Update the same virtual address entry.
259 			 */
260 			MachTLBUpdate(va, opte);
261 		}
262 		return (bpa);
263 	}
264 
265 	return (cacheable ? MIPS_PHYS_TO_KSEG0(bpa) : MIPS_PHYS_TO_KSEG1(bpa));
266 }
267 
268 /* ARGSUSED */
269 int
270 __bs_map(bus_space_tag_t tx, bus_addr_t bpa, bus_size_t size, int flags,
271     bus_space_handle_t *bshp)
272 {
273 	struct bus_space_tag_hpcmips *t = (struct bus_space_tag_hpcmips *)tx;
274 	int err;
275 	int cacheable = flags & BUS_SPACE_MAP_CACHEABLE;
276 
277 	DPRINTF(("\tbus_space_map:%#lx(%#lx)+%#lx\n",
278 	    bpa, bpa + t->base, size));
279 
280 	if (!t->extent) { /* Before autoconfiguration, can't use extent */
281 		DPRINTF(("bus_space_map: map temporary region:"
282 		    "0x%08lx-0x%08lx\n", bpa, bpa+size));
283 		bpa += t->base;
284 	} else {
285 		bpa += t->base;
286 		if ((err = extent_alloc_region(t->extent, bpa, size,
287 		    EX_NOWAIT|EX_MALLOCOK))) {
288 			DPRINTF(("\tbus_space_map: "
289 			    "extent_alloc_regiion() failed\n"));
290 			return (err);
291 		}
292 	}
293 	*bshp = __hpcmips_cacheable(t, bpa, size, cacheable);
294 
295 	return (0);
296 }
297 
298 /* ARGSUSED */
299 void
300 __bs_unmap(bus_space_tag_t tx, bus_space_handle_t bsh, bus_size_t size)
301 {
302 	struct bus_space_tag_hpcmips *t = (struct bus_space_tag_hpcmips *)tx;
303 	int err;
304 	u_int32_t addr;
305 
306 	if (!t->extent) {
307 		return; /* Before autoconfiguration, can't use extent */
308 	}
309 
310 	if (t->base < MIPS_KSEG2_START) {
311 		addr = MIPS_KSEG1_TO_PHYS(bsh);
312 	} else {
313 		addr = bsh;
314 	}
315 
316 	if ((err = extent_free(t->extent, addr, size, EX_NOWAIT))) {
317 		DPRINTF(("warning: %#lx-%#lx of %s space lost\n",
318 		    bsh, bsh+size, t->name));
319 	}
320 }
321 
322 /* ARGSUSED */
323 int
324 __bs_subregion(bus_space_tag_t t, bus_space_handle_t bsh,
325     bus_size_t offset, bus_size_t size, bus_space_handle_t *nbshp)
326 {
327 
328 	*nbshp = bsh + offset;
329 
330 	return (0);
331 }
332 
333 /* ARGSUSED */
334 int
335 __bs_alloc(bus_space_tag_t tx, bus_addr_t rstart, bus_addr_t rend,
336     bus_size_t size, bus_size_t alignment, bus_size_t boundary, int flags,
337     bus_addr_t *bpap, bus_space_handle_t *bshp)
338 {
339 	struct bus_space_tag_hpcmips *t = (struct bus_space_tag_hpcmips *)tx;
340 	int cacheable = flags & BUS_SPACE_MAP_CACHEABLE;
341 	u_long bpa;
342 	int err;
343 
344 	if (!t->extent)
345 		panic("bus_space_alloc: no extent");
346 
347 	DPRINTF(("\tbus_space_alloc:%#lx(%#lx)+%#lx\n", bpa,
348 	    bpa - t->base, size));
349 
350 	rstart += t->base;
351 	rend += t->base;
352 	if ((err = extent_alloc_subregion(t->extent, rstart, rend, size,
353 	    alignment, boundary, EX_FAST|EX_NOWAIT|EX_MALLOCOK, &bpa))) {
354 		return (err);
355 	}
356 
357 	*bshp = __hpcmips_cacheable(t, bpa, size, cacheable);
358 
359 	if (bpap) {
360 		*bpap = bpa;
361 	}
362 
363 	return (0);
364 }
365 
366 /* ARGSUSED */
367 void
368 __bs_free(bus_space_tag_t t, bus_space_handle_t bsh, bus_size_t size)
369 {
370 	/* bus_space_unmap() does all that we need to do. */
371 	bus_space_unmap(t, bsh, size);
372 }
373 
374 void
375 __bs_barrier(bus_space_tag_t t, bus_space_handle_t bsh, bus_size_t offset,
376     bus_size_t len, int flags)
377 {
378 	wbflush();
379 }
380 
381 int
382 __bs_peek(bus_space_tag_t t, bus_space_handle_t bsh, bus_size_t offset,
383     size_t size, void *ptr)
384 {
385 	u_int32_t tmp;
386 
387 	if (badaddr((void *)(bsh + offset), size))
388 		return (-1);
389 
390 	if (ptr == NULL)
391 		ptr = &tmp;
392 
393 	switch(size) {
394 	case 1:
395 		*((u_int8_t *)ptr) = bus_space_read_1(t, bsh, offset);
396 		break;
397 	case 2:
398 		*((u_int16_t *)ptr) = bus_space_read_2(t, bsh, offset);
399 		break;
400 	case 4:
401 		*((u_int32_t *)ptr) = bus_space_read_4(t, bsh, offset);
402 		break;
403 	default:
404 		panic("bus_space_peek: bad size, %d", size);
405 	}
406 
407 	return (0);
408 }
409 
410 int
411 __bs_poke(bus_space_tag_t t, bus_space_handle_t bsh, bus_size_t offset,
412     size_t size, u_int32_t val)
413 {
414 
415 	if (badaddr((void *)(bsh + offset), size))
416 		return (-1);
417 
418 	switch(size) {
419 	case 1:
420 		bus_space_write_1(t, bsh, offset, val);
421 		break;
422 	case 2:
423 		bus_space_write_2(t, bsh, offset, val);
424 		break;
425 	case 4:
426 		bus_space_write_4(t, bsh, offset, val);
427 		break;
428 	default:
429 		panic("bus_space_poke: bad size, %d", size);
430 	}
431 
432 	return (0);
433 }
434 
435 u_int8_t
436 __bs_r_1(bus_space_tag_t t, bus_space_handle_t bsh, bus_size_t offset)
437 {
438 	wbflush();
439 	return (*(volatile u_int8_t *)(bsh + offset));
440 }
441 
442 u_int16_t
443 __bs_r_2(bus_space_tag_t t, bus_space_handle_t bsh, bus_size_t offset)
444 {
445 	wbflush();
446 	return (*(volatile u_int16_t *)(bsh + offset));
447 }
448 
449 u_int32_t
450 __bs_r_4(bus_space_tag_t t, bus_space_handle_t bsh, bus_size_t offset)
451 {
452 	wbflush();
453 	return (*(volatile u_int32_t *)(bsh + offset));
454 }
455 
456 void
457 __bs_rm_1(bus_space_tag_t t, bus_space_handle_t bsh, bus_size_t offset,
458     u_int8_t *addr, bus_size_t count) {
459 	while (count--)
460 		*addr++ = bus_space_read_1(t, bsh, offset);
461 }
462 
463 void
464 __bs_rm_2(bus_space_tag_t t, bus_space_handle_t bsh, bus_size_t offset,
465     u_int16_t *addr, bus_size_t count)
466 {
467 	while (count--)
468 		*addr++ = bus_space_read_2(t, bsh, offset);
469 }
470 
471 void
472 __bs_rm_4(bus_space_tag_t t, bus_space_handle_t bsh, bus_size_t offset,
473     u_int32_t *addr, bus_size_t count)
474 {
475 	while (count--)
476 		*addr++ = bus_space_read_4(t, bsh, offset);
477 }
478 
479 void
480 __bs_rr_1(bus_space_tag_t t, bus_space_handle_t bsh, bus_size_t offset,
481     u_int8_t *addr, bus_size_t count)
482 {
483 	while (count--) {
484 		*addr++ = bus_space_read_1(t, bsh, offset);
485 		offset += sizeof(*addr);
486 	}
487 }
488 
489 void
490 __bs_rr_2(bus_space_tag_t t, bus_space_handle_t bsh, bus_size_t offset,
491     u_int16_t *addr, bus_size_t count)
492 {
493 	while (count--) {
494 		*addr++ = bus_space_read_2(t, bsh, offset);
495 		offset += sizeof(*addr);
496 	}
497 }
498 
499 void
500 __bs_rr_4(bus_space_tag_t t, bus_space_handle_t bsh, bus_size_t offset,
501     u_int32_t *addr, bus_size_t count)
502 {
503 	while (count--) {
504 		*addr++ = bus_space_read_4(t, bsh, offset);
505 		offset += sizeof(*addr);
506 	}
507 }
508 
509 void
510 __bs_w_1(bus_space_tag_t t, bus_space_handle_t bsh, bus_size_t offset,
511     u_int8_t value)
512 {
513 	*(volatile u_int8_t *)(bsh + offset) = value;
514 	wbflush();
515 }
516 
517 void
518 __bs_w_2(bus_space_tag_t t, bus_space_handle_t bsh, bus_size_t offset,
519     u_int16_t value)
520 {
521 	*(volatile u_int16_t *)(bsh + offset) = value;
522 	wbflush();
523 }
524 
525 void
526 __bs_w_4(bus_space_tag_t t, bus_space_handle_t bsh, bus_size_t offset,
527     u_int32_t value)
528 {
529 	*(volatile u_int32_t *)(bsh + offset) = value;
530 	wbflush();
531 }
532 
533 void
534 __bs_wm_1(bus_space_tag_t t, bus_space_handle_t bsh, bus_size_t offset,
535     const u_int8_t *addr, bus_size_t count)
536 {
537 	while (count--)
538 		bus_space_write_1(t, bsh, offset, *addr++);
539 }
540 
541 void
542 __bs_wm_2(bus_space_tag_t t, bus_space_handle_t bsh, bus_size_t offset,
543     const u_int16_t *addr, bus_size_t count)
544 {
545 	while (count--)
546 		bus_space_write_2(t, bsh, offset, *addr++);
547 }
548 
549 void
550 __bs_wm_4(bus_space_tag_t t, bus_space_handle_t bsh, bus_size_t offset,
551     const u_int32_t *addr, bus_size_t count)
552 {
553 	while (count--)
554 		bus_space_write_4(t, bsh, offset, *addr++);
555 }
556 
557 void
558 __bs_wr_1(bus_space_tag_t t, bus_space_handle_t bsh, bus_size_t offset,
559     const u_int8_t *addr, bus_size_t count)
560 {
561 	while (count--) {
562 		bus_space_write_1(t, bsh, offset, *addr++);
563 		offset += sizeof(*addr);
564 	}
565 }
566 
567 void
568 __bs_wr_2(bus_space_tag_t t, bus_space_handle_t bsh, bus_size_t offset,
569     const u_int16_t *addr, bus_size_t count)
570 {
571 	while (count--) {
572 		bus_space_write_2(t, bsh, offset, *addr++);
573 		offset += sizeof(*addr);
574 	}
575 }
576 
577 void
578 __bs_wr_4(bus_space_tag_t t, bus_space_handle_t bsh, bus_size_t offset,
579     const u_int32_t *addr, bus_size_t count)
580 {
581 	while (count--) {
582 		bus_space_write_4(t, bsh, offset, *addr++);
583 		offset += sizeof(*addr);
584 	}
585 }
586 
587 void
588 __bs_sm_1(bus_space_tag_t t, bus_space_handle_t bsh,
589     bus_size_t offset, u_int8_t value, bus_size_t count)
590 {
591 	while (count--)
592 		bus_space_write_1(t, bsh, offset, value);
593 }
594 
595 void
596 __bs_sm_2(bus_space_tag_t t, bus_space_handle_t bsh,
597     bus_size_t offset, u_int16_t value, bus_size_t count)
598 {
599 	while (count--)
600 		bus_space_write_2(t, bsh, offset, value);
601 }
602 
603 void
604 __bs_sm_4(bus_space_tag_t t, bus_space_handle_t bsh,
605     bus_size_t offset, u_int32_t value, bus_size_t count)
606 {
607 	while (count--)
608 		bus_space_write_4(t, bsh, offset, value);
609 }
610 
611 
612 void
613 __bs_sr_1(bus_space_tag_t t, bus_space_handle_t bsh,
614     bus_size_t offset, u_int8_t value, bus_size_t count)
615 {
616 	while (count--) {
617 		bus_space_write_1(t, bsh, offset, value);
618 		offset += (value);
619 	}
620 }
621 
622 void
623 __bs_sr_2(bus_space_tag_t t, bus_space_handle_t bsh,
624     bus_size_t offset, u_int16_t value, bus_size_t count)
625 {
626 	while (count--) {
627 		bus_space_write_2(t, bsh, offset, value);
628 		offset += (value);
629 	}
630 }
631 
632 void
633 __bs_sr_4(bus_space_tag_t t, bus_space_handle_t bsh,
634     bus_size_t offset, u_int32_t value, bus_size_t count)
635 {
636 	while (count--) {
637 		bus_space_write_4(t, bsh, offset, value);
638 		offset += (value);
639 	}
640 }
641 
642 #define __bs_c_n(n)							\
643 void __CONCAT(__bs_c_,n)(bus_space_tag_t t, bus_space_handle_t h1,	\
644     bus_size_t o1, bus_space_handle_t h2, bus_size_t o2, bus_size_t c)	\
645 {									\
646 	bus_size_t o;							\
647 									\
648 	if ((h1 + o1) >= (h2 + o2)) {					\
649 		/* src after dest: copy forward */			\
650 		for (o = 0; c != 0; c--, o += n)			\
651 			__CONCAT(bus_space_write_,n)(t, h2, o2 + o,	\
652 			    __CONCAT(bus_space_read_,n)(t, h1, o1 + o));\
653 	} else {							\
654 		/* dest after src: copy backwards */			\
655 		for (o = (c - 1) * n; c != 0; c--, o -= n)		\
656 			__CONCAT(bus_space_write_,n)(t, h2, o2 + o,	\
657 			    __CONCAT(bus_space_read_,n)(t, h1, o1 + o));\
658 	}								\
659 }
660 
661 __bs_c_n(1)
662 __bs_c_n(2)
663 __bs_c_n(4)
664