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