eisa-bus.c 9.76 KB
Newer Older
Marc Zyngier's avatar
Marc Zyngier committed
1 2 3
/*
 * EISA bus support functions for sysfs.
 *
Marc Zyngier's avatar
Marc Zyngier committed
4
 * (C) 2002, 2003 Marc Zyngier <maz@wild-wind.fr.eu.org>
Marc Zyngier's avatar
Marc Zyngier committed
5 6 7 8 9 10 11 12
 *
 * This code is released under the GPL version 2.
 */

#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/eisa.h>
#include <linux/module.h>
Marc Zyngier's avatar
Marc Zyngier committed
13
#include <linux/moduleparam.h>
Marc Zyngier's avatar
Marc Zyngier committed
14 15 16 17 18
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/ioport.h>
#include <asm/io.h>

Marc Zyngier's avatar
Marc Zyngier committed
19 20
#define SLOT_ADDRESS(r,n) (r->bus_base_addr + (0x1000 * n))

Marc Zyngier's avatar
Marc Zyngier committed
21 22 23 24 25 26 27
#define EISA_DEVINFO(i,s) { .id = { .sig = i }, .name = s }

struct eisa_device_info {
	struct eisa_device_id id;
	char name[DEVICE_NAME_SIZE];
};

Marc Zyngier's avatar
Marc Zyngier committed
28
static struct eisa_device_info __initdata eisa_table[] = {
Marc Zyngier's avatar
Marc Zyngier committed
29 30 31 32 33 34 35
#ifdef CONFIG_EISA_NAMES
#include "devlist.h"
#endif
};

#define EISA_INFOS (sizeof (eisa_table) / (sizeof (struct eisa_device_info)))

Marc Zyngier's avatar
Marc Zyngier committed
36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
#define EISA_MAX_FORCED_DEV 16
#define EISA_FORCED_OFFSET  2

static int enable_dev[EISA_MAX_FORCED_DEV + EISA_FORCED_OFFSET]  = { 1, EISA_MAX_FORCED_DEV, };
static int disable_dev[EISA_MAX_FORCED_DEV + EISA_FORCED_OFFSET] = { 1, EISA_MAX_FORCED_DEV, };

static int is_forced_dev (int *forced_tab,
			  struct eisa_root_device *root,
			  struct eisa_device *edev)
{
	int i, x;

	for (i = 0; i < EISA_MAX_FORCED_DEV; i++) {
		if (!forced_tab[EISA_FORCED_OFFSET + i])
			return 0;

		x = (root->bus_nr << 8) | edev->slot;
		if (forced_tab[EISA_FORCED_OFFSET + i] == x)
			return 1;
	}

	return 0;
}

Marc Zyngier's avatar
Marc Zyngier committed
60 61 62 63 64 65
static void __init eisa_name_device (struct eisa_device *edev)
{
	int i;

	for (i = 0; i < EISA_INFOS; i++) {
		if (!strcmp (edev->id.sig, eisa_table[i].id.sig)) {
66
			strlcpy (edev->dev.name,
Marc Zyngier's avatar
Marc Zyngier committed
67
				 eisa_table[i].name,
68
				 DEVICE_NAME_SIZE);
Marc Zyngier's avatar
Marc Zyngier committed
69 70 71 72 73 74 75 76 77 78 79 80 81 82 83
			return;
		}
	}

	/* No name was found */
	sprintf (edev->dev.name, "EISA device %.7s", edev->id.sig);
}

