Home | History | Annotate | Download | only in bus
      1 #include <stdint.h>
      2 #include <string.h>
      3 #include <stdlib.h>
      4 #include <stdio.h>
      5 #include <errno.h>
      6 #include <gpxe/io.h>
      7 #include <gpxe/isa.h>
      8 
      9 FILE_LICENCE ( GPL2_OR_LATER );
     10 
     11 /*
     12  * isa.c implements a "classical" port-scanning method of ISA device
     13  * detection.  The driver must provide a list of probe addresses
     14  * (probe_addrs), together with a function (probe_addr) that can be
     15  * used to test for the physical presence of a device at any given
     16  * address.
     17  *
     18  * Note that this should probably be considered the "last resort" for
     19  * device probing.  If the card supports ISAPnP or EISA, use that
     20  * instead.  Some cards (e.g. the 3c509) implement a proprietary
     21  * ISAPnP-like mechanism.
     22  *
     23  * The ISA probe address list can be overridden by config.h; if the
     24  * user specifies ISA_PROBE_ADDRS then that list will be used first.
     25  * (If ISA_PROBE_ONLY is defined, the driver's own list will never be
     26  * used).
     27  */
     28 
     29 /*
     30  * User-supplied probe address list
     31  *
     32  */
     33 static isa_probe_addr_t isa_extra_probe_addrs[] = {
     34 #ifdef ISA_PROBE_ADDRS
     35 	ISA_PROBE_ADDRS
     36 #endif
     37 };
     38 #define ISA_EXTRA_PROBE_ADDR_COUNT \
     39      ( sizeof ( isa_extra_probe_addrs ) / sizeof ( isa_extra_probe_addrs[0] ) )
     40 
     41 #define ISA_IOIDX_MIN( driver ) ( -ISA_EXTRA_PROBE_ADDR_COUNT )
     42 #ifdef ISA_PROBE_ONLY
     43 #define ISA_IOIDX_MAX( driver ) ( -1 )
     44 #else
     45 #define ISA_IOIDX_MAX( driver ) ( (int) (driver)->addr_count - 1 )
     46 #endif
     47 
     48 #define ISA_IOADDR( driver, ioidx )					  \
     49 	( ( (ioidx) < 0 ) ?						  \
     50 	  isa_extra_probe_addrs[ (ioidx) + ISA_EXTRA_PROBE_ADDR_COUNT ] : \
     51 	  (driver)->probe_addrs[(ioidx)] )
     52 
     53 static void isabus_remove ( struct root_device *rootdev );
     54 
     55 /**
     56  * Probe an ISA device
     57  *
     58  * @v isa		ISA device
     59  * @ret rc		Return status code
     60  */
     61 static int isa_probe ( struct isa_device *isa ) {
     62 	int rc;
     63 
     64 	DBG ( "Trying ISA driver %s at I/O %04x\n",
     65 	      isa->driver->name, isa->ioaddr );
     66 
     67 	if ( ( rc = isa->driver->probe ( isa ) ) != 0 ) {
     68 		DBG ( "...probe failed\n" );
     69 		return rc;
     70 	}
     71 
     72 	DBG ( "...device found\n" );
     73 	return 0;
     74 }
     75 
     76 /**
     77  * Remove an ISA device
     78  *
     79  * @v isa		ISA device
     80  */
     81 static void isa_remove ( struct isa_device *isa ) {
     82 	isa->driver->remove ( isa );
     83 	DBG ( "Removed ISA%04x\n", isa->ioaddr );
     84 }
     85 
     86 /**
     87  * Probe ISA root bus
     88  *
     89  * @v rootdev		ISA bus root device
     90  *
     91  * Scans the ISA bus for devices and registers all devices it can
     92  * find.
     93  */
     94 static int isabus_probe ( struct root_device *rootdev ) {
     95 	struct isa_device *isa = NULL;
     96 	struct isa_driver *driver;
     97 	int ioidx;
     98 	int rc;
     99 
    100 	for_each_table_entry ( driver, ISA_DRIVERS ) {
    101 		for ( ioidx = ISA_IOIDX_MIN ( driver ) ;
    102 		      ioidx <= ISA_IOIDX_MAX ( driver ) ; ioidx++ ) {
    103 			/* Allocate struct isa_device */
    104 			if ( ! isa )
    105 				isa = malloc ( sizeof ( *isa ) );
    106 			if ( ! isa ) {
    107 				rc = -ENOMEM;
    108 				goto err;
    109 			}
    110 			memset ( isa, 0, sizeof ( *isa ) );
    111 			isa->driver = driver;
    112 			isa->ioaddr = ISA_IOADDR ( driver, ioidx );
    113 
    114 			/* Add to device hierarchy */
    115 			snprintf ( isa->dev.name, sizeof ( isa->dev.name ),
    116 				   "ISA%04x", isa->ioaddr );
    117 			isa->dev.desc.bus_type = BUS_TYPE_ISA;
    118 			isa->dev.desc.vendor = driver->vendor_id;
    119 			isa->dev.desc.device = driver->prod_id;
    120 			isa->dev.parent = &rootdev->dev;
    121 			list_add ( &isa->dev.siblings,
    122 				   &rootdev->dev.children );
    123 			INIT_LIST_HEAD ( &isa->dev.children );
    124 
    125 			/* Try probing at this I/O address */
    126 			if ( isa_probe ( isa ) == 0 ) {
    127 				/* isadev registered, we can drop our ref */
    128 				isa = NULL;
    129 			} else {
    130 				/* Not registered; re-use struct */
    131 				list_del ( &isa->dev.siblings );
    132 			}
    133 		}
    134 	}
    135 
    136 	free ( isa );
    137 	return 0;
    138 
    139  err:
    140 	free ( isa );
    141 	isabus_remove ( rootdev );
    142 	return rc;
    143 }
    144 
    145 /**
    146  * Remove ISA root bus
    147  *
    148  * @v rootdev		ISA bus root device
    149  */
    150 static void isabus_remove ( struct root_device *rootdev ) {
    151 	struct isa_device *isa;
    152 	struct isa_device *tmp;
    153 
    154 	list_for_each_entry_safe ( isa, tmp, &rootdev->dev.children,
    155 				   dev.siblings ) {
    156 		isa_remove ( isa );
    157 		list_del ( &isa->dev.siblings );
    158 		free ( isa );
    159 	}
    160 }
    161 
    162 /** ISA bus root device driver */
    163 static struct root_driver isa_root_driver = {
    164 	.probe = isabus_probe,
    165 	.remove = isabus_remove,
    166 };
    167 
    168 /** ISA bus root device */
    169 struct root_device isa_root_device __root_device = {
    170 	.dev = { .name = "ISA" },
    171 	.driver = &isa_root_driver,
    172 };
    173