aboutsummaryrefslogtreecommitdiff
path: root/src/drivers/bus/eisa.c
blob: 31c248f0717d3cd97e64d777b5a99a48f9958d93 (plain)
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
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
#include "string.h"
#include "io.h"
#include "timer.h"
#include "console.h"
#include "dev.h"
#include "eisa.h"

/*
 * Increment a bus_loc structure to the next possible EISA location.
 * Leave the structure zeroed and return 0 if there are no more valid
 * locations.
 *
 */
static int eisa_next_location ( struct bus_loc *bus_loc ) {
	struct eisa_loc *eisa_loc = ( struct eisa_loc * ) bus_loc;
	
	/*
	 * Ensure that there is sufficient space in the shared bus
	 * structures for a struct isa_loc and a struct
	 * isa_dev, as mandated by bus.h.
	 *
	 */
	BUS_LOC_CHECK ( struct eisa_loc );
	BUS_DEV_CHECK ( struct eisa_device );

	return ( eisa_loc->slot = ( ++eisa_loc->slot & EISA_MAX_SLOT ) );
}

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

	/* Copy slot number to struct eisa, set default values */
	eisa->slot = eisa_loc->slot;
	eisa->name = "?";

	/* Slot 0 is never valid */
	if ( ! eisa->slot )
		return 0;

	/* 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 %hhx (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;
}

/*
 * Test whether or not a driver is capable of driving the device.
 *
 */
static int eisa_check_driver ( struct bus_dev *bus_dev,
				 struct device_driver *device_driver ) {
	struct eisa_device *eisa = ( struct eisa_device * ) bus_dev;
	struct eisa_driver *driver
		= ( struct eisa_driver * ) device_driver->bus_driver_info;
	unsigned int i;

	/* 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;
			return 1;
		}
	}

	/* No device found */
	return 0;
}

/*
 * Describe an EISA device
 *
 */
static char * eisa_describe_device ( struct bus_dev *bus_dev ) {
	struct eisa_device *eisa = ( struct eisa_device * ) bus_dev;
	static char eisa_description[] = "EISA 00";

	sprintf ( eisa_description + 5, "%hhx", eisa->slot );
	return eisa_description;
}

/*
 * Name an EISA device
 *
 */
static const char * eisa_name_device ( struct bus_dev *bus_dev ) {
	struct eisa_device *eisa = ( struct eisa_device * ) bus_dev;
	
	return eisa->name;
}

/*
 * EISA bus operations table
 *
 */
struct bus_driver eisa_driver __bus_driver = {
	.name			= "EISA",
	.next_location		= eisa_next_location,
	.fill_device		= eisa_fill_device,
	.check_driver		= eisa_check_driver,
	.describe_device	= eisa_describe_device,
	.name_device		= eisa_name_device,
};

/*
 * Fill in a nic structure
 *
 */
void eisa_fill_nic ( struct nic *nic, struct eisa_device *eisa ) {

	/* Fill in ioaddr and irqno */
	nic->ioaddr = eisa->ioaddr;
	nic->irqno = 0;

	/* Fill in DHCP device ID structure */
	nic->dhcp_dev_id.bus_type = ISA_BUS_TYPE;
	nic->dhcp_dev_id.vendor_id = htons ( eisa->mfg_id );
	nic->dhcp_dev_id.device_id = htons ( eisa->prod_id );
}

/*
 * Reset and enable/disable an EISA device
 *
 */
void eisa_device_enabled ( struct eisa_device *eisa, int enabled ) {
	/* 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 ( enabled ? EISA_CMD_ENABLE : 0,
	       eisa->ioaddr + EISA_GLOBAL_CONFIG );
	udelay ( 1000 ); /* Must wait 800 */

	DBG ( "EISA %s device %hhx\n", ( enabled ? "enabled" : "disabled" ),
	      eisa->slot );
}