xref: /openbsd/sys/arch/powerpc/powerpc/bus_space.c (revision 9abf980c)
1 /*	$OpenBSD: bus_space.c,v 1.5 2015/02/09 13:35:44 deraadt Exp $	*/
2 /*	$NetBSD: machdep.c,v 1.4 1996/10/16 19:33:11 ws Exp $	*/
3 
4 /*
5  * Copyright (C) 1995, 1996 Wolfgang Solfrank.
6  * Copyright (C) 1995, 1996 TooLs GmbH.
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  * 3. All advertising materials mentioning features or use of this software
18  *    must display the following acknowledgement:
19  *	This product includes software developed by TooLs GmbH.
20  * 4. The name of TooLs GmbH may not be used to endorse or promote products
21  *    derived from this software without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR
24  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
25  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
26  * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
27  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
28  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
29  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
30  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
31  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
32  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33  */
34 
35 #include <sys/param.h>
36 #include <sys/systm.h>
37 #include <sys/malloc.h>
38 #include <sys/extent.h>
39 
40 #include <uvm/uvm_extern.h>
41 
42 #include <machine/bus.h>
43 
44 extern int ppc_malloc_ok;
45 extern struct extent *devio_ex;
46 
47 int bus_mem_add_mapping(bus_addr_t, bus_size_t, int, bus_space_handle_t *);
48 bus_addr_t bus_space_unmap_p(bus_space_tag_t, bus_space_handle_t,  bus_size_t);
49 
50 
51 /* BUS functions */
52 int
bus_space_map(bus_space_tag_t t,bus_addr_t bpa,bus_size_t size,int flags,bus_space_handle_t * bshp)53 bus_space_map(bus_space_tag_t t, bus_addr_t bpa, bus_size_t size,
54     int flags, bus_space_handle_t *bshp)
55 {
56 	int error;
57 
58 	if  (POWERPC_BUS_TAG_BASE(t) == 0) {
59 		/* if bus has base of 0 fail. */
60 		return EINVAL;
61 	}
62 
63 	/*
64 	 * The address may, or may not, be relative to the
65 	 * base address of this bus.
66 	 */
67 	if (bpa < POWERPC_BUS_TAG_BASE(t))
68 		bpa += POWERPC_BUS_TAG_BASE(t);
69 
70 	if ((error = extent_alloc_region(devio_ex, bpa, size, EX_NOWAIT |
71 	    (ppc_malloc_ok ? EX_MALLOCOK : 0))))
72 		return error;
73 
74 	if ((error = bus_mem_add_mapping(bpa, size, flags, bshp))) {
75 		if (extent_free(devio_ex, bpa, size, EX_NOWAIT |
76 			(ppc_malloc_ok ? EX_MALLOCOK : 0)))
77 		{
78 			printf("bus_space_map: pa 0x%lx, size 0x%lx\n",
79 				bpa, size);
80 			printf("bus_space_map: can't free region\n");
81 		}
82 	}
83 	return error;
84 }
85 
86 bus_addr_t
bus_space_unmap_p(bus_space_tag_t t,bus_space_handle_t bsh,bus_size_t size)87 bus_space_unmap_p(bus_space_tag_t t, bus_space_handle_t bsh, bus_size_t size)
88 {
89 	bus_addr_t paddr;
90 
91 	pmap_extract(pmap_kernel(), bsh, &paddr);
92 	bus_space_unmap((t), (bsh), (size));
93 	return paddr ;
94 }
95 
96 void
bus_space_unmap(bus_space_tag_t t,bus_space_handle_t bsh,bus_size_t size)97 bus_space_unmap(bus_space_tag_t t, bus_space_handle_t bsh, bus_size_t size)
98 {
99 	bus_addr_t sva;
100 	bus_size_t off, len;
101 	bus_addr_t bpa;
102 
103 	sva = trunc_page(bsh);
104 	off = bsh - sva;
105 	len = round_page(size+off);
106 
107 	if (pmap_extract(pmap_kernel(), sva, &bpa) == TRUE) {
108 		if (extent_free(devio_ex, bpa | (bsh & PAGE_MASK), size,
109 		    EX_NOWAIT | (ppc_malloc_ok ? EX_MALLOCOK : 0)))
110 		{
111 			printf("bus_space_map: pa 0x%lx, size 0x%lx\n",
112 				bpa, size);
113 			printf("bus_space_map: can't free region\n");
114 		}
115 	}
116 
117 	pmap_kremove(sva, len);
118 	pmap_update(pmap_kernel());
119 
120 	/* do not free memory which was stolen from the vm system */
121 	if (ppc_malloc_ok &&
122 	    ((sva >= VM_MIN_KERNEL_ADDRESS) && (sva < VM_MAX_KERNEL_ADDRESS)))
123 		km_free((void *)sva, len, &kv_any, &kp_none);
124 }
125 
126 paddr_t
bus_space_mmap(bus_space_tag_t t,bus_addr_t bpa,off_t off,int prot,int flags)127 bus_space_mmap(bus_space_tag_t t, bus_addr_t bpa, off_t off, int prot,
128     int flags)
129 {
130 	int pmapflags = PMAP_NOCACHE;
131 
132 	if (POWERPC_BUS_TAG_BASE(t) == 0)
133 		return (-1);
134 
135 	/*
136 	 * The address may, or may not, be relative to the
137 	 * base address of this bus.
138 	 */
139 	if (bpa < POWERPC_BUS_TAG_BASE(t))
140 		bpa += POWERPC_BUS_TAG_BASE(t);
141 
142 	if (flags & BUS_SPACE_MAP_CACHEABLE)
143 		pmapflags &= ~PMAP_NOCACHE;
144 
145 	return ((bpa + off) | pmapflags);
146 }
147 
148 vaddr_t ppc_kvm_stolen = VM_KERN_ADDRESS_SIZE;
149 
150 int
bus_mem_add_mapping(bus_addr_t bpa,bus_size_t size,int flags,bus_space_handle_t * bshp)151 bus_mem_add_mapping(bus_addr_t bpa, bus_size_t size, int flags,
152     bus_space_handle_t *bshp)
153 {
154 	bus_addr_t vaddr;
155 	bus_addr_t spa, epa;
156 	bus_size_t off, len;
157 	int pmapflags;
158 
159 	spa = trunc_page(bpa);
160 	epa = bpa + size;
161 	off = bpa - spa;
162 	len = round_page(size+off);
163 
164 #ifdef DIAGNOSTIC
165 	if (epa <= spa && epa != 0)
166 		panic("bus_mem_add_mapping: overflow");
167 #endif
168 
169 	if (ppc_malloc_ok == 0) {
170 		/* need to steal vm space before kernel vm is initialized */
171 		vaddr = VM_MIN_KERNEL_ADDRESS + ppc_kvm_stolen;
172 		ppc_kvm_stolen += len;
173 		if (ppc_kvm_stolen > PPC_SEGMENT_LENGTH) {
174 			panic("ppc_kvm_stolen, out of space");
175 		}
176 	} else {
177 		vaddr = (vaddr_t)km_alloc(len, &kv_any, &kp_none, &kd_nowait);
178 		if (vaddr == 0)
179 			return (ENOMEM);
180 	}
181 	*bshp = vaddr + off;
182 
183 	if (flags & BUS_SPACE_MAP_CACHEABLE)
184 		pmapflags = PMAP_WT;
185 	else
186 		pmapflags = PMAP_NOCACHE;
187 
188 	for (; len > 0; len -= PAGE_SIZE) {
189 		pmap_kenter_pa(vaddr, spa | pmapflags, PROT_READ | PROT_WRITE);
190 		spa += PAGE_SIZE;
191 		vaddr += PAGE_SIZE;
192 	}
193 	pmap_update(pmap_kernel());
194 
195 	return (0);
196 }
197 
198 int
bus_space_alloc(bus_space_tag_t tag,bus_addr_t rstart,bus_addr_t rend,bus_size_t size,bus_size_t alignment,bus_size_t boundary,int flags,bus_addr_t * addrp,bus_space_handle_t * handlep)199 bus_space_alloc(bus_space_tag_t tag, bus_addr_t rstart, bus_addr_t rend,
200     bus_size_t size, bus_size_t alignment, bus_size_t boundary, int flags,
201     bus_addr_t *addrp, bus_space_handle_t *handlep)
202 {
203 
204 	panic("bus_space_alloc: unimplemented");
205 }
206 
207 void
bus_space_free(bus_space_tag_t tag,bus_space_handle_t handle,bus_size_t size)208 bus_space_free(bus_space_tag_t tag, bus_space_handle_t handle, bus_size_t size)
209 {
210 
211 	panic("bus_space_free: unimplemented");
212 }
213 
214 void *
mapiodev(paddr_t pa,psize_t len)215 mapiodev(paddr_t pa, psize_t len)
216 {
217 	paddr_t spa;
218 	vaddr_t vaddr, va;
219 	int off;
220 	int size;
221 
222 	spa = trunc_page(pa);
223 	off = pa - spa;
224 	size = round_page(off+len);
225 	if (ppc_malloc_ok == 0) {
226 		/* need to steal vm space before kernel vm is initialized */
227 		va = VM_MIN_KERNEL_ADDRESS + ppc_kvm_stolen;
228 		ppc_kvm_stolen += size;
229 		if (ppc_kvm_stolen > PPC_SEGMENT_LENGTH) {
230 			panic("ppc_kvm_stolen, out of space");
231 		}
232 	} else {
233 		va = (vaddr_t)km_alloc(size, &kv_any, &kp_none, &kd_nowait);
234 		if (va == 0)
235 			return (NULL);
236 	}
237 
238 	for (vaddr = va; size > 0; size -= PAGE_SIZE) {
239 		pmap_kenter_pa(vaddr, spa, PROT_READ | PROT_WRITE);
240 		spa += PAGE_SIZE;
241 		vaddr += PAGE_SIZE;
242 	}
243 	pmap_update(pmap_kernel());
244 
245 	return (void *) (va+off);
246 }
247 
248 void
unmapiodev(void * kva,psize_t p_size)249 unmapiodev(void *kva, psize_t p_size)
250 {
251 	vaddr_t vaddr;
252 	int size;
253 
254 	size = round_page(p_size);
255 
256 	vaddr = trunc_page((vaddr_t)kva);
257 
258 	pmap_kremove(vaddr, size);
259 	pmap_update(pmap_kernel());
260 
261 	km_free((void *)vaddr, size, &kv_any, &kp_none);
262 }
263 
264 
265 /*
266  * probably should be ppc_space_copy
267  */
268 
269 #define _CONCAT(A,B) A ## B
270 #define __C(A,B)	_CONCAT(A,B)
271 
272 #define BUS_SPACE_COPY_N(BYTES,TYPE)					\
273 void									\
274 __C(bus_space_copy_,BYTES)(void *v, bus_space_handle_t h1,		\
275     bus_size_t o1, bus_space_handle_t h2, bus_size_t o2,		\
276     bus_size_t c)							\
277 {									\
278 	TYPE *src, *dst;						\
279 	int i;								\
280 									\
281 	src = (TYPE *) (h1+o1);						\
282 	dst = (TYPE *) (h2+o2);						\
283 									\
284 	if (h1 == h2 && o2 > o1)					\
285 		for (i = c-1; i >= 0; i--)				\
286 			dst[i] = src[i];				\
287 	else								\
288 		for (i = 0; i < c; i++)					\
289 			dst[i] = src[i];				\
290 }
291 BUS_SPACE_COPY_N(1,u_int8_t)
292 BUS_SPACE_COPY_N(2,u_int16_t)
293 BUS_SPACE_COPY_N(4,u_int32_t)
294 
295 void
bus_space_set_region_1(bus_space_tag_t t,bus_space_handle_t h,bus_size_t o,u_int8_t val,bus_size_t c)296 bus_space_set_region_1(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o,
297     u_int8_t val, bus_size_t c)
298 {
299 	u_int8_t *dst;
300 	int i;
301 
302 	dst = (u_int8_t *) (h+o);
303 
304 	for (i = 0; i < c; i++)
305 		dst[i] = val;
306 }
307 
308 void
bus_space_set_region_2(bus_space_tag_t t,bus_space_handle_t h,bus_size_t o,u_int16_t val,bus_size_t c)309 bus_space_set_region_2(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o,
310     u_int16_t val, bus_size_t c)
311 {
312 	u_int16_t *dst;
313 	int i;
314 
315 	dst = (u_int16_t *) (h+o);
316 	val = swap16(val);
317 
318 	for (i = 0; i < c; i++)
319 		dst[i] = val;
320 }
321 void
bus_space_set_region_4(bus_space_tag_t t,bus_space_handle_t h,bus_size_t o,u_int32_t val,bus_size_t c)322 bus_space_set_region_4(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o,
323     u_int32_t val, bus_size_t c)
324 {
325 	u_int32_t *dst;
326 	int i;
327 
328 	dst = (u_int32_t *) (h+o);
329 	val = swap32(val);
330 
331 	for (i = 0; i < c; i++)
332 		dst[i] = val;
333 }
334 
335 #define BUS_SPACE_READ_RAW_MULTI_N(BYTES,SHIFT,TYPE)			\
336 void									\
337 __C(bus_space_read_raw_multi_,BYTES)(bus_space_tag_t bst,		\
338     bus_space_handle_t h, bus_addr_t o, u_int8_t *dst, bus_size_t size)	\
339 {									\
340 	TYPE *src;							\
341 	TYPE *rdst = (TYPE *)dst;					\
342 	int i;								\
343 	int count = size >> SHIFT;					\
344 									\
345 	src = (TYPE *)(h+o);						\
346 	for (i = 0; i < count; i++) {					\
347 		rdst[i] = *src;						\
348 		__asm__("eieio");					\
349 	}								\
350 }
351 BUS_SPACE_READ_RAW_MULTI_N(2,1,u_int16_t)
352 BUS_SPACE_READ_RAW_MULTI_N(4,2,u_int32_t)
353 
354 #define BUS_SPACE_WRITE_RAW_MULTI_N(BYTES,SHIFT,TYPE)			\
355 void									\
356 __C(bus_space_write_raw_multi_,BYTES)( bus_space_tag_t bst,		\
357     bus_space_handle_t h, bus_addr_t o, const u_int8_t *src,		\
358     bus_size_t size)							\
359 {									\
360 	int i;								\
361 	TYPE *dst;							\
362 	TYPE *rsrc = (TYPE *)src;					\
363 	int count = size >> SHIFT;					\
364 									\
365 	dst = (TYPE *)(h+o);						\
366 	for (i = 0; i < count; i++) {					\
367 		*dst = rsrc[i];						\
368 		__asm__("eieio");					\
369 	}								\
370 }
371 
372 BUS_SPACE_WRITE_RAW_MULTI_N(2,1,u_int16_t)
373 BUS_SPACE_WRITE_RAW_MULTI_N(4,2,u_int32_t)
374 
375 int
bus_space_subregion(bus_space_tag_t t,bus_space_handle_t bsh,bus_size_t offset,bus_size_t size,bus_space_handle_t * nbshp)376 bus_space_subregion(bus_space_tag_t t, bus_space_handle_t bsh,
377     bus_size_t offset, bus_size_t size, bus_space_handle_t *nbshp)
378 {
379 	*nbshp = bsh + offset;
380 	return (0);
381 }
382