static char __init *decode_eisa_sig(unsigned long addr)
{
        static char sig_str[EISA_SIG_LEN];
	u8 sig[4];
        u16 rev;
	int i;

Marc Zyngier's avatar
Marc Zyngier committed
84 85 86 87 88 89 90 91 92 93 94
	for (i = 0; i < 4; i++) {
#ifdef CONFIG_EISA_VLB_PRIMING
		/*
		 * This ugly stuff is used to wake up VL-bus cards
		 * (AHA-284x is the only known example), so we can
		 * read the EISA id.
		 *
		 * Thankfully, this only exists on x86...
		 */
		outb(0x80 + i, addr);
#endif
Marc Zyngier's avatar
Marc Zyngier committed
95
		sig[i] = inb (addr + i);
Marc Zyngier's avatar
Marc Zyngier committed
96 97 98 99

		if (!i && (sig[0] & 0x80))
			return NULL;
	}
Marc Zyngier's avatar
Marc Zyngier committed
100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119
	
        sig_str[0] = ((sig[0] >> 2) & 0x1f) + ('A' - 1);
        sig_str[1] = (((sig[0] & 3) << 3) | (sig[1] >> 5)) + ('A' - 1);
        sig_str[2] = (sig[1] & 0x1f) + ('A' - 1);
        rev = (sig[2] << 8) | sig[3];
        sprintf(sig_str + 3, "%04X", rev);

        return sig_str;
}

static int eisa_bus_match (struct device *dev, struct device_driver *drv)
{
	struct eisa_device *edev = to_eisa_device (dev);
	struct eisa_driver *edrv = to_eisa_driver (drv);
	const struct eisa_device_id *eids = edrv->id_table;

	if (!eids)
		return 0;

	while (strlen (eids->sig)) {
Marc Zyngier's avatar
Marc Zyngier committed
120 121
		if (!strcmp (eids->sig, edev->id.sig) &&
		    edev->state & EISA_CONFIG_ENABLED) {
Marc Zyngier's avatar
Marc Zyngier committed
122
			edev->id.driver_data = eids->driver_data;
Marc Zyngier's avatar
Marc Zyngier committed
123
			return 1;
Marc Zyngier's avatar
Marc Zyngier committed
124
		}
Marc Zyngier's avatar
Marc Zyngier committed
125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144

		eids++;
	}

	return 0;
}

struct bus_type eisa_bus_type = {
	.name  = "eisa",
	.match = eisa_bus_match,
};

int eisa_driver_register (struct eisa_driver *edrv)
{
	int r;
	
	edrv->driver.bus = &eisa_bus_type;
	if ((r = driver_register (&edrv->driver)) < 0)
		return r;

Marc Zyngier's avatar
Marc Zyngier committed
145
	return 0;
Marc Zyngier's avatar
Marc Zyngier committed
146 147 148 149 150 151 152 153 154 155 156 157 158 159 160
}

void eisa_driver_unregister (struct eisa_driver *edrv)
{
	driver_unregister (&edrv->driver);
}

static ssize_t eisa_show_sig (struct device *dev, char *buf)
{
        struct eisa_device *edev = to_eisa_device (dev);
        return sprintf (buf,"%s\n", edev->id.sig);
}

static DEVICE_ATTR(signature, S_IRUGO, eisa_show_sig, NULL);

Marc Zyngier's avatar
Marc Zyngier committed
161 162 163 164 165 166 167 168 169 170 171
static ssize_t eisa_show_state (struct device *dev, char *buf)
{
        struct eisa_device *edev = to_eisa_device (dev);
        return sprintf (buf,"%d\n", edev->state & EISA_CONFIG_ENABLED);
}

static DEVICE_ATTR(enabled, S_IRUGO, eisa_show_state, NULL);

static int __init eisa_init_device (struct eisa_root_device *root,
				    struct eisa_device *edev,
				    int slot)
