xref: /openbsd/sys/dev/cardbus/rbus.c (revision 898184e3)
1 /*	$OpenBSD: rbus.c,v 1.16 2010/09/22 02:28:37 jsg Exp $	*/
2 /*	$NetBSD: rbus.c,v 1.3 1999/11/06 06:20:53 soren Exp $	*/
3 /*
4  * Copyright (c) 1999
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/param.h>
29 #include <sys/systm.h>
30 #include <sys/device.h>
31 #include <sys/malloc.h>
32 #include <sys/extent.h>
33 
34 #include <machine/bus.h>
35 
36 #include <dev/cardbus/rbus.h>
37 
38 /* #define RBUS_DEBUG */
39 
40 #if defined RBUS_DEBUG
41 #define STATIC
42 #define DPRINTF(a) printf a
43 #else
44 #ifdef DDB
45 #define STATIC
46 #else
47 #define STATIC static
48 #endif
49 #define DPRINTF(a)
50 #endif
51 
52 
53 int
54 rbus_space_alloc(rbus_tag_t rbt, bus_addr_t addr, bus_size_t size,
55     bus_addr_t mask, bus_addr_t align, int flags, bus_addr_t *addrp,
56     bus_space_handle_t *bshp)
57 {
58 	return (rbus_space_alloc_subregion(rbt, rbt->rb_start, rbt->rb_end,
59 	    addr, size, mask, align, flags, addrp, bshp));
60 }
61 
62 int
63 rbus_space_alloc_subregion(rbus_tag_t rbt, bus_addr_t substart,
64     bus_addr_t subend, bus_addr_t addr, bus_size_t size,
65     bus_addr_t mask, bus_addr_t align, int flags, bus_addr_t *addrp,
66     bus_space_handle_t *bshp)
67 {
68 	bus_addr_t decodesize = mask + 1;
69 	bus_addr_t boundary, search_addr;
70 	int val;
71 	u_long result;
72 	int exflags = EX_FAST | EX_NOWAIT | EX_MALLOCOK;
73 
74 	DPRINTF(("rbus_space_alloc: addr %lx, size %lx, mask %lx, align %lx\n",
75 	    (u_long)addr, (u_long)size, (u_long)mask, (u_long)align));
76 
77 	if (mask == 0) {
78 		/* FULL Decode */
79 		decodesize = 0;
80 	}
81 
82 	if (rbt->rb_flags == RBUS_SPACE_SHARE ||
83 	    rbt->rb_flags == RBUS_SPACE_DEDICATE) {
84 		/* rbt has its own sh_extent */
85 
86 		/* sanity check: the subregion [substart, subend] should be
87 		   smaller than the region included in sh_extent */
88 		if (substart < rbt->rb_ext->ex_start ||
89 		    subend > rbt->rb_ext->ex_end) {
90 			DPRINTF(("rbus: out of range\n"));
91 			return (1);
92 		}
93 
94 		if (decodesize == align) {
95 			if (extent_alloc_subregion(rbt->rb_ext, substart,
96 			    subend, size, align, 0, 0, exflags, &result))
97 				return (1);
98 		} else if (decodesize == 0) {
99 			/* maybe, the register is overflowed. */
100 
101 			if (extent_alloc_subregion(rbt->rb_ext, addr,
102 			    addr + size, size, 1, 0, 0, exflags, &result))
103 				return (1);
104 		} else {
105 			boundary = decodesize > align ? decodesize : align;
106 
107 			search_addr = (substart & ~(boundary - 1)) + addr;
108 
109 			if (search_addr < substart)
110 				search_addr += boundary;
111 
112 			val = 1;
113 			for (; search_addr + size <= subend;
114 			    search_addr += boundary) {
115 				val = extent_alloc_subregion(
116 				    rbt->rb_ext,search_addr,
117 				    search_addr + size, size, align, 0, 0,
118 				    exflags, &result);
119 				DPRINTF(("rbus: trying [%lx:%lx] %lx\n",
120 				    (u_long)search_addr,
121 				    (u_long)search_addr + size,
122 				    (u_long)align));
123 				if (val == 0)
124 					break;
125 			}
126 
127 			if (val != 0) {
128 				/* no space found */
129 				DPRINTF(("rbus: no space found\n"));
130 				return (1);
131 			}
132 		}
133 
134 		if (md_space_map(rbt, result, size, flags, bshp)) {
135 			/* map failed */
136 			extent_free(rbt->rb_ext, result, size, exflags);
137 			return (1);
138 		}
139 
140 		if (addrp != NULL)
141 			*addrp = result;
142 		return (0);
143 	} else {
144 		/* error!! */
145 		DPRINTF(("rbus: no rbus type\n"));
146 		return (1);
147 	}
148 }
149 
150 int
151 rbus_space_free(rbus_tag_t rbt, bus_space_handle_t bsh, bus_size_t size,
152     bus_addr_t *addrp)
153 {
154 	int exflags = EX_FAST | EX_NOWAIT;
155 	bus_addr_t addr;
156 	int status = 1;
157 
158 	if (rbt->rb_flags == RBUS_SPACE_SHARE ||
159 	    rbt->rb_flags == RBUS_SPACE_DEDICATE) {
160 		md_space_unmap(rbt, bsh, size, &addr);
161 
162 		extent_free(rbt->rb_ext, addr, size, exflags);
163 
164 		status = 0;
165 	} else {
166 		/* error. INVALID rbustag */
167 		status = 1;
168 	}
169 
170 	if (addrp != NULL)
171 		*addrp = addr;
172 
173 	return (status);
174 }
175 
176 /*
177  * rbus_tag_t
178  * rbus_new_body(bus_space_tag_t bt,
179  *               struct extent *ex, bus_addr_t start, bus_size_t end,
180  *               int flags)
181  *
182  */
183 rbus_tag_t
184 rbus_new_body(bus_space_tag_t bt, struct extent *ex,
185     bus_addr_t start, bus_addr_t end, int flags)
186 {
187 	rbus_tag_t rb;
188 
189 	if ((rb = (rbus_tag_t)malloc(sizeof(struct rbustag), M_DEVBUF,
190 	    M_NOWAIT)) == NULL) {
191 		panic("no memory for rbus instance");
192 	}
193 
194 	rb->rb_bt = bt;
195 	rb->rb_start = start;
196 	rb->rb_end = end;
197 	rb->rb_flags = flags;
198 	rb->rb_ext = ex;
199 
200 	DPRINTF(("rbus_new_body: [%lx, %lx] type %s name [%s]\n",
201 	    (u_long)start, (u_long)end,
202 	   flags == RBUS_SPACE_SHARE ? "share" :
203 	   flags == RBUS_SPACE_DEDICATE ? "dedicated" : "invalid",
204 	   ex != NULL ? ex->ex_name : "noname"));
205 
206 	return (rb);
207 }
208 
209 /*
210  * rbus_tag_t rbus_new_root_delegate(bus_space_tag, bus_addr_t,
211  *                                   bus_size_t)
212  *
213  *  This function makes a root rbus instance.
214  */
215 rbus_tag_t
216 rbus_new_root_delegate(bus_space_tag_t bt, bus_addr_t start, bus_size_t size)
217 {
218 	rbus_tag_t rb;
219 	struct extent *ex;
220 
221 	if ((ex = extent_create("rbus root", start, start + size, M_DEVBUF,
222 	    NULL, 0, EX_NOCOALESCE|EX_NOWAIT)) == NULL)
223 		return (NULL);
224 
225 	rb = rbus_new_body(bt, ex, start, start + size,
226 	    RBUS_SPACE_DEDICATE);
227 
228 	if (rb == NULL)
229 		extent_destroy(ex);
230 
231 	return (rb);
232 }
233 
234 /*
235  * rbus_tag_t rbus_new_root_share(bus_space_tag, struct extent *,
236  *                                 bus_addr_t, bus_size_t)
237  *
238  *  This function makes a root rbus instance.
239  */
240 rbus_tag_t
241 rbus_new_root_share(bus_space_tag_t bt, struct extent *ex, bus_addr_t start,
242     bus_size_t size)
243 {
244 	/* sanity check */
245 	if (start < ex->ex_start || start + size > ex->ex_end) {
246 		/* out of range: [start, size] should be contained in
247 		 * parent space
248 		 */
249 		return (0);
250 		/* Should I invoke panic? */
251 	}
252 
253 	return (rbus_new_body(bt, ex, start, start + size,
254 	    RBUS_SPACE_SHARE));
255 }
256