The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/i386/isa/pnp.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /*
    2  * Copyright (c) 1996, Sujal M. Patel
    3  * All rights reserved.
    4  *
    5  * Redistribution and use in source and binary forms, with or without
    6  * modification, are permitted provided that the following conditions
    7  * are met:
    8  * 1. Redistributions of source code must retain the above copyright
    9  *    notice, this list of conditions and the following disclaimer.
   10  * 2. Redistributions in binary form must reproduce the above copyright
   11  *    notice, this list of conditions and the following disclaimer in the
   12  *    documentation and/or other materials provided with the distribution.
   13  *
   14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
   15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
   18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   24  * SUCH DAMAGE.
   25  *
   26  * $FreeBSD: src/sys/i386/isa/pnp.c,v 1.4.2.3 1999/09/05 08:13:17 peter Exp $
   27  */
   28 
   29 #include <sys/param.h>
   30 #include <sys/systm.h>
   31 #include <sys/kernel.h> /* for DATA_SET */
   32 #include <machine/clock.h>
   33 #include <machine/cpufunc.h>
   34 
   35 #include <i386/isa/pnp.h>
   36 #include <i386/isa/isa_device.h>
   37 #ifdef PC98
   38 #include <pc98/pc98/pc98.h>
   39 #else
   40 #include <i386/isa/isa.h>
   41 #endif
   42 #include <i386/isa/icu.h>
   43 
   44 int num_pnp_cards = 0;
   45 pnp_id pnp_devices[MAX_PNP_CARDS];
   46 /*
   47  * these entries are initialized using the autoconfig menu
   48  * The struct is invalid (and must be initialized) if the first
   49  * CSN is zero. The init code fills invalid entries with CSN 255
   50  * which is not a supported value.
   51  */
   52 
   53 struct pnp_cinfo pnp_ldn_overrides[MAX_PNP_LDN] = { { 0 } };
   54 /*
   55  * the following is a flag which tells if the data is valid.
   56  */
   57 
   58 static int doing_pnp_probe = 0 ;
   59 static int current_csn ;
   60 static int current_pnp_id ;
   61 static int current_pnp_serial ;
   62 
   63 /*
   64  * the following block is an example on what is needed for
   65  * a PnP device driver.
   66  */
   67 static  char*   nullpnp_probe(u_long csn, u_long vendor_id);
   68 static  void    nullpnp_attach(u_long csn, u_long vendor_id, char *name,
   69     struct isa_device *dev);
   70 static  u_long  nullpnp_count = 0 ;
   71 
   72 static struct pnp_device nullpnp_device = {
   73     "goodpnp",
   74     nullpnp_probe,
   75     nullpnp_attach,
   76     &nullpnp_count,
   77     NULL /* imask */
   78 };
   79 
   80 DATA_SET (pnpdevice_set, nullpnp_device);
   81 
   82 static char*
   83 nullpnp_probe(u_long tag, u_long type)
   84 {
   85     return NULL ;
   86 }
   87 
   88 static void
   89 nullpnp_attach(u_long csn, u_long vend_id, char *name,
   90         struct isa_device *dev)
   91 {
   92     return;
   93 }
   94 
   95 /* The READ_DATA port that we are using currently */
   96 static int pnp_rd_port;
   97 
   98 void pnp_send_Initiation_LFSR (void);
   99 int pnp_get_serial (pnp_id *p);
  100 void config_pnp_device (pnp_id *p, int csn);
  101 int pnp_isolation_protocol (void);
  102 void pnp_write(int d, u_char r);
  103 u_char pnp_read(int d);
  104 
  105 void
  106 pnp_write(int d, u_char r)
  107 {
  108     outb (_PNP_ADDRESS, d);
  109     outb (_PNP_WRITE_DATA, r);
  110 }
  111 
  112 u_char
  113 pnp_read(int d)
  114 {
  115     outb (_PNP_ADDRESS, d);
  116     return (inb(3 | (pnp_rd_port <<2)));
  117 }
  118 
  119 /*
  120  * Send Initiation LFSR as described in "Plug and Play ISA Specification",
  121  * Intel May 94.
  122  */
  123 void
  124 pnp_send_Initiation_LFSR()
  125 {
  126     int cur, i;
  127 
  128     /* Reset the LSFR */
  129     outb(_PNP_ADDRESS, 0);
  130     outb(_PNP_ADDRESS, 0); /* yes, we do need it twice! */
  131 
  132     cur = 0x6a;
  133     outb(_PNP_ADDRESS, cur);
  134 
  135     for (i = 1; i < 32; i++) {
  136         cur = (cur >> 1) | (((cur ^ (cur >> 1)) << 7) & 0xff);
  137         outb(_PNP_ADDRESS, cur);
  138     }
  139 }
  140 
  141 
  142 /*
  143  * Get the device's serial number.  Returns 1 if the serial is valid.
  144  */
  145 int
  146 pnp_get_serial(pnp_id *p)
  147 {
  148     int i, bit, valid = 0, sum = 0x6a;
  149     u_char *data = (u_char *)p;
  150 
  151     bzero(data, sizeof(char) * 9);
  152     outb(_PNP_ADDRESS, SERIAL_ISOLATION);
  153     for (i = 0; i < 72; i++) {
  154         bit = inb((pnp_rd_port << 2) | 0x3) == 0x55;
  155         DELAY(250);     /* Delay 250 usec */
  156 
  157         /* Can't Short Circuit the next evaluation, so 'and' is last */
  158         bit = (inb((pnp_rd_port << 2) | 0x3) == 0xaa) && bit;
  159         DELAY(250);     /* Delay 250 usec */
  160 
  161         valid = valid || bit;
  162 
  163         if (i < 64)
  164             sum = (sum >> 1) |
  165                 (((sum ^ (sum >> 1) ^ bit) << 7) & 0xff);
  166 
  167         data[i / 8] = (data[i / 8] >> 1) | (bit ? 0x80 : 0);
  168     }
  169 
  170     valid = valid && (data[8] == sum);
  171 
  172     return valid;
  173 }
  174 
  175 /*
  176  * read_pnp_parms loads pnp parameters from the currently selected
  177  * device into the struct pnp_cinfo parameter passed.
  178  * The second argument specifies the Logical Device to use.
  179  */
  180 int
  181 read_pnp_parms(struct pnp_cinfo *d, int ldn)
  182 {
  183     int i ;
  184 
  185     if (doing_pnp_probe == 0 || d == NULL)
  186         return 0 ;      /* fail */
  187 
  188     bzero(d, sizeof(struct pnp_cinfo));
  189     d->vendor_id = current_pnp_id ;
  190     d->serial = current_pnp_serial ;
  191 
  192     d->csn = current_csn ;
  193     d->ldn = ldn ;      /* XXX this should be different ... */
  194     pnp_write (SET_LDN, ldn );
  195     i = pnp_read(SET_LDN) ;
  196     if (i != ldn) {
  197         printf("Warning: LDN %d does not exist\n", ldn);
  198     }
  199     for (i = 0; i < 8; i++) {
  200         d->port[i] = pnp_read(IO_CONFIG_BASE + i * 2) << 8 ;
  201         d->port[i] |= pnp_read(IO_CONFIG_BASE + i * 2 + 1);
  202 
  203         if (i < 4) {
  204             d->mem[i].base = pnp_read (MEM_CONFIG + i*8) << 16 ;
  205             d->mem[i].base |= pnp_read (MEM_CONFIG + i*8 + 1) << 8 ;
  206             d->mem[i].control = pnp_read (MEM_CONFIG + i*8 + 2) ;
  207             d->mem[i].range = pnp_read (MEM_CONFIG + i*8 + 3) << 16 ;
  208             d->mem[i].range |= pnp_read (MEM_CONFIG + i*8 + 4) << 8 ;
  209         }
  210         if (i < 2) {
  211             d->irq[i] = pnp_read(IRQ_CONFIG + i * 2);
  212             d->irq_type[i] = pnp_read(IRQ_CONFIG + 1 + i * 2);
  213             d->drq[i] = pnp_read(DRQ_CONFIG + i);
  214         }
  215     }
  216     d->enable = pnp_read(ACTIVATE);
  217     for (i = 0 ; i < MAX_PNP_LDN; i++) {
  218         if (pnp_ldn_overrides[i].csn == d->csn &&
  219                 pnp_ldn_overrides[i].ldn == ldn) {
  220             d->flags = pnp_ldn_overrides[i].flags ;
  221             d->override = pnp_ldn_overrides[i].override ;
  222             break ;
  223         }
  224     }
  225     if (bootverbose)
  226         printf("pnp%d:%d port 0x%04x 0x%04x 0x%04x 0x%04x irq %d:%d drq %d:%d en %d fl 0x%x\n",
  227         d->csn, d->ldn,
  228         d->port[0], d->port[1], d->port[2], d->port[3],
  229         d->irq[0], d->irq[1],
  230         d->drq[0], d->drq[1],
  231         d->enable, d->flags);
  232     return 1 ; /* success */
  233 }
  234 
  235 /*
  236  * write_pnp_parms initializes a logical device with the parms
  237  * in d, and then activates the board if the last parameter is 1.
  238  */
  239 
  240 int
  241 write_pnp_parms(struct pnp_cinfo *d, int ldn)
  242 {
  243     int i, empty = -1 ;
  244 
  245     /*
  246      * some safety checks first.
  247      */
  248     if (doing_pnp_probe == 0 || d==NULL || d->vendor_id != current_pnp_id)
  249         return 0 ;      /* fail */
  250 
  251     pnp_write (SET_LDN, ldn );
  252     i = pnp_read(SET_LDN) ;
  253     if (i != ldn) {
  254         printf("Warning: LDN %d does not exist\n", ldn);
  255     }
  256     for (i = 0; i < 8; i++) {
  257         pnp_write(IO_CONFIG_BASE + i * 2, d->port[i] >> 8 );
  258         pnp_write(IO_CONFIG_BASE + i * 2 + 1, d->port[i] & 0xff );
  259     }
  260     for (i = 0; i < 4; i++) {
  261         pnp_write(MEM_CONFIG + i*8, (d->mem[i].base >> 16) & 0xff );
  262         pnp_write(MEM_CONFIG + i*8+1, (d->mem[i].base >> 8) & 0xff );
  263         pnp_write(MEM_CONFIG + i*8+2, d->mem[i].control & 0xff );
  264         pnp_write(MEM_CONFIG + i*8+3, (d->mem[i].range >> 16) & 0xff );
  265         pnp_write(MEM_CONFIG + i*8+4, (d->mem[i].range >> 8) & 0xff );
  266     }
  267     for (i = 0; i < 2; i++) {
  268         pnp_write(IRQ_CONFIG + i*2    , d->irq[i] );
  269         pnp_write(IRQ_CONFIG + i*2 + 1, d->irq_type[i] );
  270         pnp_write(DRQ_CONFIG + i, d->drq[i] );
  271     }
  272     /*
  273      * store parameters read into the current kernel
  274      * so manual editing next time is easier
  275      */
  276     for (i = 0 ; i < MAX_PNP_LDN; i++) {
  277         if (pnp_ldn_overrides[i].csn == d->csn &&
  278                 pnp_ldn_overrides[i].ldn == ldn) {
  279             d->flags = pnp_ldn_overrides[i].flags ;
  280             pnp_ldn_overrides[i] = *d ;
  281             break ;
  282         } else if (pnp_ldn_overrides[i].csn < 1 ||
  283                 pnp_ldn_overrides[i].csn == 255)
  284             empty = i ;
  285     }
  286     if (i== MAX_PNP_LDN && empty != -1)
  287         pnp_ldn_overrides[empty] = *d;
  288 
  289     /*
  290      * Here should really perform the range check, and
  291      * return a failure if not successful.
  292      */
  293     pnp_write (IO_RANGE_CHECK, 0);
  294     DELAY(1000); /* XXX is it really necessary ? */
  295     pnp_write (ACTIVATE, d->enable ? 1 : 0);
  296     DELAY(1000); /* XXX is it really necessary ? */
  297     return 1 ;
  298 }
  299 
  300 /*
  301  * To finalize a card's initialization, and before accessing its
  302  * registers, we need to bring the card in WaitForKey. To this purpose,
  303  * we need to issue a WaitForKey command, which brings _all_ cards
  304  * in that state. So, before configuring the next board, we must also
  305  * sent the Init-Key to bring cards to the SLEEP state again.
  306  *
  307  * In fact, one could hope that cards respond to normal I/O accesses
  308  * even in the SLEEP state, which could be done by issuing a WAKE[0].
  309  * This seems to work on the CS4236, but not on the CS4232 on my Zappa
  310  * motherboard .
  311  */
  312 int
  313 enable_pnp_card()
  314 {
  315     /* the next wake should bring the card in WaitForKey ? */
  316     pnp_write (WAKE, 0);
  317     pnp_write(CONFIG_CONTROL, 0x02);    /* All cards in WaitForKey */
  318     DELAY(1000); /* XXX is it really necessary ? */
  319     return 1 ; /* success */
  320 }
  321 
  322 /*
  323  * Configure PnP devices. pnp_id is made of:
  324  *      4 bytes: board id (which can be printed as an ascii string);
  325  *      4 bytes: board serial number (often 0 or -1 ?)
  326  */
  327 
  328 void
  329 config_pnp_device(pnp_id *p, int csn)
  330 {
  331     struct pnp_cinfo *ci;
  332     int i;
  333     u_char *data = (u_char *)p;
  334 
  335     /* these are for autoconfigure a-la pci */
  336     struct pnp_device *dvp, **dvpp;
  337     char *name ;
  338 
  339     printf("CSN %d Vendor ID: %c%c%c%02x%02x [0x%08x] Serial 0x%08x\n",
  340             csn,
  341             ((data[0] & 0x7c) >> 2) + '@',
  342             (((data[0] & 0x03) << 3) | ((data[1] & 0xe0) >> 5)) + '@',
  343             (data[1] & 0x1f) + '@', data[2], data[3],
  344             p->vendor_id, p->serial);
  345 
  346     doing_pnp_probe = 1 ;
  347     current_csn = csn ;
  348     current_pnp_id = p->vendor_id ;
  349     current_pnp_serial = p->serial ;
  350 
  351     /*
  352      * use kernel table to override possible devices
  353      */
  354     for (i = 0 ; i < MAX_PNP_LDN; i++) {
  355         if (pnp_ldn_overrides[i].csn == csn &&
  356                 pnp_ldn_overrides[i].override == 1) {
  357             struct pnp_cinfo d;
  358             printf("PnP: override config for CSN %d LDN %d vend_id 0x%08x\n",
  359                 csn, pnp_ldn_overrides[i].ldn, current_pnp_id);
  360             /* next assignement is done otherwise read fails */
  361             d.vendor_id = current_pnp_id ;
  362             read_pnp_parms(&d, pnp_ldn_overrides[i].ldn);
  363             if (pnp_ldn_overrides[i].enable == 0) {
  364                 /* just disable ... */
  365                 d.enable = 0;
  366                 write_pnp_parms(&d, pnp_ldn_overrides[i].ldn);
  367             } else {
  368                 /* set all parameters */
  369                 /* next assignement is done otherwise write fails */
  370                 pnp_ldn_overrides[i].vendor_id = current_pnp_id ;
  371                 write_pnp_parms(&pnp_ldn_overrides[i],
  372                     pnp_ldn_overrides[i].ldn);
  373             }
  374         }
  375     }
  376 
  377     /* lookup device in ioconfiguration */
  378     dvpp = (struct pnp_device **)pnpdevice_set.ls_items;
  379     while ( (dvp = *dvpp++) ) {
  380         if (dvp->pd_probe) {
  381             if ( (name = (*dvp->pd_probe)(csn, p->vendor_id)) )
  382                 break;
  383         }
  384     }
  385     if (dvp && name && dvp->pd_count) { /* found a matching device */
  386         int unit ;
  387 
  388         /* pnpcb->pnpcb_seen |= ( 1ul << csn ) ; */
  389 
  390         /* get and increment the unit */
  391         unit = (*dvp->pd_count)++;
  392 
  393         /*
  394          * now call the attach routine. The board has not been
  395          * configured yet, so better not access isa registers in
  396          * the attach routine until enable_pnp_card() has been done.
  397          */
  398         
  399         bzero( &(dvp->dev), sizeof(dvp->dev) );
  400         dvp->dev.id_unit = unit ;
  401         if (dvp->pd_attach)
  402             (*dvp->pd_attach) (csn, p->vendor_id, name, &(dvp->dev));
  403         if (dvp->dev.id_irq) {
  404             /* the board uses interrupts. Register it. */
  405             if (dvp->imask)
  406                 INTRMASK( *(dvp->imask), dvp->dev.id_irq );
  407             register_intr(ffs(dvp->dev.id_irq)-1, dvp->dev.id_id,
  408                 dvp->dev.id_ri_flags, dvp->dev.id_intr,
  409                 dvp->imask, dvp->dev.id_unit);
  410             INTREN(dvp->dev.id_irq);
  411         }
  412         printf("%s%d (%s <%s> sn 0x%08x) at 0x%x "
  413             "irq %d drq %d flags 0x%x id %d\n",
  414                 dvp->dev.id_driver && dvp->dev.id_driver->name ?
  415                     dvp->dev.id_driver->name : "unknown",
  416                 unit,
  417                 dvp->pd_name, name, p->serial,
  418                 dvp->dev.id_iobase,
  419                 ffs(dvp->dev.id_irq)-1,
  420                 dvp->dev.id_drq,
  421                 dvp->dev.id_flags,
  422                 dvp->dev.id_id);
  423     }
  424 
  425     doing_pnp_probe = 0 ;
  426 }
  427 
  428 
  429 /*
  430  * Run the isolation protocol. Use pnp_rd_port as the READ_DATA port
  431  * value (caller should try multiple READ_DATA locations before giving
  432  * up). Upon exiting, all cards are aware that they should use
  433  * pnp_rd_port as the READ_DATA port.
  434  *
  435  * In the first pass, a csn is assigned to each board and pnp_id's
  436  * are saved to an array, pnp_devices. In the second pass, each
  437  * card is woken up and the device configuration is called.
  438  */
  439 int
  440 pnp_isolation_protocol()
  441 {
  442     int csn;
  443 
  444     pnp_send_Initiation_LFSR();
  445 
  446     pnp_write(CONFIG_CONTROL, 0x04);    /* Reset CSN for All Cards */
  447 
  448     for (csn = 1; (csn < MAX_PNP_CARDS); csn++) {
  449         /* Wake up cards without a CSN */
  450         pnp_write(WAKE, 0);
  451         pnp_write(SET_RD_DATA, pnp_rd_port);
  452         outb(_PNP_ADDRESS, SERIAL_ISOLATION);
  453         DELAY(1000);    /* Delay 1 msec */
  454 
  455         if (pnp_get_serial( &(pnp_devices[csn-1]) ) ) {
  456             pnp_write(SET_CSN, csn);
  457             /* pnp_write(CONFIG_CONTROL, 2); */
  458         } else
  459             break;
  460     }
  461     num_pnp_cards = csn - 1;
  462     for (csn = 1; csn <= num_pnp_cards ; csn++) {
  463         /*
  464          * make sure cards are in SLEEP state
  465          */
  466         pnp_send_Initiation_LFSR();
  467         pnp_write(WAKE, csn);
  468         config_pnp_device( &(pnp_devices[csn-1]), csn);
  469         /*
  470          * Put all cards in WaitForKey, just in case the previous
  471          * attach routine forgot it.
  472          */
  473         pnp_write(CONFIG_CONTROL, 0x02);
  474         DELAY(1000); /* XXX is it really necessary ? */
  475     }
  476     return num_pnp_cards ;
  477 }
  478 
  479 
  480 /*
  481  * pnp_configure()
  482  *
  483  * autoconfiguration of pnp devices. This routine just runs the
  484  * isolation protocol over several ports, until one is successful.
  485  *
  486  * may be called more than once ?
  487  *
  488  */
  489 
  490 void
  491 pnp_configure()
  492 {
  493     int num_pnp_devs;
  494 
  495     if (pnp_ldn_overrides[0].csn == 0) {
  496         if (bootverbose)
  497             printf("Initializing PnP override table\n");
  498         bzero (pnp_ldn_overrides, sizeof(pnp_ldn_overrides));
  499         pnp_ldn_overrides[0].csn = 255 ;
  500     }
  501     printf("Probing for PnP devices:\n");
  502 
  503     /* Try various READ_DATA ports from 0x203-0x3ff */
  504     for (pnp_rd_port = 0x80; (pnp_rd_port < 0xff); pnp_rd_port += 0x10) {
  505         if (bootverbose)
  506             printf("Trying Read_Port at %x\n", (pnp_rd_port << 2) | 0x3);
  507 
  508         num_pnp_devs = pnp_isolation_protocol();
  509         if (num_pnp_devs)
  510             break;
  511     }
  512     if (!num_pnp_devs) {
  513         printf("No Plug-n-Play devices were found\n");
  514         return;
  515     }
  516 }

Cache object: 5d59c9a1c29ff789a366b7ac48b408b8


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.