Marc Zyngier's avatar
Marc Zyngier committed
172
{
Marc Zyngier's avatar
Marc Zyngier committed
173 174 175 176 177 178 179 180 181
	char *sig;
        unsigned long sig_addr;
	int i;

	sig_addr = SLOT_ADDRESS (root, slot) + EISA_VENDOR_ID_OFFSET;

	if (!(sig = decode_eisa_sig (sig_addr)))
		return -1;	/* No EISA device here */
	
Marc Zyngier's avatar
Marc Zyngier committed
182
	memcpy (edev->id.sig, sig, EISA_SIG_LEN);
Marc Zyngier's avatar
Marc Zyngier committed
183
	edev->slot = slot;
Marc Zyngier's avatar
Marc Zyngier committed
184
	edev->state = inb (SLOT_ADDRESS (root, slot) + EISA_CONFIG_OFFSET) & EISA_CONFIG_ENABLED;
Marc Zyngier's avatar
Marc Zyngier committed
185
	edev->base_addr = SLOT_ADDRESS (root, slot);
Marc Zyngier's avatar
Marc Zyngier committed
186
	edev->dma_mask = root->dma_mask; /* Default DMA mask */
Marc Zyngier's avatar
Marc Zyngier committed
187
	eisa_name_device (edev);
Marc Zyngier's avatar
Marc Zyngier committed
188
	edev->dev.parent = root->dev;
Marc Zyngier's avatar
Marc Zyngier committed
189
	edev->dev.bus = &eisa_bus_type;
Marc Zyngier's avatar
Marc Zyngier committed
190
	edev->dev.dma_mask = &edev->dma_mask;
Marc Zyngier's avatar
Marc Zyngier committed
191
	sprintf (edev->dev.bus_id, "%02X:%02X", root->bus_nr, slot);
Marc Zyngier's avatar
Marc Zyngier committed
192

Marc Zyngier's avatar
Marc Zyngier committed
193 194 195 196 197 198 199 200 201 202 203
	for (i = 0; i < EISA_MAX_RESOURCES; i++)
		edev->res[i].name  = edev->dev.name;

	if (is_forced_dev (enable_dev, root, edev))
		edev->state = EISA_CONFIG_ENABLED | EISA_CONFIG_FORCED;
	
	if (is_forced_dev (disable_dev, root, edev))
		edev->state = EISA_CONFIG_FORCED;

	return 0;
}
Marc Zyngier's avatar
Marc Zyngier committed
204

Marc Zyngier's avatar
Marc Zyngier committed
205 206
static int __init eisa_register_device (struct eisa_device *edev)
{
Marc Zyngier's avatar
Marc Zyngier committed
207 208
	if (device_register (&edev->dev))
		return -1;
Marc Zyngier's avatar
Marc Zyngier committed
209 210

	device_create_file (&edev->dev, &dev_attr_signature);
Marc Zyngier's avatar
Marc Zyngier committed
211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248
	device_create_file (&edev->dev, &dev_attr_enabled);

	return 0;
}

static int __init eisa_request_resources (struct eisa_root_device *root,
					  struct eisa_device *edev,
					  int slot)
{
	int i;

	for (i = 0; i < EISA_MAX_RESOURCES; i++) {
		/* Don't register resource for slot 0, since this is
		 * very likely to fail... :-( Instead, grab the EISA
		 * id, now we can display something in /proc/ioports.
		 */

		/* Only one region for mainboard */
		if (!slot && i > 0) {
			edev->res[i].start = edev->res[i].end = 0;
			continue;
		}
		
		if (slot) {
			edev->res[i].name  = NULL;
			edev->res[i].start = SLOT_ADDRESS (root, slot) + (i * 0x400);
			edev->res[i].end   = edev->res[i].start + 0xff;
			edev->res[i].flags = IORESOURCE_IO;
		} else {
			edev->res[i].name  = NULL;
			edev->res[i].start = SLOT_ADDRESS (root, slot) + EISA_VENDOR_ID_OFFSET;
			edev->res[i].end   = edev->res[i].start + 3;
			edev->res[i].flags = IORESOURCE_BUSY;
		}

		if (request_resource (root->res, &edev->res[i]))
			goto failed;
	}
Marc Zyngier's avatar
Marc Zyngier committed
249 250

	return 0;
Marc Zyngier's avatar
Marc Zyngier committed
251 252 253 254 255 256 257 258 259 260 261 262 263 264 265
	
 failed:
	while (--i >= 0)
		release_resource (&edev->res[i]);

	return -1;
}

