1/**************************************************************************
2*
3*    isapnp.c -- Etherboot isapnp support for the 3Com 3c515
4*    Written 2002-2003 by Timothy Legge <tlegge@rogers.com>
5*
6*    This program is free software; you can redistribute it and/or modify
7*    it under the terms of the GNU General Public License as published by
8*    the Free Software Foundation; either version 2 of the License, or
9*    (at your option) any later version.
10*
11*    This program is distributed in the hope that it will be useful,
12*    but WITHOUT ANY WARRANTY; without even the implied warranty of
13*    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14*    GNU General Public License for more details.
15*
16*    You should have received a copy of the GNU General Public License
17*    along with this program; if not, write to the Free Software
18*    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19*
20*    Portions of this code:
21*	Copyright (C) 2001  P.J.H.Fox (fox@roestock.demon.co.uk)
22*
23*
24*    REVISION HISTORY:
25*    ================
26*    Version 0.1 April 26, 2002 TJL
27*    Version 0.2 01/08/2003	TJL Moved outside the 3c515.c driver file
28*    Version 0.3 Sept 23, 2003	timlegge Change delay to currticks
29*
30*
31*    Generalised into an ISAPnP bus that can be used by more than just
32*    the 3c515 by Michael Brown <mbrown@fensystems.co.uk>
33*
34***************************************************************************/
35
36/** @file
37 *
38 * ISAPnP bus support
39 *
40 * Etherboot orignally gained ISAPnP support in a very limited way for
41 * the 3c515 NIC.  The current implementation is almost a complete
42 * rewrite based on the ISAPnP specification, with passing reference
43 * to the Linux ISAPnP code.
44 *
45 * There can be only one ISAPnP bus in a system.  Once the read port
46 * is known and all cards have been allocated CSNs, there's nothing to
47 * be gained by re-scanning for cards.
48 *
49 * External code (e.g. the ISAPnP ROM prefix) may already know the
50 * read port address, in which case it can store it in
51 * #isapnp_read_port.  Note that setting the read port address in this
52 * way will prevent further isolation from taking place; you should
53 * set the read port address only if you know that devices have
54 * already been allocated CSNs.
55 *
56 */
57
58FILE_LICENCE ( GPL2_OR_LATER );
59
60#include <stdint.h>
61#include <stdlib.h>
62#include <string.h>
63#include <stdio.h>
64#include <errno.h>
65#include <gpxe/io.h>
66#include <unistd.h>
67#include <gpxe/isapnp.h>
68
69/**
70 * ISAPnP Read Port address.
71 *
72 * ROM prefix may be able to set this address, which is why this is
73 * non-static.
74 */
75uint16_t isapnp_read_port;
76
77static void isapnpbus_remove ( struct root_device *rootdev );
78
79/*
80 * ISAPnP utility functions
81 *
82 */
83
84#define ISAPNP_CARD_ID_FMT "ID %04x:%04x (\"%s\") serial %x"
85#define ISAPNP_CARD_ID_DATA(identifier)					  \
86	(identifier)->vendor_id, (identifier)->prod_id,			  \
87	isa_id_string ( (identifier)->vendor_id, (identifier)->prod_id ), \
88	(identifier)->serial
89#define ISAPNP_DEV_ID_FMT "ID %04x:%04x (\"%s\")"
90#define ISAPNP_DEV_ID_DATA(isapnp)					  \
91	(isapnp)->vendor_id, (isapnp)->prod_id,				  \
92	isa_id_string ( (isapnp)->vendor_id, (isapnp)->prod_id )
93
94static inline void isapnp_write_address ( unsigned int address ) {
95	outb ( address, ISAPNP_ADDRESS );
96}
97
98static inline void isapnp_write_data ( unsigned int data ) {
99	outb ( data, ISAPNP_WRITE_DATA );
100}
101
102static inline unsigned int isapnp_read_data ( void ) {
103	return inb ( isapnp_read_port );
104}
105
106static inline void isapnp_write_byte ( unsigned int address,
107				       unsigned int value ) {
108	isapnp_write_address ( address );
109	isapnp_write_data ( value );
110}
111
112static inline unsigned int isapnp_read_byte ( unsigned int address ) {
113	isapnp_write_address ( address );
114	return isapnp_read_data ();
115}
116
117static inline unsigned int isapnp_read_word ( unsigned int address ) {
118	/* Yes, they're in big-endian order */
119	return ( ( isapnp_read_byte ( address ) << 8 )
120		 | isapnp_read_byte ( address + 1 ) );
121}
122
123/** Inform cards of a new read port address */
124static inline void isapnp_set_read_port ( void ) {
125	isapnp_write_byte ( ISAPNP_READPORT, ( isapnp_read_port >> 2 ) );
126}
127
128/**
129 * Enter the Isolation state.
130 *
131 * Only cards currently in the Sleep state will respond to this
132 * command.
133 */
134static inline void isapnp_serialisolation ( void ) {
135	isapnp_write_address ( ISAPNP_SERIALISOLATION );
136}
137
138/**
139 * Enter the Wait for Key state.
140 *
141 * All cards will respond to this command, regardless of their current
142 * state.
143 */
144static inline void isapnp_wait_for_key ( void ) {
145	isapnp_write_byte ( ISAPNP_CONFIGCONTROL, ISAPNP_CONFIG_WAIT_FOR_KEY );
146}
147
148/**
149 * Reset (i.e. remove) Card Select Number.
150 *
151 * Only cards currently in the Sleep state will respond to this
152 * command.
153 */
154static inline void isapnp_reset_csn ( void ) {
155	isapnp_write_byte ( ISAPNP_CONFIGCONTROL, ISAPNP_CONFIG_RESET_CSN );
156}
157
158/**
159 * Place a specified card into the Config state.
160 *
161 * @v csn		Card Select Number
162 * @ret None		-
163 * @err None		-
164 *
165 * Only cards currently in the Sleep, Isolation, or Config states will
166 * respond to this command.  The card that has the specified CSN will
167 * enter the Config state, all other cards will enter the Sleep state.
168 */
169static inline void isapnp_wake ( uint8_t csn ) {
170	isapnp_write_byte ( ISAPNP_WAKE, csn );
171}
172
173static inline unsigned int isapnp_read_resourcedata ( void ) {
174	return isapnp_read_byte ( ISAPNP_RESOURCEDATA );
175}
176
177static inline unsigned int isapnp_read_status ( void ) {
178	return isapnp_read_byte ( ISAPNP_STATUS );
179}
180
181/**
182 * Assign a Card Select Number to a card, and enter the Config state.
183 *
184 * @v csn		Card Select Number
185 *
186 * Only cards in the Isolation state will respond to this command.
187 * The isolation protocol is designed so that only one card will
188 * remain in the Isolation state by the time the isolation protocol
189 * completes.
190 */
191static inline void isapnp_write_csn ( unsigned int csn ) {
192	isapnp_write_byte ( ISAPNP_CARDSELECTNUMBER, csn );
193}
194
195static inline void isapnp_logicaldevice ( unsigned int logdev ) {
196	isapnp_write_byte ( ISAPNP_LOGICALDEVICENUMBER, logdev );
197}
198
199static inline void isapnp_activate ( unsigned int logdev ) {
200	isapnp_logicaldevice ( logdev );
201	isapnp_write_byte ( ISAPNP_ACTIVATE, 1 );
202}
203
204static inline void isapnp_deactivate ( unsigned int logdev ) {
205	isapnp_logicaldevice ( logdev );
206	isapnp_write_byte ( ISAPNP_ACTIVATE, 0 );
207}
208
209static inline unsigned int isapnp_read_iobase ( unsigned int index ) {
210	return isapnp_read_word ( ISAPNP_IOBASE ( index ) );
211}
212
213static inline unsigned int isapnp_read_irqno ( unsigned int index ) {
214	return isapnp_read_byte ( ISAPNP_IRQNO ( index ) );
215}
216
217static void isapnp_delay ( void ) {
218	udelay ( 1000 );
219}
220
221/**
222 * Linear feedback shift register.
223 *
224 * @v lfsr		Current value of the LFSR
225 * @v input_bit		Current input bit to the LFSR
226 * @ret lfsr		Next value of the LFSR
227 *
228 * This routine implements the linear feedback shift register as
229 * described in Appendix B of the PnP ISA spec.  The hardware
230 * implementation uses eight D-type latches and two XOR gates.  I
231 * think this is probably the smallest possible implementation in
232 * software.  Six instructions when input_bit is a constant 0 (for
233 * isapnp_send_key).  :)
234 */
235static inline unsigned int isapnp_lfsr_next ( unsigned int lfsr,
236					      unsigned int input_bit ) {
237	register uint8_t lfsr_next;
238
239	lfsr_next = lfsr >> 1;
240	lfsr_next |= ( ( ( lfsr ^ lfsr_next ) ^ input_bit ) ) << 7;
241	return lfsr_next;
242}
243
244/**
245 * Send the ISAPnP initiation key.
246 *
247 * Sending the key causes all ISAPnP cards that are currently in the
248 * Wait for Key state to transition into the Sleep state.
249 */
250static void isapnp_send_key ( void ) {
251	unsigned int i;
252	unsigned int lfsr;
253
254	isapnp_delay();
255	isapnp_write_address ( 0x00 );
256	isapnp_write_address ( 0x00 );
257
258	lfsr = ISAPNP_LFSR_SEED;
259	for ( i = 0 ; i < 32 ; i++ ) {
260		isapnp_write_address ( lfsr );
261		lfsr = isapnp_lfsr_next ( lfsr, 0 );
262	}
263}
264
265/**
266 * Compute ISAPnP identifier checksum
267 *
268 * @v identifier	ISAPnP identifier
269 * @ret checksum	Expected checksum value
270 */
271static unsigned int isapnp_checksum ( struct isapnp_identifier *identifier ) {
272	unsigned int i, j;
273	unsigned int lfsr;
274	unsigned int byte;
275
276	lfsr = ISAPNP_LFSR_SEED;
277	for ( i = 0 ; i < 8 ; i++ ) {
278		byte = * ( ( ( uint8_t * ) identifier ) + i );
279		for ( j = 0 ; j < 8 ; j++ ) {
280			lfsr = isapnp_lfsr_next ( lfsr, byte );
281			byte >>= 1;
282		}
283	}
284	return lfsr;
285}
286
287/*
288 * Read a byte of resource data from the current location
289 *
290 * @ret byte		Byte of resource data
291 */
292static inline unsigned int isapnp_peek_byte ( void ) {
293	unsigned int i;
294
295	/* Wait for data to be ready */
296	for ( i = 0 ; i < 20 ; i++ ) {
297		if ( isapnp_read_status() & 0x01 ) {
298			/* Byte ready - read it */
299			return isapnp_read_resourcedata();
300		}
301		isapnp_delay();
302	}
303	/* Data never became ready - return 0xff */
304	return 0xff;
305}
306
307/**
308 * Read resource data.
309 *
310 * @v buf		Buffer in which to store data, or NULL
311 * @v bytes		Number of bytes to read
312 *
313 * Resource data is read from the current location.  If #buf is NULL,
314 * the data is discarded.
315 */
316static void isapnp_peek ( void *buf, size_t len ) {
317	unsigned int i;
318	unsigned int byte;
319
320	for ( i = 0 ; i < len ; i++) {
321		byte = isapnp_peek_byte();
322		if ( buf )
323			* ( ( uint8_t * ) buf + i ) = byte;
324	}
325}
326
327/**
328 * Find a tag within the resource data.
329 *
330 * @v wanted_tag	The tag that we're looking for
331 * @v buf		Buffer in which to store the tag's contents
332 * @v len		Length of buffer
333 * @ret rc		Return status code
334 *
335 * Scan through the resource data until we find a particular tag, and
336 * read its contents into a buffer.
337 */
338static int isapnp_find_tag ( unsigned int wanted_tag, void *buf, size_t len ) {
339	unsigned int tag;
340	unsigned int tag_len;
341
342	DBG2 ( "ISAPnP read tag" );
343	do {
344		tag = isapnp_peek_byte();
345		if ( ISAPNP_IS_SMALL_TAG ( tag ) ) {
346			tag_len = ISAPNP_SMALL_TAG_LEN ( tag );
347			tag = ISAPNP_SMALL_TAG_NAME ( tag );
348		} else {
349			tag_len = ( isapnp_peek_byte() +
350				    ( isapnp_peek_byte() << 8 ) );
351			tag = ISAPNP_LARGE_TAG_NAME ( tag );
352		}
353		DBG2 ( " %02x (%02x)", tag, tag_len );
354		if ( tag == wanted_tag ) {
355			if ( len > tag_len )
356				len = tag_len;
357			isapnp_peek ( buf, len );
358			DBG2 ( "\n" );
359			return 0;
360		} else {
361			isapnp_peek ( NULL, tag_len );
362		}
363	} while ( tag != ISAPNP_TAG_END );
364	DBG2 ( "\n" );
365	return -ENOENT;
366}
367
368/**
369 * Find specified Logical Device ID tag
370 *
371 * @v logdev		Logical device ID
372 * @v logdevid		Logical device ID structure to fill in
373 * @ret rc		Return status code
374 */
375static int isapnp_find_logdevid ( unsigned int logdev,
376				  struct isapnp_logdevid *logdevid ) {
377	unsigned int i;
378	int rc;
379
380	for ( i = 0 ; i <= logdev ; i++ ) {
381		if ( ( rc = isapnp_find_tag ( ISAPNP_TAG_LOGDEVID, logdevid,
382					      sizeof ( *logdevid ) ) ) != 0 )
383			return rc;
384	}
385	return 0;
386}
387
388/**
389 * Try isolating ISAPnP cards at the current read port.
390 *
391 * @ret \>0		Number of ISAPnP cards found
392 * @ret 0		There are no ISAPnP cards in the system
393 * @ret \<0		A conflict was detected; try a new read port
394 * @err None		-
395 *
396 * The state diagram on page 18 (PDF page 24) of the PnP ISA spec
397 * gives the best overview of what happens here.
398 */
399static int isapnp_try_isolate ( void ) {
400	struct isapnp_identifier identifier;
401	unsigned int i, j;
402	unsigned int seen_55aa, seen_life;
403	unsigned int csn = 0;
404	unsigned int data;
405	unsigned int byte;
406
407	DBG ( "ISAPnP attempting isolation at read port %04x\n",
408	      isapnp_read_port );
409
410	/* Place all cards into the Sleep state, whatever state
411	 * they're currently in.
412	 */
413	isapnp_wait_for_key();
414	isapnp_send_key();
415
416	/* Reset all assigned CSNs */
417	isapnp_reset_csn();
418	isapnp_delay();
419	isapnp_delay();
420
421	/* Place all cards into the Isolation state */
422	isapnp_wait_for_key ();
423	isapnp_send_key();
424	isapnp_wake ( 0x00 );
425
426	/* Set the read port */
427	isapnp_set_read_port();
428	isapnp_delay();
429
430	while ( 1 ) {
431
432		/* All cards that do not have assigned CSNs are
433		 * currently in the Isolation state, each time we go
434		 * through this loop.
435		 */
436
437		/* Initiate serial isolation */
438		isapnp_serialisolation();
439		isapnp_delay();
440
441		/* Read identifier serially via the ISAPnP read port. */
442		memset ( &identifier, 0, sizeof ( identifier ) );
443		seen_55aa = seen_life = 0;
444		for ( i = 0 ; i < 9 ; i++ ) {
445			byte = 0;
446			for ( j = 0 ; j < 8 ; j++ ) {
447				data = isapnp_read_data();
448				isapnp_delay();
449				data = ( ( data << 8 ) | isapnp_read_data() );
450				isapnp_delay();
451				byte >>= 1;
452				if (  data != 0xffff ) {
453					seen_life++;
454					if ( data == 0x55aa ) {
455						byte |= 0x80;
456						seen_55aa++;
457					}
458				}
459			}
460			*( ( ( uint8_t * ) &identifier ) + i ) = byte;
461		}
462
463		/* If we didn't see any 55aa patterns, stop here */
464		if ( ! seen_55aa ) {
465			if ( csn ) {
466				DBG ( "ISAPnP found no more cards\n" );
467			} else {
468				if ( seen_life ) {
469					DBG ( "ISAPnP saw life but no cards, "
470					      "trying new read port\n" );
471					csn = -1;
472				} else {
473					DBG ( "ISAPnP saw no signs of life, "
474					      "abandoning isolation\n" );
475				}
476			}
477			break;
478		}
479
480		/* If the checksum was invalid stop here */
481		if ( identifier.checksum != isapnp_checksum ( &identifier) ) {
482			DBG ( "ISAPnP found malformed card "
483			      ISAPNP_CARD_ID_FMT "\n  with checksum %02x "
484			      "(should be %02x), trying new read port\n",
485			      ISAPNP_CARD_ID_DATA ( &identifier ),
486			      identifier.checksum,
487			      isapnp_checksum ( &identifier) );
488			csn = -1;
489			break;
490		}
491
492		/* Give the device a CSN */
493		csn++;
494		DBG ( "ISAPnP found card " ISAPNP_CARD_ID_FMT
495		      ", assigning CSN %02x\n",
496		      ISAPNP_CARD_ID_DATA ( &identifier ), csn );
497
498		isapnp_write_csn ( csn );
499		isapnp_delay();
500
501		/* Send this card back to Sleep and force all cards
502		 * without a CSN into Isolation state
503		 */
504		isapnp_wake ( 0x00 );
505		isapnp_delay();
506	}
507
508	/* Place all cards in Wait for Key state */
509	isapnp_wait_for_key();
510
511	/* Return number of cards found */
512	if ( csn > 0 ) {
513		DBG ( "ISAPnP found %d cards at read port %04x\n",
514		      csn, isapnp_read_port );
515	}
516	return csn;
517}
518
519/**
520 * Find a valid read port and isolate all ISAPnP cards.
521 *
522 */
523static void isapnp_isolate ( void ) {
524	for ( isapnp_read_port = ISAPNP_READ_PORT_START ;
525	      isapnp_read_port <= ISAPNP_READ_PORT_MAX ;
526	      isapnp_read_port += ISAPNP_READ_PORT_STEP ) {
527		/* Avoid problematic locations such as the NE2000
528		 * probe space
529		 */
530		if ( ( isapnp_read_port >= 0x280 ) &&
531		     ( isapnp_read_port <= 0x380 ) )
532			continue;
533
534		/* If we detect any ISAPnP cards at this location, stop */
535		if ( isapnp_try_isolate() >= 0 )
536			return;
537	}
538}
539
540/**
541 * Activate or deactivate an ISAPnP device.
542 *
543 * @v isapnp		ISAPnP device
544 * @v activation	True to enable, False to disable the device
545 * @ret None		-
546 * @err None		-
547 *
548 * This routine simply activates the device in its current
549 * configuration, or deactivates the device.  It does not attempt any
550 * kind of resource arbitration.
551 *
552 */
553void isapnp_device_activation ( struct isapnp_device *isapnp,
554				int activation ) {
555	/* Wake the card and select the logical device */
556	isapnp_wait_for_key ();
557	isapnp_send_key ();
558	isapnp_wake ( isapnp->csn );
559	isapnp_logicaldevice ( isapnp->logdev );
560
561	/* Activate/deactivate the logical device */
562	isapnp_activate ( activation );
563	isapnp_delay();
564
565	/* Return all cards to Wait for Key state */
566	isapnp_wait_for_key ();
567
568	DBG ( "ISAPnP %s device %02x:%02x\n",
569	      ( activation ? "activated" : "deactivated" ),
570	      isapnp->csn, isapnp->logdev );
571}
572
573/**
574 * Probe an ISAPnP device
575 *
576 * @v isapnp		ISAPnP device
577 * @ret rc		Return status code
578 *
579 * Searches for a driver for the ISAPnP device.  If a driver is found,
580 * its probe() routine is called.
581 */
582static int isapnp_probe ( struct isapnp_device *isapnp ) {
583	struct isapnp_driver *driver;
584	struct isapnp_device_id *id;
585	unsigned int i;
586	int rc;
587
588	DBG ( "Adding ISAPnP device %02x:%02x (%04x:%04x (\"%s\") "
589	      "io %x irq %d)\n", isapnp->csn, isapnp->logdev,
590	      isapnp->vendor_id, isapnp->prod_id,
591	      isa_id_string ( isapnp->vendor_id, isapnp->prod_id ),
592	      isapnp->ioaddr, isapnp->irqno );
593
594	for_each_table_entry ( driver, ISAPNP_DRIVERS ) {
595		for ( i = 0 ; i < driver->id_count ; i++ ) {
596			id = &driver->ids[i];
597			if ( id->vendor_id != isapnp->vendor_id )
598				continue;
599			if ( ISA_PROD_ID ( id->prod_id ) !=
600			     ISA_PROD_ID ( isapnp->prod_id ) )
601				continue;
602			isapnp->driver = driver;
603			isapnp->driver_name = id->name;
604			DBG ( "...using driver %s\n", isapnp->driver_name );
605			if ( ( rc = driver->probe ( isapnp, id ) ) != 0 ) {
606				DBG ( "......probe failed\n" );
607				continue;
608			}
609			return 0;
610		}
611	}
612
613	DBG ( "...no driver found\n" );
614	return -ENOTTY;
615}
616
617/**
618 * Remove an ISAPnP device
619 *
620 * @v isapnp		ISAPnP device
621 */
622static void isapnp_remove ( struct isapnp_device *isapnp ) {
623	isapnp->driver->remove ( isapnp );
624	DBG ( "Removed ISAPnP device %02x:%02x\n",
625	      isapnp->csn, isapnp->logdev );
626}
627
628/**
629 * Probe ISAPnP root bus
630 *
631 * @v rootdev		ISAPnP bus root device
632 *
633 * Scans the ISAPnP bus for devices and registers all devices it can
634 * find.
635 */
636static int isapnpbus_probe ( struct root_device *rootdev ) {
637	struct isapnp_device *isapnp = NULL;
638	struct isapnp_identifier identifier;
639	struct isapnp_logdevid logdevid;
640	unsigned int csn;
641	unsigned int logdev;
642	int rc;
643
644	/* Perform isolation if it hasn't yet been done */
645	if ( ! isapnp_read_port )
646		isapnp_isolate();
647
648	for ( csn = 1 ; csn <= 0xff ; csn++ ) {
649		for ( logdev = 0 ; logdev <= 0xff ; logdev++ ) {
650
651			/* Allocate struct isapnp_device */
652			if ( ! isapnp )
653				isapnp = malloc ( sizeof ( *isapnp ) );
654			if ( ! isapnp ) {
655				rc = -ENOMEM;
656				goto err;
657			}
658			memset ( isapnp, 0, sizeof ( *isapnp ) );
659			isapnp->csn = csn;
660			isapnp->logdev = logdev;
661
662			/* Wake the card */
663			isapnp_wait_for_key();
664			isapnp_send_key();
665			isapnp_wake ( csn );
666
667			/* Read the card identifier */
668			isapnp_peek ( &identifier, sizeof ( identifier ) );
669
670			/* No card with this CSN; stop here */
671			if ( identifier.vendor_id & 0x80 )
672				goto done;
673
674			/* Find the Logical Device ID tag */
675			if ( ( rc = isapnp_find_logdevid ( logdev,
676							   &logdevid ) ) != 0){
677				/* No more logical devices; go to next CSN */
678				break;
679			}
680
681			/* Select the logical device */
682			isapnp_logicaldevice ( logdev );
683
684			/* Populate struct isapnp_device */
685			isapnp->vendor_id = logdevid.vendor_id;
686			isapnp->prod_id = logdevid.prod_id;
687			isapnp->ioaddr = isapnp_read_iobase ( 0 );
688			isapnp->irqno = isapnp_read_irqno ( 0 );
689
690			/* Return all cards to Wait for Key state */
691			isapnp_wait_for_key();
692
693			/* Add to device hierarchy */
694			snprintf ( isapnp->dev.name,
695				   sizeof ( isapnp->dev.name ),
696				   "ISAPnP%02x:%02x", csn, logdev );
697			isapnp->dev.desc.bus_type = BUS_TYPE_ISAPNP;
698			isapnp->dev.desc.vendor = isapnp->vendor_id;
699			isapnp->dev.desc.device = isapnp->prod_id;
700			isapnp->dev.desc.ioaddr = isapnp->ioaddr;
701			isapnp->dev.desc.irq = isapnp->irqno;
702			isapnp->dev.parent = &rootdev->dev;
703			list_add ( &isapnp->dev.siblings,
704				   &rootdev->dev.children );
705			INIT_LIST_HEAD ( &isapnp->dev.children );
706
707			/* Look for a driver */
708			if ( isapnp_probe ( isapnp ) == 0 ) {
709				/* isapnpdev registered, we can drop our ref */
710				isapnp = NULL;
711			} else {
712				/* Not registered; re-use struct */
713				list_del ( &isapnp->dev.siblings );
714			}
715		}
716	}
717
718 done:
719	free ( isapnp );
720	return 0;
721
722 err:
723	free ( isapnp );
724	isapnpbus_remove ( rootdev );
725	return rc;
726}
727
728/**
729 * Remove ISAPnP root bus
730 *
731 * @v rootdev		ISAPnP bus root device
732 */
733static void isapnpbus_remove ( struct root_device *rootdev ) {
734	struct isapnp_device *isapnp;
735	struct isapnp_device *tmp;
736
737	list_for_each_entry_safe ( isapnp, tmp, &rootdev->dev.children,
738				   dev.siblings ) {
739		isapnp_remove ( isapnp );
740		list_del ( &isapnp->dev.siblings );
741		free ( isapnp );
742	}
743}
744
745/** ISAPnP bus root device driver */
746static struct root_driver isapnp_root_driver = {
747	.probe = isapnpbus_probe,
748	.remove = isapnpbus_remove,
749};
750
751/** ISAPnP bus root device */
752struct root_device isapnp_root_device __root_device = {
753	.dev = { .name = "ISAPnP" },
754	.driver = &isapnp_root_driver,
755};
756