root/arch/i386/pci/pci_addr_fixup.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. pci_addr_fixup
  2. pciaddr_resource_reserve
  3. pciaddr_resource_reserve_disabled
  4. pciaddr_resource_allocate
  5. pciaddr_resource_manage
  6. pciaddr_do_resource_allocate
  7. pciaddr_do_resource_reserve
  8. pciaddr_do_resource_reserve_disabled
  9. pciaddr_ioaddr
  10. pciaddr_print_devid
  11. pciaddr_device_is_agp
  12. pciaddr_search

    1 /*      $OpenBSD: pci_addr_fixup.c,v 1.21 2007/02/20 21:15:01 tom Exp $ */
    2 /*      $NetBSD: pci_addr_fixup.c,v 1.7 2000/08/03 20:10:45 nathanw Exp $       */
    3 
    4 /*-
    5  * Copyright (c) 2000 UCHIYAMA Yasushi.  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  * 3. The name of the author may not be used to endorse or promote products
   16  *    derived from this software without specific prior written permission.
   17  *
   18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
   19  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   20  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
   21  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
   22  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
   23  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
   24  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
   25  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
   26  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
   27  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   28  */
   29 
   30 #include <sys/param.h>
   31 #include <sys/systm.h>
   32 #include <sys/malloc.h>
   33 #include <sys/kernel.h>
   34 #include <sys/device.h>
   35 #include <sys/extent.h>
   36 
   37 #include <uvm/uvm_extern.h>
   38 
   39 #include <machine/bus.h>
   40 
   41 #include <dev/pci/pcireg.h>
   42 #include <dev/pci/pcivar.h>
   43 #include <dev/pci/pcidevs.h>
   44 
   45 #include <i386/pci/pcibiosvar.h>
   46 
   47 typedef int (*pciaddr_resource_manage_func_t)(struct pcibios_softc *, pci_chipset_tag_t, pcitag_t, int,
   48         struct extent *, int, bus_addr_t *, bus_size_t);
   49 void    pciaddr_resource_manage(struct pcibios_softc *,
   50     pci_chipset_tag_t, pcitag_t, pciaddr_resource_manage_func_t);
   51 void    pciaddr_resource_reserve(struct pcibios_softc *,
   52     pci_chipset_tag_t, pcitag_t);
   53 void    pciaddr_resource_reserve_disabled(struct pcibios_softc *,
   54     pci_chipset_tag_t, pcitag_t);
   55 int     pciaddr_do_resource_reserve(struct pcibios_softc *,
   56     pci_chipset_tag_t, pcitag_t, int, struct extent *, int,
   57     bus_addr_t *, bus_size_t);
   58 int     pciaddr_do_resource_reserve_disabled(struct pcibios_softc *,
   59     pci_chipset_tag_t, pcitag_t, int, struct extent *, int, u_long *,
   60     bus_size_t);
   61 void    pciaddr_resource_allocate(struct pcibios_softc *,
   62     pci_chipset_tag_t, pcitag_t);
   63 int     pciaddr_do_resource_allocate(struct pcibios_softc *,
   64     pci_chipset_tag_t, pcitag_t, int, struct extent *, int, bus_addr_t *,
   65     bus_size_t);
   66 bus_addr_t pciaddr_ioaddr(u_int32_t);
   67 void    pciaddr_print_devid(pci_chipset_tag_t, pcitag_t);
   68 
   69 int     pciaddr_device_is_agp(pci_chipset_tag_t, pcitag_t);
   70 
   71 #define PCIADDR_MEM_START       0x0
   72 #define PCIADDR_MEM_END         0xffffffff
   73 #define PCIADDR_PORT_START      0x0
   74 #define PCIADDR_PORT_END        0xffff
   75 
   76 /* for ISA devices */
   77 #define PCIADDR_ISAPORT_RESERVE 0x5800 /* empirical value */
   78 #define PCIADDR_ISAMEM_RESERVE  (16 * 1024 * 1024)
   79 
   80 void
   81 pci_addr_fixup(struct pcibios_softc *sc, pci_chipset_tag_t pc, int maxbus)
   82 {
   83         extern paddr_t avail_end;
   84         const char *verbose_header = 
   85                 "[%s]-----------------------\n"
   86                 "  device vendor product\n"
   87                 "  register space address    size\n"
   88                 "--------------------------------------------\n";
   89         const char *verbose_footer = 
   90                 "--------------------------[%3d devices bogus]\n";
   91 
   92         const struct {
   93                 bus_addr_t start;
   94                 bus_size_t size;
   95                 char *name;
   96         } system_reserve [] = {
   97                 { 0xfec00000, 0x100000, "I/O APIC" },
   98                 { 0xfee00000, 0x100000, "Local APIC" },
   99                 { 0xfffe0000, 0x20000, "BIOS PROM" },
  100                 { 0, 0, 0 }, /* terminator */
  101         }, *srp;
  102         paddr_t start;
  103         int error;
  104 
  105         sc->extent_mem = extent_create("PCI I/O memory space",
  106             PCIADDR_MEM_START, PCIADDR_MEM_END, M_DEVBUF, 0, 0, EX_NOWAIT);
  107         KASSERT(sc->extent_mem);
  108         sc->extent_port = extent_create("PCI I/O port space",
  109             PCIADDR_PORT_START, PCIADDR_PORT_END, M_DEVBUF, 0, 0, EX_NOWAIT);
  110         KASSERT(sc->extent_port);
  111 
  112         /* 
  113          * 1. check & reserve system BIOS setting.
  114          */
  115         PCIBIOS_PRINTV((verbose_header, "System BIOS Setting"));
  116         pci_device_foreach(sc, pc, maxbus, pciaddr_resource_reserve);
  117         pci_device_foreach(sc, pc, maxbus, pciaddr_resource_reserve_disabled);
  118         PCIBIOS_PRINTV((verbose_footer, sc->nbogus));
  119 
  120         /* 
  121          * 2. reserve non-PCI area.
  122          */
  123         for (srp = system_reserve; srp->size; srp++) {
  124                 error = extent_alloc_region(sc->extent_mem, srp->start,
  125                     srp->size, EX_NOWAIT| EX_MALLOCOK); 
  126                 if (error != 0)
  127                         printf("WARNING: can't reserve area for %s.\n",
  128                                srp->name);
  129         }
  130 
  131         /* 
  132          * 3. determine allocation space 
  133          */
  134         start = round_page(avail_end + 1);
  135         if (start < PCIADDR_ISAMEM_RESERVE)
  136                 start = PCIADDR_ISAMEM_RESERVE;
  137         sc->mem_alloc_start = (start + 0x100000 + 1) & ~(0x100000 - 1);
  138         sc->port_alloc_start = PCIADDR_ISAPORT_RESERVE;
  139         PCIBIOS_PRINTV((" Physical memory end: 0x%08x\n PCI memory mapped I/O "
  140             "space start: 0x%08x\n", avail_end, sc->mem_alloc_start));
  141 
  142         /* 
  143          * 4. do fixup 
  144          */
  145         PCIBIOS_PRINTV((verbose_header, "PCIBIOS fixup stage"));
  146         sc->nbogus = 0;
  147         pci_device_foreach(sc, pc, maxbus, pciaddr_resource_allocate);
  148         PCIBIOS_PRINTV((verbose_footer, sc->nbogus));
  149 
  150 }
  151 
  152 void
  153 pciaddr_resource_reserve(struct pcibios_softc *sc, pci_chipset_tag_t pc,
  154     pcitag_t tag)
  155 {
  156         if (pcibios_flags & PCIBIOS_VERBOSE)
  157                 pciaddr_print_devid(pc, tag);
  158         pciaddr_resource_manage(sc, pc, tag, pciaddr_do_resource_reserve);
  159 }
  160 
  161 void
  162 pciaddr_resource_reserve_disabled(struct pcibios_softc *sc,
  163     pci_chipset_tag_t pc, pcitag_t tag)
  164 {
  165         if (pcibios_flags & PCIBIOS_VERBOSE)
  166                 pciaddr_print_devid(pc, tag);
  167         pciaddr_resource_manage(sc, pc, tag,
  168             pciaddr_do_resource_reserve_disabled);
  169 }
  170 
  171 void
  172 pciaddr_resource_allocate(struct pcibios_softc *sc, pci_chipset_tag_t pc,
  173     pcitag_t tag)
  174 {
  175         if (pcibios_flags & PCIBIOS_VERBOSE)
  176                 pciaddr_print_devid(pc, tag);
  177         pciaddr_resource_manage(sc, pc, tag, pciaddr_do_resource_allocate);
  178 }
  179 
  180 void
  181 pciaddr_resource_manage(struct pcibios_softc *sc, pci_chipset_tag_t pc,
  182     pcitag_t tag, pciaddr_resource_manage_func_t func)
  183 {
  184         struct extent *ex;
  185         pcireg_t val, mask;
  186         bus_addr_t addr;
  187         bus_size_t size;
  188         int error, mapreg, type, reg_start, reg_end, width;
  189 
  190         val = pci_conf_read(pc, tag, PCI_BHLC_REG);
  191         switch (PCI_HDRTYPE_TYPE(val)) {
  192         default:
  193                 printf("WARNING: unknown PCI device header 0x%x.\n",
  194                     PCI_HDRTYPE_TYPE(val));
  195                 sc->nbogus++;
  196                 return;
  197         case 0: 
  198                 reg_start = PCI_MAPREG_START;
  199                 reg_end   = PCI_MAPREG_END;
  200                 break;
  201         case 1: /* PCI-PCI bridge */
  202                 reg_start = PCI_MAPREG_START;
  203                 reg_end   = PCI_MAPREG_PPB_END;
  204                 break;
  205         case 2: /* PCI-CardBus bridge */
  206                 reg_start = PCI_MAPREG_START;
  207                 reg_end   = PCI_MAPREG_PCB_END;
  208                 break;
  209         }
  210         error = 0;
  211     
  212         for (mapreg = reg_start; mapreg < reg_end; mapreg += width) {
  213                 /* inquire PCI device bus space requirement */
  214                 val = pci_conf_read(pc, tag, mapreg);
  215                 pci_conf_write(pc, tag, mapreg, ~0);
  216 
  217                 mask = pci_conf_read(pc, tag, mapreg);
  218                 pci_conf_write(pc, tag, mapreg, val);
  219         
  220                 type = PCI_MAPREG_TYPE(val);
  221                 width = 4;
  222                 if (type == PCI_MAPREG_TYPE_MEM) {
  223                         if (PCI_MAPREG_MEM_TYPE(val) == 
  224                             PCI_MAPREG_MEM_TYPE_64BIT) {
  225                                 /* XXX We could examine the upper 32 bits
  226                                  * XXX of the BAR here, but we are totally 
  227                                  * XXX unprepared to handle a non-zero value, 
  228                                  * XXX either here or anywhere else in 
  229                                  * XXX i386-land. 
  230                                  * XXX So just arrange to not look at the
  231                                  * XXX upper 32 bits, lest we misinterpret
  232                                  * XXX it as a 32-bit BAR set to zero. 
  233                                  */
  234                             width = 8;
  235                         }
  236                         addr = PCI_MAPREG_MEM_ADDR(val);
  237                         size = PCI_MAPREG_MEM_SIZE(mask);
  238                         ex = sc->extent_mem;
  239                 } else {
  240                         /* XXX some devices give 32bit value */
  241                         addr = PCI_MAPREG_IO_ADDR(val) & PCIADDR_PORT_END;
  242                         size = PCI_MAPREG_IO_SIZE(mask);
  243                         ex = sc->extent_port;
  244                 }
  245         
  246                 if (!size) /* unused register */
  247                         continue;
  248 
  249                 /* reservation/allocation phase */
  250                 error += (*func) (sc, pc, tag, mapreg, ex, type, &addr, size);
  251 
  252                 PCIBIOS_PRINTV(("\t%02xh %s 0x%08x 0x%08x\n", 
  253                                 mapreg, type ? "port" : "mem ", 
  254                                 (unsigned int)addr, (unsigned int)size));
  255         }
  256 
  257         if (error)
  258                 sc->nbogus++;
  259 
  260         PCIBIOS_PRINTV(("\t\t[%s]\n", error ? "NG" : "OK"));
  261 }
  262 
  263 int
  264 pciaddr_do_resource_allocate(struct pcibios_softc *sc, pci_chipset_tag_t pc,
  265     pcitag_t tag, int mapreg, struct extent *ex, int type, bus_addr_t *addr,
  266     bus_size_t size)
  267 {
  268         bus_addr_t start;
  269         int error;
  270         
  271         if (*addr) /* no need to allocate */
  272                 return (0);
  273 
  274         /* XXX Don't allocate if device is AGP device to avoid conflict. */
  275         if (pciaddr_device_is_agp(pc, tag)) 
  276                 return (0);
  277         
  278         start = (type == PCI_MAPREG_TYPE_MEM ? sc->mem_alloc_start
  279                 : sc->port_alloc_start);
  280         if (start < ex->ex_start || start + size - 1 >= ex->ex_end) {
  281                 PCIBIOS_PRINTV(("No available resources. fixup failed\n"));
  282                 return (1);
  283         }
  284         error = extent_alloc_subregion(ex, start, ex->ex_end, size, size, 0, 0,
  285             EX_FAST|EX_NOWAIT|EX_MALLOCOK, addr);
  286         if (error) {
  287                 PCIBIOS_PRINTV(("No available resources. fixup failed\n"));
  288                 return (1);
  289         }
  290 
  291         /* write new address to PCI device configuration header */
  292         pci_conf_write(pc, tag, mapreg, *addr);
  293         /* check */
  294         if (pcibios_flags & PCIBIOS_VERBOSE) {
  295                 printf("pci_addr_fixup: ");
  296                 pciaddr_print_devid(pc, tag);
  297         }
  298 
  299         if (pciaddr_ioaddr(pci_conf_read(pc, tag, mapreg)) != *addr) {
  300                 pci_conf_write(pc, tag, mapreg, 0); /* clear */
  301                 PCIBIOS_PRINTV(("fixup failed. (new address=%#x)\n", *addr));
  302                 return (1);
  303         }
  304         PCIBIOS_PRINTV(("new address 0x%08x\n", *addr));
  305 
  306         return (0);
  307 }
  308 
  309 int
  310 pciaddr_do_resource_reserve(struct pcibios_softc *sc, pci_chipset_tag_t pc,
  311     pcitag_t tag, int mapreg, struct extent *ex, int type, bus_addr_t *addr,
  312     bus_size_t size)
  313 {
  314         pcireg_t val;
  315         int error;
  316 
  317         if (*addr == 0)
  318                 return (0);
  319 
  320         val = pci_conf_read(pc, tag, PCI_COMMAND_STATUS_REG);
  321         if (type == PCI_MAPREG_TYPE_MEM &&
  322             (val & PCI_COMMAND_MEM_ENABLE) != PCI_COMMAND_MEM_ENABLE)
  323                 return (0);
  324         if (type == PCI_MAPREG_TYPE_IO &&
  325             (val & PCI_COMMAND_IO_ENABLE) != PCI_COMMAND_IO_ENABLE)
  326                 return (0);
  327 
  328         error = extent_alloc_region(ex, *addr, size, EX_NOWAIT | EX_MALLOCOK);
  329         if (error) {
  330                 PCIBIOS_PRINTV(("Resource conflict.\n"));
  331                 pci_conf_write(pc, tag, mapreg, 0); /* clear */
  332                 return (1);
  333         }
  334 
  335         return (0);
  336 }
  337 
  338 int
  339 pciaddr_do_resource_reserve_disabled(struct pcibios_softc *sc,
  340     pci_chipset_tag_t pc, pcitag_t tag, int mapreg, struct extent *ex, int type,
  341     u_long *addr, bus_size_t size)
  342 {
  343         pcireg_t val;
  344         int error;
  345 
  346         if (*addr == 0)
  347                 return (0);
  348 
  349         val = pci_conf_read(pc, tag, PCI_COMMAND_STATUS_REG);
  350         if (type == PCI_MAPREG_TYPE_MEM &&
  351             (val & PCI_COMMAND_MEM_ENABLE) == PCI_COMMAND_MEM_ENABLE)
  352                 return (0);
  353         if (type == PCI_MAPREG_TYPE_IO &&
  354             (val & PCI_COMMAND_IO_ENABLE) == PCI_COMMAND_IO_ENABLE)
  355                 return (0);
  356 
  357         PCIBIOS_PRINTV(("disabled %s space at addr 0x%x size 0x%x\n",
  358             type == PCI_MAPREG_TYPE_MEM ? "mem" : "io", *addr, size));
  359 
  360         error = extent_alloc_region(ex, *addr, size, EX_NOWAIT | EX_MALLOCOK);
  361         if (error) {
  362                 PCIBIOS_PRINTV(("Resource conflict.\n"));
  363                 pci_conf_write(pc, tag, mapreg, 0); /* clear */
  364                 return (1);
  365         }
  366 
  367         return (0);
  368 }
  369 
  370 bus_addr_t
  371 pciaddr_ioaddr(u_int32_t val)
  372 {
  373         return ((PCI_MAPREG_TYPE(val) == PCI_MAPREG_TYPE_MEM)
  374                 ? PCI_MAPREG_MEM_ADDR(val)
  375                 : (PCI_MAPREG_IO_ADDR(val) & PCIADDR_PORT_END));
  376 }
  377 
  378 void
  379 pciaddr_print_devid(pci_chipset_tag_t pc, pcitag_t tag)
  380 {
  381         int bus, device, function;      
  382         pcireg_t id;
  383         
  384         id = pci_conf_read(pc, tag, PCI_ID_REG);
  385         pci_decompose_tag(pc, tag, &bus, &device, &function);
  386         printf("%03d:%02d:%d %04x:%04x\n", bus, device, function, 
  387                PCI_VENDOR(id), PCI_PRODUCT(id));
  388 }
  389 
  390 int
  391 pciaddr_device_is_agp(pci_chipset_tag_t pc, pcitag_t tag)
  392 {
  393         pcireg_t class, status, rval;
  394         int off;
  395 
  396         /* Check AGP device. */
  397         class = pci_conf_read(pc, tag, PCI_CLASS_REG);
  398         if (PCI_CLASS(class) == PCI_CLASS_DISPLAY) {
  399                 status = pci_conf_read(pc, tag, PCI_COMMAND_STATUS_REG);
  400                 if (status & PCI_STATUS_CAPLIST_SUPPORT) {
  401                         rval = pci_conf_read(pc, tag, PCI_CAPLISTPTR_REG);
  402                         for (off = PCI_CAPLIST_PTR(rval);
  403                             off != 0;
  404                             off = PCI_CAPLIST_NEXT(rval) ) {
  405                                 rval = pci_conf_read(pc, tag, off);
  406                                 if (PCI_CAPLIST_CAP(rval) == PCI_CAP_AGP) 
  407                                         return (1);
  408                         }
  409                 }
  410         }
  411         return (0);
  412 }
  413 
  414 
  415 struct extent *
  416 pciaddr_search(int mem_port, bus_addr_t *startp, bus_size_t size)
  417 {
  418         extern struct cfdriver pcibios_cd;
  419         struct pcibios_softc *sc;
  420 
  421         sc = (struct pcibios_softc *)device_lookup(&pcibios_cd, 0);
  422         if (sc && !(pcibios_flags & PCIBIOS_ADDR_FIXUP)) {
  423                 struct extent_region *rp;
  424                 struct extent *ex = mem_port? sc->extent_mem : sc->extent_port;
  425 
  426                 /* Search the PCI I/O memory space extent for free
  427                  * space that will accommodate size.  Remember that the
  428                  * extent stores allocated space and we're searching
  429                  * for the gaps.
  430                  *
  431                  * If we're at the end or the gap between this region
  432                  * and the next region big enough, then we're done
  433                  */
  434                 for (rp = LIST_FIRST(&ex->ex_regions);
  435                     rp && *startp + size > rp->er_start;
  436                     rp = LIST_NEXT(rp, er_link)) {
  437                         bus_addr_t new_start;
  438 
  439                         new_start = (rp->er_end - 1 + size) & ~(size - 1);
  440                         if (new_start > *startp)
  441                                 *startp = new_start;
  442                 }
  443 
  444                 return (ex);
  445         }
  446 
  447         return (NULL);
  448 }

/* [<][>][^][v][top][bottom][index][help] */