1 /* $NetBSD: rbus.c,v 1.17 2003/05/17 08:23:14 scw 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.17 2003/05/17 08:23:14 scw 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 u_long 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 (u_long)addr, (u_long)size, (u_long)mask, (u_long)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, &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, &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, &result); 153 DPRINTF(("rbus: trying [%lx:%lx] %lx\n", 154 (u_long)search_addr, 155 (u_long)search_addr + size, (u_long)align)); 156 if (val == 0) { 157 break; 158 } 159 } 160 if (val != 0) { 161 /* no space found */ 162 DPRINTF(("rbus: no space found\n")); 163 return 1; 164 } 165 } 166 167 if(md_space_map(rbt->rb_bt, result, size, flags, bshp)) { 168 /* map failed */ 169 extent_free(rbt->rb_ext, result, size, exflags); 170 return 1; 171 } 172 173 if (addrp != NULL) { 174 *addrp = result + rbt->rb_offset; 175 } 176 return 0; 177 178 } else { 179 /* error!! */ 180 DPRINTF(("rbus: no rbus type\n")); 181 return 1; 182 } 183 return 1; 184 } 185 186 187 188 189 190 int 191 rbus_space_free(rbt, bsh, size, addrp) 192 rbus_tag_t rbt; 193 bus_space_handle_t bsh; 194 bus_size_t size; 195 bus_addr_t *addrp; 196 { 197 int exflags = EX_FAST | EX_NOWAIT; 198 bus_addr_t addr; 199 int status = 1; 200 201 if (rbt->rb_flags == RBUS_SPACE_ASK_PARENT) { 202 status = rbus_space_free(rbt->rb_parent, bsh, size, &addr); 203 } else if (rbt->rb_flags == RBUS_SPACE_SHARE || 204 rbt->rb_flags == RBUS_SPACE_DEDICATE) { 205 md_space_unmap(rbt->rb_bt, bsh, size, &addr); 206 207 extent_free(rbt->rb_ext, addr, size, exflags); 208 209 status = 0; 210 } else { 211 /* error. INVALID rbustag */ 212 status = 1; 213 } 214 if (addrp != NULL) { 215 *addrp = addr; 216 } 217 return status; 218 } 219 220 221 222 /* 223 * static rbus_tag_t 224 * rbus_new_body(bus_space_tag_t bt, rbus_tag_t parent, 225 * struct extent *ex, bus_addr_t start, bus_size_t end, 226 * bus_addr_t offset, int flags) 227 * 228 */ 229 static rbus_tag_t 230 rbus_new_body(bt, parent, ex, start, end, offset, flags) 231 bus_space_tag_t bt; 232 rbus_tag_t parent; 233 struct extent *ex; 234 bus_addr_t start, end, offset; 235 int flags; 236 { 237 rbus_tag_t rb; 238 239 /* sanity check */ 240 if (parent != NULL) { 241 if (start < parent->rb_start || end > parent->rb_end) { 242 /* 243 * out of range: [start, size] should be 244 * containd in parent space 245 */ 246 return 0; 247 /* Should I invoke panic? */ 248 } 249 } 250 251 if (NULL == (rb = (rbus_tag_t)malloc(sizeof(struct rbustag), M_DEVBUF, 252 M_NOWAIT))) { 253 panic("no memory for rbus instance"); 254 } 255 256 rb->rb_bt = bt; 257 rb->rb_parent = parent; 258 rb->rb_start = start; 259 rb->rb_end = end; 260 rb->rb_offset = offset; 261 rb->rb_flags = flags; 262 rb->rb_ext = ex; 263 264 DPRINTF(("rbus_new_body: [%lx, %lx] type %s name [%s]\n", 265 (u_long)start, (u_long)end, 266 flags == RBUS_SPACE_SHARE ? "share" : 267 flags == RBUS_SPACE_DEDICATE ? "dedicated" : 268 flags == RBUS_SPACE_ASK_PARENT ? "parent" : "invalid", 269 ex != NULL ? ex->ex_name : "noname")); 270 271 return rb; 272 } 273 274 275 276 /* 277 * rbus_tag_t rbus_new(rbus_tag_t parent, bus_addr_t start, bus_size_t 278 * size, bus_addr_t offset, int flags) 279 * 280 * This function makes a new child rbus instance. 281 */ 282 rbus_tag_t 283 rbus_new(parent, start, size, offset, flags) 284 rbus_tag_t parent; 285 bus_addr_t start; 286 bus_size_t size; 287 bus_addr_t offset; 288 int flags; 289 { 290 rbus_tag_t rb; 291 struct extent *ex = NULL; 292 bus_addr_t end = start + size; 293 294 if (flags == RBUS_SPACE_SHARE) { 295 ex = parent->rb_ext; 296 } else if (flags == RBUS_SPACE_DEDICATE) { 297 if (NULL == (ex = extent_create("rbus", start, end, M_DEVBUF, 298 NULL, 0, EX_NOCOALESCE|EX_NOWAIT))) { 299 return NULL; 300 } 301 } else if (flags == RBUS_SPACE_ASK_PARENT) { 302 ex = NULL; 303 } else { 304 /* Invalid flag */ 305 return 0; 306 } 307 308 rb = rbus_new_body(parent->rb_bt, parent, ex, start, start + size, 309 offset, flags); 310 311 if ((rb == NULL) && (flags == RBUS_SPACE_DEDICATE)) { 312 extent_destroy(ex); 313 } 314 315 return rb; 316 } 317 318 319 320 321 /* 322 * rbus_tag_t rbus_new_root_delegate(bus_space_tag, bus_addr_t, 323 * bus_size_t, bus_addr_t offset) 324 * 325 * This function makes a root rbus instance. 326 */ 327 rbus_tag_t 328 rbus_new_root_delegate(bt, start, size, offset) 329 bus_space_tag_t bt; 330 bus_addr_t start; 331 bus_size_t size; 332 bus_addr_t offset; 333 { 334 rbus_tag_t rb; 335 struct extent *ex; 336 337 if (NULL == (ex = extent_create("rbus root", start, start + size, 338 M_DEVBUF, NULL, 0, EX_NOCOALESCE|EX_NOWAIT))) { 339 return NULL; 340 } 341 342 rb = rbus_new_body(bt, NULL, ex, start, start + size, offset, 343 RBUS_SPACE_DEDICATE); 344 345 if (rb == NULL) { 346 extent_destroy(ex); 347 } 348 349 return rb; 350 } 351 352 353 354 /* 355 * rbus_tag_t rbus_new_root_share(bus_space_tag, struct extent *, 356 * bus_addr_t, bus_size_t, bus_addr_t offset) 357 * 358 * This function makes a root rbus instance. 359 */ 360 rbus_tag_t 361 rbus_new_root_share(bt, ex, start, size, offset) 362 bus_space_tag_t bt; 363 struct extent *ex; 364 bus_addr_t start; 365 bus_size_t size; 366 bus_addr_t offset; 367 { 368 /* sanity check */ 369 if (start < ex->ex_start || start + size > ex->ex_end) { 370 /* 371 * out of range: [start, size] should be containd in 372 * parent space 373 */ 374 return 0; 375 /* Should I invoke panic? */ 376 } 377 378 return rbus_new_body(bt, NULL, ex, start, start + size, offset, 379 RBUS_SPACE_SHARE); 380 } 381 382 383 384 385 386 /* 387 * int rbus_delete (rbus_tag_t rb) 388 * 389 * This function deletes the rbus structure pointed in the argument. 390 */ 391 int 392 rbus_delete(rb) 393 rbus_tag_t rb; 394 { 395 DPRINTF(("rbus_delete called [%s]\n", 396 rb->rb_ext != NULL ? rb->rb_ext->ex_name : "noname")); 397 if (rb->rb_flags == RBUS_SPACE_DEDICATE) { 398 extent_destroy(rb->rb_ext); 399 } 400 401 free(rb, M_DEVBUF); 402 403 return 0; 404 } 405