xref: /netbsd/sys/dev/cardbus/rbus.c (revision 6550d01e)
1 /*	$NetBSD: rbus.c,v 1.28 2009/12/15 22:17:12 snj Exp $	*/
2 
3 /*
4  * Copyright (c) 1999 and 2000
5  *     HAYAKAWA Koichi.  All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27 
28 #include <sys/cdefs.h>
29 __KERNEL_RCSID(0, "$NetBSD: rbus.c,v 1.28 2009/12/15 22:17:12 snj Exp $");
30 
31 #include <sys/param.h>
32 #include <sys/systm.h>
33 #include <sys/device.h>
34 #include <sys/malloc.h>
35 #include <sys/extent.h>
36 
37 #include <sys/bus.h>
38 
39 #include <dev/cardbus/rbus.h>
40 
41 /* #define RBUS_DEBUG */
42 
43 #if defined RBUS_DEBUG
44 #define STATIC
45 #define DPRINTF(a) printf a
46 #else
47 #define STATIC static
48 #define DPRINTF(a)
49 #endif
50 
51 
52 
53 static rbus_tag_t rbus_new_body(bus_space_tag_t bt, rbus_tag_t parent,
54 				    struct extent *ex, bus_addr_t start,
55 				    bus_addr_t end, bus_addr_t offset,
56 				    int flags);
57 
58 
59 int
60 rbus_space_alloc(rbus_tag_t rbt, bus_addr_t addr, bus_size_t size, bus_addr_t mask, bus_addr_t align, int flags, bus_addr_t *addrp, bus_space_handle_t *bshp)
61 {
62 	return rbus_space_alloc_subregion(rbt, rbt->rb_start, rbt->rb_end,
63 	    addr, size, mask, align, flags, addrp, bshp);
64 }
65 
66 
67 
68 
69 int
70 rbus_space_alloc_subregion(rbus_tag_t rbt, bus_addr_t substart, bus_addr_t subend, bus_addr_t addr, bus_size_t size, bus_addr_t mask, bus_addr_t align, int flags, bus_addr_t *addrp, bus_space_handle_t *bshp)
71 {
72 	bus_addr_t decodesize = mask + 1;
73 	bus_addr_t boundary, search_addr;
74 	int val;
75 	u_long result;
76 	int exflags = EX_FAST | EX_NOWAIT | EX_MALLOCOK;
77 
78 	DPRINTF(("rbus_space_alloc: addr %lx, size %lx, mask %lx, align %lx\n",
79 	    (u_long)addr, (u_long)size, (u_long)mask, (u_long)align));
80 
81 	addr += rbt->rb_offset;
82 
83 	if (mask == 0) {
84 		/* FULL Decode */
85 		decodesize = 0;
86 	}
87 
88 	if (rbt->rb_flags == RBUS_SPACE_ASK_PARENT) {
89 		return rbus_space_alloc(rbt->rb_parent, addr, size, mask,
90 		    align, flags, addrp, bshp);
91 	} else if (rbt->rb_flags == RBUS_SPACE_SHARE ||
92 	    rbt->rb_flags == RBUS_SPACE_DEDICATE) {
93 		/* rbt has its own sh_extent */
94 
95 		/*
96 		 * sanity check: the subregion [substart, subend] should be
97 		 * smaller than the region included in sh_extent.
98 		 */
99 		if (substart < rbt->rb_ext->ex_start
100 		    || subend > rbt->rb_ext->ex_end) {
101 			DPRINTF(("rbus: out of range\n"));
102 			return 1;
103 		}
104 
105 		if (decodesize == align) {
106 			if(extent_alloc_subregion(rbt->rb_ext, substart,
107 			    subend, size, align, 0, exflags, &result)) {
108 				return 1;
109 			}
110 		} else if (decodesize == 0) {
111 			/* maybe, the register is overflowed. */
112 
113 			if (extent_alloc_subregion(rbt->rb_ext, addr,
114 			    addr + size, size, 1, 0, exflags, &result)) {
115 				return 1;
116 			}
117 		} else {
118 
119 			boundary = decodesize > align ? decodesize : align;
120 
121 			search_addr = (substart & ~(boundary - 1)) + addr;
122 
123 			if (search_addr < substart) {
124 				search_addr += boundary;
125 			}
126 
127 			val = 1;
128 			for (; search_addr + size <= subend;
129 			     search_addr += boundary) {
130 				val = extent_alloc_subregion(rbt->rb_ext,
131 				    search_addr, search_addr + size, size,
132 				    align, 0, exflags, &result);
133 				DPRINTF(("rbus: trying [%lx:%lx] %lx\n",
134 				    (u_long)search_addr,
135 				    (u_long)search_addr + size, (u_long)align));
136 				if (val == 0) {
137 					break;
138 				}
139 			}
140 			if (val != 0) {
141 				/* no space found */
142 				DPRINTF(("rbus: no space found\n"));
143 				return 1;
144 			}
145 		}
146 
147 		if(md_space_map(rbt->rb_bt, result, size, flags, bshp)) {
148 			/* map failed */
149 			extent_free(rbt->rb_ext, result, size, exflags);
150 			return 1;
151 		}
152 
153 		if (addrp != NULL) {
154 			*addrp = result + rbt->rb_offset;
155 		}
156 		return 0;
157 	} else {
158 		/* error!! */
159 		DPRINTF(("rbus: no rbus type\n"));
160 		return 1;
161 	}
162 }
163 
164 
165 
166 
167 
168 int
169 rbus_space_free(rbus_tag_t rbt, bus_space_handle_t bsh, bus_size_t size, bus_addr_t *addrp)
170 {
171 	int exflags = EX_FAST | EX_NOWAIT;
172 	bus_addr_t addr;
173 	int status = 1;
174 
175 	if (rbt->rb_flags == RBUS_SPACE_ASK_PARENT) {
176 		status = rbus_space_free(rbt->rb_parent, bsh, size, &addr);
177 	} else if (rbt->rb_flags == RBUS_SPACE_SHARE ||
178 	    rbt->rb_flags == RBUS_SPACE_DEDICATE) {
179 		md_space_unmap(rbt->rb_bt, bsh, size, &addr);
180 
181 		extent_free(rbt->rb_ext, addr, size, exflags);
182 
183 		status = 0;
184 	} else {
185 		/* error. INVALID rbustag */
186 		status = 1;
187 	}
188 	if (status == 0 && addrp != NULL) {
189 		*addrp = addr;
190 	}
191 	return status;
192 }
193 
194 
195 
196 /*
197  * static rbus_tag_t
198  * rbus_new_body(bus_space_tag_t bt, rbus_tag_t parent,
199  *               struct extent *ex, bus_addr_t start, bus_size_t end,
200  *               bus_addr_t offset, int flags)
201  *
202  */
203 static rbus_tag_t
204 rbus_new_body(bus_space_tag_t bt, rbus_tag_t parent, struct extent *ex, bus_addr_t start, bus_addr_t end, bus_addr_t offset, int flags)
205 {
206 	rbus_tag_t rb;
207 
208 	/* sanity check */
209 	if (parent != NULL) {
210 		if (start < parent->rb_start || end > parent->rb_end) {
211 			/*
212 			 * out of range: [start, size] should be
213 			 * contained in parent space
214 			 */
215 			return 0;
216 			/* Should I invoke panic? */
217 		}
218 	}
219 
220 	if (NULL == (rb = (rbus_tag_t)malloc(sizeof(struct rbustag), M_DEVBUF,
221 	    M_NOWAIT))) {
222 		panic("no memory for rbus instance");
223 	}
224 
225 	rb->rb_bt = bt;
226 	rb->rb_parent = parent;
227 	rb->rb_start = start;
228 	rb->rb_end = end;
229 	rb->rb_offset = offset;
230 	rb->rb_flags = flags;
231 	rb->rb_ext = ex;
232 
233 	DPRINTF(("rbus_new_body: [%lx, %lx] type %s name [%s]\n",
234 	    (u_long)start, (u_long)end,
235 	    flags == RBUS_SPACE_SHARE ? "share" :
236 	    flags == RBUS_SPACE_DEDICATE ? "dedicated" :
237 	    flags == RBUS_SPACE_ASK_PARENT ? "parent" : "invalid",
238 	    ex != NULL ? ex->ex_name : "noname"));
239 
240 	return rb;
241 }
242 
243 
244 
245 /*
246  * rbus_tag_t rbus_new(rbus_tag_t parent, bus_addr_t start, bus_size_t
247  *                     size, bus_addr_t offset, int flags)
248  *
249  *  This function makes a new child rbus instance.
250  */
251 rbus_tag_t
252 rbus_new(rbus_tag_t parent, bus_addr_t start, bus_size_t size, bus_addr_t offset, int flags)
253 {
254 	rbus_tag_t rb;
255 	struct extent *ex = NULL;
256 	bus_addr_t end = start + size;
257 
258 	if (flags == RBUS_SPACE_SHARE) {
259 		ex = parent->rb_ext;
260 	} else if (flags == RBUS_SPACE_DEDICATE) {
261 		if (NULL == (ex = extent_create("rbus", start, end, M_DEVBUF,
262 		    NULL, 0, EX_NOCOALESCE|EX_NOWAIT))) {
263 			return NULL;
264 		}
265 	} else if (flags == RBUS_SPACE_ASK_PARENT) {
266 		ex = NULL;
267 	} else {
268 		/* Invalid flag */
269 		return 0;
270 	}
271 
272 	rb = rbus_new_body(parent->rb_bt, parent, ex, start, start + size,
273 	    offset, flags);
274 
275 	if ((rb == NULL) && (flags == RBUS_SPACE_DEDICATE)) {
276 		extent_destroy(ex);
277 	}
278 
279 	return rb;
280 }
281 
282 
283 
284 
285 /*
286  * rbus_tag_t rbus_new_root_delegate(bus_space_tag, bus_addr_t,
287  *                                   bus_size_t, bus_addr_t offset)
288  *
289  *  This function makes a root rbus instance.
290  */
291 rbus_tag_t
292 rbus_new_root_delegate(bus_space_tag_t bt, bus_addr_t start, bus_size_t size, bus_addr_t offset)
293 {
294 	rbus_tag_t rb;
295 	struct extent *ex;
296 
297 	if (NULL == (ex = extent_create("rbus root", start, start + size,
298 	    M_DEVBUF, NULL, 0, EX_NOCOALESCE|EX_NOWAIT))) {
299 		return NULL;
300 	}
301 
302 	rb = rbus_new_body(bt, NULL, ex, start, start + size, offset,
303 	    RBUS_SPACE_DEDICATE);
304 
305 	if (rb == NULL) {
306 		extent_destroy(ex);
307 	}
308 
309 	return rb;
310 }
311 
312 
313 
314 /*
315  * rbus_tag_t rbus_new_root_share(bus_space_tag, struct extent *,
316  *                                 bus_addr_t, bus_size_t, bus_addr_t offset)
317  *
318  *  This function makes a root rbus instance.
319  */
320 rbus_tag_t
321 rbus_new_root_share(bus_space_tag_t bt, struct extent *ex, bus_addr_t start, bus_size_t size, bus_addr_t offset)
322 {
323 	/* sanity check */
324 	if (start < ex->ex_start || start + size > ex->ex_end) {
325 		/*
326 		 * out of range: [start, size] should be contained in
327 		 * parent space
328 		 */
329 		return 0;
330 		/* Should I invoke panic? */
331 	}
332 
333 	return rbus_new_body(bt, NULL, ex, start, start + size, offset,
334 	    RBUS_SPACE_SHARE);
335 }
336 
337 
338 
339 
340 
341 /*
342  * int rbus_delete (rbus_tag_t rb)
343  *
344  *   This function deletes the rbus structure pointed in the argument.
345  */
346 int
347 rbus_delete(rbus_tag_t rb)
348 {
349 	DPRINTF(("rbus_delete called [%s]\n",
350 	    rb->rb_ext != NULL ? rb->rb_ext->ex_name : "noname"));
351 	if (rb->rb_flags == RBUS_SPACE_DEDICATE) {
352 		extent_destroy(rb->rb_ext);
353 	}
354 
355 	free(rb, M_DEVBUF);
356 
357 	return 0;
358 }
359