static void __init eisa_release_resources (struct eisa_device *edev)
{
	int i;

	for (i = 0; i < EISA_MAX_RESOURCES; i++)
		if (edev->res[i].start || edev->res[i].end)
			release_resource (&edev->res[i]);
Marc Zyngier's avatar
Marc Zyngier committed
266 267
}

Marc Zyngier's avatar
Marc Zyngier committed
268
static int __init eisa_probe (struct eisa_root_device *root)
Marc Zyngier's avatar
Marc Zyngier committed
269 270
{
        int i, c;
Marc Zyngier's avatar
Marc Zyngier committed
271
	struct eisa_device *edev;
Marc Zyngier's avatar
Marc Zyngier committed
272

Marc Zyngier's avatar
Marc Zyngier committed
273 274
        printk (KERN_INFO "EISA: Probing bus %d at %s\n",
		root->bus_nr, root->dev->name);
Marc Zyngier's avatar
Marc Zyngier committed
275 276 277

	/* First try to get hold of slot 0. If there is no device
	 * here, simply fail, unless root->force_probe is set. */
Marc Zyngier's avatar
Marc Zyngier committed
278
	
Marc Zyngier's avatar
Marc Zyngier committed
279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314
	if (!(edev = kmalloc (sizeof (*edev), GFP_KERNEL))) {
		printk (KERN_ERR "EISA: Couldn't allocate mainboard slot\n");
		return -ENOMEM;
	}
		
	memset (edev, 0, sizeof (*edev));

	if (eisa_request_resources (root, edev, 0)) {
		printk (KERN_WARNING \
			"EISA: Cannot allocate resource for mainboard\n");
		kfree (edev);
		if (!root->force_probe)
			return -EBUSY;
		goto force_probe;
	}

	if (eisa_init_device (root, edev, 0)) {
		eisa_release_resources (edev);
		kfree (edev);
		if (!root->force_probe)
			return -ENODEV;
		goto force_probe;
	}

	printk (KERN_INFO "EISA: Mainboard %s detected.\n", edev->id.sig);

	if (eisa_register_device (edev)) {
		printk (KERN_ERR "EISA: Failed to register %s\n",
			edev->id.sig);
		eisa_release_resources (edev);
		kfree (edev);
	}
	
 force_probe:
	
        for (c = 0, i = 1; i <= root->slots; i++) {
Marc Zyngier's avatar
Marc Zyngier committed
315 316 317 318 319 320 321 322
		if (!(edev = kmalloc (sizeof (*edev), GFP_KERNEL))) {
			printk (KERN_ERR "EISA: Out of memory for slot %d\n",
				i);
			continue;
		}
		
		memset (edev, 0, sizeof (*edev));

Marc Zyngier's avatar
Marc Zyngier committed
323
		if (eisa_request_resources (root, edev, i)) {
Marc Zyngier's avatar
Marc Zyngier committed
324 325 326 327 328 329 330
			printk (KERN_WARNING \
				"Cannot allocate resource for EISA slot %d\n",
				i);
			kfree (edev);
			continue;
		}

Marc Zyngier's avatar
Marc Zyngier committed
331 332
                if (eisa_init_device (root, edev, i)) {
			eisa_release_resources (edev);
Marc Zyngier's avatar
Marc Zyngier committed
333 334 335 336
			kfree (edev);
			continue;
		}
		
Marc Zyngier's avatar
Marc Zyngier committed
337 338 339 340 341 342 343 344 345 346 347 348 349 350 351
		printk (KERN_INFO "EISA: slot %d : %s detected",
			i, edev->id.sig);
			
		switch (edev->state) {
		case EISA_CONFIG_ENABLED | EISA_CONFIG_FORCED:
			printk (" (forced enabled)");
			break;

		case EISA_CONFIG_FORCED:
			printk (" (forced disabled)");
			break;

		case 0:
			printk (" (disabled)");
			break;
Marc Zyngier's avatar
Marc Zyngier committed
352
		}
Marc Zyngier's avatar
Marc Zyngier committed
353 354
			
		printk (".\n");
Marc Zyngier's avatar
Marc Zyngier committed
355

Marc Zyngier's avatar
Marc Zyngier committed
356 357 358 359 360 361
		c++;

		if (eisa_register_device (edev)) {
			printk (KERN_ERR "EISA: Failed to register %s\n",
				edev->id.sig);
			eisa_release_resources (edev);
Marc Zyngier's avatar
Marc Zyngier committed
362 363
			kfree (edev);
		}
Marc Zyngier's avatar
Marc Zyngier committed
364
        }
Marc Zyngier's avatar
Marc Zyngier committed
365

366
        printk (KERN_INFO "EISA: Detected %d card%s.\n", c, c == 1 ? "" : "s");
Marc Zyngier's avatar
Marc Zyngier committed
367 368 369 370

	return 0;
}

