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