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