Marc Zyngier's avatar
Marc Zyngier committed
371 372 373 374 375 376
static struct resource eisa_root_res = {
	.name  = "EISA root resource",
	.start = 0,
	.end   = 0xffffffff,
	.flags = IORESOURCE_IO,
};
Marc Zyngier's avatar
Marc Zyngier committed
377 378 379

static int eisa_bus_count;

Marc Zyngier's avatar
Marc Zyngier committed
380
int __init eisa_root_register (struct eisa_root_device *root)
Marc Zyngier's avatar
Marc Zyngier committed
381
{
Marc Zyngier's avatar
Marc Zyngier committed
382
	int err;
Marc Zyngier's avatar
Marc Zyngier committed
383

Marc Zyngier's avatar
Marc Zyngier committed
384 385 386 387
	/* Use our own resources to check if this bus base address has
	 * been already registered. This prevents the virtual root
	 * device from registering after the real one has, for
	 * example... */
Marc Zyngier's avatar
Marc Zyngier committed
388
	
Marc Zyngier's avatar
Marc Zyngier committed
389 390 391 392 393 394 395
	root->eisa_root_res.name  = eisa_root_res.name;
	root->eisa_root_res.start = root->res->start;
	root->eisa_root_res.end   = root->res->end;
	root->eisa_root_res.flags = IORESOURCE_BUSY;

	if ((err = request_resource (&eisa_root_res, &root->eisa_root_res)))
		return err;
Marc Zyngier's avatar
Marc Zyngier committed
396 397
	
	root->bus_nr = eisa_bus_count++;
Marc Zyngier's avatar
Marc Zyngier committed
398 399 400 401 402

	if ((err = eisa_probe (root)))
		release_resource (&root->eisa_root_res);

	return err;
Marc Zyngier's avatar
Marc Zyngier committed
403 404
}

Marc Zyngier's avatar
Marc Zyngier committed
405 406 407 408 409 410 411 412
static int __init eisa_init (void)
{
	int r;
	
	if ((r = bus_register (&eisa_bus_type)))
		return r;

	printk (KERN_INFO "EISA bus registered\n");
Marc Zyngier's avatar
Marc Zyngier committed
413
	return 0;
Marc Zyngier's avatar
Marc Zyngier committed
414 415
}

Marc Zyngier's avatar
Marc Zyngier committed
416 417 418 419 420 421 422
/* Couldn't use intarray with checking on... :-( */
#undef  param_check_intarray
#define param_check_intarray(name, p)

module_param(enable_dev,  intarray, 0444);
module_param(disable_dev, intarray, 0444);

Marc Zyngier's avatar
Marc Zyngier committed
423 424 425 426 427
postcore_initcall (eisa_init);

EXPORT_SYMBOL (eisa_bus_type);
EXPORT_SYMBOL (eisa_driver_register);
EXPORT_SYMBOL (eisa_driver_unregister);