summaryrefslogtreecommitdiffstats
path: root/src/drivers/bus/eisa.c
blob: d1902dd430e6c7ce9513736f0729958c46abeb43 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
#include "string.h"
#include "io.h"
#include "timer.h"
#include "eisa.h"

/*
 * Ensure that there is sufficient space in the shared dev_bus
 * structure for a struct pci_device.
 *
 */
DEV_BUS( struct eisa_device, eisa_dev );
static char eisa_magic[0]; /* guaranteed unique symbol */

/*
 * Fill in parameters for an EISA device based on slot number
 *
 * Return 1 if device present, 0 otherwise
 *
 */
static int fill_eisa_device ( struct eisa_device *eisa ) {
	uint8_t present;

	/* Set ioaddr */
	eisa->ioaddr = EISA_SLOT_BASE ( eisa->slot );

	/* Test for board present */
	outb ( 0xff, eisa->ioaddr + EISA_MFG_ID_HI );
	present = inb ( eisa->ioaddr + EISA_MFG_ID_HI );
	if ( present & 0x80 ) {
		/* No board present */
		return 0;
	}

	/* Read mfg and product IDs.  Yes, the resulting uint16_ts
	 * will be upside-down.  This appears to be by design.
	 */
	eisa->mfg_id = ( inb ( eisa->ioaddr + EISA_MFG_ID_LO ) << 8 )
		+ present;
	eisa->prod_id = ( inb ( eisa->ioaddr + EISA_PROD_ID_LO ) << 8 )
		+ inb ( eisa->ioaddr + EISA_PROD_ID_HI );

	DBG ( "EISA found slot %d (base %#hx) ID %hx:%hx (\"%s\")\n",
	      eisa->slot, eisa->ioaddr, eisa->mfg_id, eisa->prod_id,
	      isa_id_string ( eisa->mfg_id, eisa->prod_id ) );

	return 1;
}

/*
 * Find an EISA device matching the specified driver
 *
 */
int find_eisa_device ( struct eisa_device *eisa, struct eisa_driver *driver ) {
	unsigned int i;

	/* Initialise struct eisa if it's the first time it's been used. */
	if ( eisa->magic != eisa_magic ) {
		memset ( eisa, 0, sizeof ( *eisa ) );
		eisa->magic = eisa_magic;
		eisa->slot = EISA_MIN_SLOT;
	}

	/* Iterate through all possible EISA slots, starting where we
	 * left off.
	 */
	DBG ( "EISA searching for device matching driver %s\n", driver->name );
	for ( ; eisa->slot <= EISA_MAX_SLOT ; eisa->slot++ ) {
		/* If we've already used this device, skip it */
		if ( eisa->already_tried ) {
			eisa->already_tried = 0;
			continue;
		}

		/* Fill in device parameters */
		if ( ! fill_eisa_device ( eisa ) ) {
			continue;
		}

		/* Compare against driver's ID list */
		for ( i = 0 ; i < driver->id_count ; i++ ) {
			struct eisa_id *id = &driver->ids[i];
			
			if ( ( eisa->mfg_id == id->mfg_id ) &&
			     ( ISA_PROD_ID ( eisa->prod_id ) ==
			       ISA_PROD_ID ( id->prod_id ) ) ) {
				DBG ( "EISA found ID %hx:%hx (\"%s\") "
				      "(device %s) matching driver %s\n",
				      eisa->mfg_id, eisa->prod_id,
				      isa_id_string ( eisa->mfg_id,
						      eisa->prod_id ),
				      id->name, driver->name );
				eisa->name = id->name;
				eisa->already_tried = 1;
				return 1;
			}
		}
	}

	/* No device found */
	DBG ( "EISA found no device matching driver %s\n", driver->name );
	eisa->slot = EISA_MIN_SLOT;
	return 0;
}

/*
 * Find the next EISA device that can be used to boot using the
 * specified driver.
 *
 */
int find_eisa_boot_device ( struct dev *dev, struct eisa_driver *driver ) {
	struct eisa_device *eisa = ( struct eisa_device * )dev->bus;

	if ( ! find_eisa_device ( eisa, driver ) )
		return 0;

	dev->name = eisa->name;
	dev->devid.bus_type = ISA_BUS_TYPE;
	dev->devid.vendor_id = eisa->mfg_id;
	dev->devid.device_id = eisa->prod_id;

	return 1;
}

/*
 * Reset and enable an EISA device
 *
 */
void enable_eisa_device ( struct eisa_device *eisa ) {
	/* Set reset line high for 1000 µs.  Spec says 500 µs, but
	 * this doesn't work for all cards, so we are conservative.
	 */
	outb ( EISA_CMD_RESET, eisa->ioaddr + EISA_GLOBAL_CONFIG );
	udelay ( 1000 ); /* Must wait 800 */

	/* Set reset low and write a 1 to ENABLE.  Delay again, in
	 * case the card takes a while to wake up.
	 */
	outb ( EISA_CMD_ENABLE, eisa->ioaddr + EISA_GLOBAL_CONFIG );
	udelay ( 1000 ); /* Must wait 800 */
}