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