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