mca_32.c 12.5 KB
Newer Older
L
Linus Torvalds 已提交
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
/*
 *  Written by Martin Kolinek, February 1996
 *
 * Changes:
 *
 *	Chris Beauregard July 28th, 1996
 *	- Fixed up integrated SCSI detection
 *
 *	Chris Beauregard August 3rd, 1996
 *	- Made mca_info local
 *	- Made integrated registers accessible through standard function calls
 *	- Added name field
 *	- More sanity checking
 *
 *	Chris Beauregard August 9th, 1996
 *	- Rewrote /proc/mca
 *
 *	Chris Beauregard January 7th, 1997
 *	- Added basic NMI-processing
 *	- Added more information to mca_info structure
 *
 *	David Weinehall October 12th, 1998
 *	- Made a lot of cleaning up in the source
 *	- Added use of save_flags / restore_flags
 *	- Added the 'driver_loaded' flag in MCA_adapter
 *	- Added an alternative implemention of ZP Gu's mca_find_unused_adapter
 *
 *	David Weinehall March 24th, 1999
 *	- Fixed the output of 'Driver Installed' in /proc/mca/pos
 *	- Made the Integrated Video & SCSI show up even if they have id 0000
 *
 *	Alexander Viro November 9th, 1999
 *	- Switched to regular procfs methods
 *
 *	Alfred Arnold & David Weinehall August 23rd, 2000
 *	- Added support for Planar POS-registers
 */

#include <linux/module.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/mca.h>
44
#include <linux/kprobes.h>
45
#include <linux/slab.h>
L
Linus Torvalds 已提交
46 47 48 49 50 51 52 53 54
#include <asm/io.h>
#include <linux/proc_fs.h>
#include <linux/mman.h>
#include <linux/mm.h>
#include <linux/pagemap.h>
#include <linux/ioport.h>
#include <asm/uaccess.h>
#include <linux/init.h>

55
static unsigned char which_scsi;
L
Linus Torvalds 已提交
56

57
int MCA_bus;
L
Linus Torvalds 已提交
58 59 60 61 62 63 64 65 66 67 68 69
EXPORT_SYMBOL(MCA_bus);

/*
 * Motherboard register spinlock. Untested on SMP at the moment, but
 * are there any MCA SMP boxes?
 *
 * Yes - Alan
 */
static DEFINE_SPINLOCK(mca_lock);

/* Build the status info for the adapter */

70 71
static void mca_configure_adapter_status(struct mca_device *mca_dev)
{
L
Linus Torvalds 已提交
72 73 74 75 76
	mca_dev->status = MCA_ADAPTER_NONE;

	mca_dev->pos_id = mca_dev->pos[0]
		+ (mca_dev->pos[1] << 8);

77
	if (!mca_dev->pos_id && mca_dev->slot < MCA_MAX_SLOT_NR) {
L
Linus Torvalds 已提交
78

79 80
		/*
		 * id = 0x0000 usually indicates hardware failure,
L
Linus Torvalds 已提交
81 82 83 84 85 86 87 88 89 90
		 * however, ZP Gu (zpg@castle.net> reports that his 9556
		 * has 0x0000 as id and everything still works. There
		 * also seem to be an adapter with id = 0x0000; the
		 * NCR Parallel Bus Memory Card. Until this is confirmed,
		 * however, this code will stay.
		 */

		mca_dev->status = MCA_ADAPTER_ERROR;

		return;
91
	} else if (mca_dev->pos_id != 0xffff) {
L
Linus Torvalds 已提交
92

93 94
		/*
		 * 0xffff usually indicates that there's no adapter,
L
Linus Torvalds 已提交
95 96 97 98 99 100 101 102 103
		 * however, some integrated adapters may have 0xffff as
		 * their id and still be valid. Examples are on-board
		 * VGA of the 55sx, the integrated SCSI of the 56 & 57,
		 * and possibly also the 95 ULTIMEDIA.
		 */

		mca_dev->status = MCA_ADAPTER_NORMAL;
	}

104
	if ((mca_dev->pos_id == 0xffff ||
L
Linus Torvalds 已提交
105 106 107
	    mca_dev->pos_id == 0x0000) && mca_dev->slot >= MCA_MAX_SLOT_NR) {
		int j;

108 109
		for (j = 2; j < 8; j++) {
			if (mca_dev->pos[j] != 0xff) {
L
Linus Torvalds 已提交
110 111 112 113 114 115
				mca_dev->status = MCA_ADAPTER_NORMAL;
				break;
			}
		}
	}

116
	if (!(mca_dev->pos[2] & MCA_ENABLED)) {
L
Linus Torvalds 已提交
117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135

		/* enabled bit is in POS 2 */

		mca_dev->status = MCA_ADAPTER_DISABLED;
	}
} /* mca_configure_adapter_status */

/*--------------------------------------------------------------------*/

static struct resource mca_standard_resources[] = {
	{ .start = 0x60, .end = 0x60, .name = "system control port B (MCA)" },
	{ .start = 0x90, .end = 0x90, .name = "arbitration (MCA)" },
	{ .start = 0x91, .end = 0x91, .name = "card Select Feedback (MCA)" },
	{ .start = 0x92, .end = 0x92, .name = "system Control port A (MCA)" },
	{ .start = 0x94, .end = 0x94, .name = "system board setup (MCA)" },
	{ .start = 0x96, .end = 0x97, .name = "POS (MCA)" },
	{ .start = 0x100, .end = 0x107, .name = "POS (MCA)" }
};

136
#define MCA_STANDARD_RESOURCES	ARRAY_SIZE(mca_standard_resources)
L
Linus Torvalds 已提交
137

138
/*
L
Linus Torvalds 已提交
139 140 141 142 143 144 145
 *	mca_read_and_store_pos - read the POS registers into a memory buffer
 *      @pos: a char pointer to 8 bytes, contains the POS register value on
 *            successful return
 *
 *	Returns 1 if a card actually exists (i.e. the pos isn't
 *	all 0xff) or 0 otherwise
 */
146 147
static int mca_read_and_store_pos(unsigned char *pos)
{
L
Linus Torvalds 已提交
148 149 150
	int j;
	int found = 0;

151 152 153
	for (j = 0; j < 8; j++) {
		pos[j] = inb_p(MCA_POS_REG(j));
		if (pos[j] != 0xff) {
L
Linus Torvalds 已提交
154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173
			/* 0xff all across means no device. 0x00 means
			 * something's broken, but a device is
			 * probably there.  However, if you get 0x00
			 * from a motherboard register it won't matter
			 * what we find.  For the record, on the
			 * 57SLC, the integrated SCSI adapter has
			 * 0xffff for the adapter ID, but nonzero for
			 * other registers.  */

			found = 1;
		}
	}
	return found;
}

static unsigned char mca_pc_read_pos(struct mca_device *mca_dev, int reg)
{
	unsigned char byte;
	unsigned long flags;

174
	if (reg < 0 || reg >= 8)
L
Linus Torvalds 已提交
175 176 177
		return 0;

	spin_lock_irqsave(&mca_lock, flags);
178
	if (mca_dev->pos_register) {
L
Linus Torvalds 已提交
179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209
		/* Disable adapter setup, enable motherboard setup */

		outb_p(0, MCA_ADAPTER_SETUP_REG);
		outb_p(mca_dev->pos_register, MCA_MOTHERBOARD_SETUP_REG);

		byte = inb_p(MCA_POS_REG(reg));
		outb_p(0xff, MCA_MOTHERBOARD_SETUP_REG);
	} else {

		/* Make sure motherboard setup is off */

		outb_p(0xff, MCA_MOTHERBOARD_SETUP_REG);

		/* Read the appropriate register */

		outb_p(0x8|(mca_dev->slot & 0xf), MCA_ADAPTER_SETUP_REG);
		byte = inb_p(MCA_POS_REG(reg));
		outb_p(0, MCA_ADAPTER_SETUP_REG);
	}
	spin_unlock_irqrestore(&mca_lock, flags);

	mca_dev->pos[reg] = byte;

	return byte;
}

static void mca_pc_write_pos(struct mca_device *mca_dev, int reg,
			     unsigned char byte)
{
	unsigned long flags;

210
	if (reg < 0 || reg >= 8)
L
Linus Torvalds 已提交
211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233
		return;

	spin_lock_irqsave(&mca_lock, flags);

	/* Make sure motherboard setup is off */

	outb_p(0xff, MCA_MOTHERBOARD_SETUP_REG);

	/* Read in the appropriate register */

	outb_p(0x8|(mca_dev->slot&0xf), MCA_ADAPTER_SETUP_REG);
	outb_p(byte, MCA_POS_REG(reg));
	outb_p(0, MCA_ADAPTER_SETUP_REG);

	spin_unlock_irqrestore(&mca_lock, flags);

	/* Update the global register list, while we have the byte */

	mca_dev->pos[reg] = byte;

}

/* for the primary MCA bus, we have identity transforms */
234
static int mca_dummy_transform_irq(struct mca_device *mca_dev, int irq)
L
Linus Torvalds 已提交
235 236 237 238
{
	return irq;
}

239
static int mca_dummy_transform_ioport(struct mca_device *mca_dev, int port)
L
Linus Torvalds 已提交
240 241 242 243
{
	return port;
}

244
static void *mca_dummy_transform_memory(struct mca_device *mca_dev, void *mem)
L
Linus Torvalds 已提交
245 246 247 248 249 250 251 252 253 254 255 256 257
{
	return mem;
}


static int __init mca_init(void)
{
	unsigned int i, j;
	struct mca_device *mca_dev;
	unsigned char pos[8];
	short mca_builtin_scsi_ports[] = {0xf7, 0xfd, 0x00};
	struct mca_bus *bus;

258 259
	/*
	 * WARNING: Be careful when making changes here. Putting an adapter
L
Linus Torvalds 已提交
260
	 * and the motherboard simultaneously into setup mode may result in
L
Lucas De Marchi 已提交
261
	 * damage to chips (according to The Indispensable PC Hardware Book
L
Linus Torvalds 已提交
262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289
	 * by Hans-Peter Messmer). Also, we disable system interrupts (so
	 * that we are not disturbed in the middle of this).
	 */

	/* Make sure the MCA bus is present */

	if (mca_system_init()) {
		printk(KERN_ERR "MCA bus system initialisation failed\n");
		return -ENODEV;
	}

	if (!MCA_bus)
		return -ENODEV;

	printk(KERN_INFO "Micro Channel bus detected.\n");

	/* All MCA systems have at least a primary bus */
	bus = mca_attach_bus(MCA_PRIMARY_BUS);
	if (!bus)
		goto out_nomem;
	bus->default_dma_mask = 0xffffffffLL;
	bus->f.mca_write_pos = mca_pc_write_pos;
	bus->f.mca_read_pos = mca_pc_read_pos;
	bus->f.mca_transform_irq = mca_dummy_transform_irq;
	bus->f.mca_transform_ioport = mca_dummy_transform_ioport;
	bus->f.mca_transform_memory = mca_dummy_transform_memory;

	/* get the motherboard device */
290
	mca_dev = kzalloc(sizeof(struct mca_device), GFP_KERNEL);
291
	if (unlikely(!mca_dev))
L
Linus Torvalds 已提交
292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315
		goto out_nomem;

	/*
	 * We do not expect many MCA interrupts during initialization,
	 * but let us be safe:
	 */
	spin_lock_irq(&mca_lock);

	/* Make sure adapter setup is off */

	outb_p(0, MCA_ADAPTER_SETUP_REG);

	/* Read motherboard POS registers */

	mca_dev->pos_register = 0x7f;
	outb_p(mca_dev->pos_register, MCA_MOTHERBOARD_SETUP_REG);
	mca_dev->name[0] = 0;
	mca_read_and_store_pos(mca_dev->pos);
	mca_configure_adapter_status(mca_dev);
	/* fake POS and slot for a motherboard */
	mca_dev->pos_id = MCA_MOTHERBOARD_POS;
	mca_dev->slot = MCA_MOTHERBOARD;
	mca_register_device(MCA_PRIMARY_BUS, mca_dev);

316
	mca_dev = kzalloc(sizeof(struct mca_device), GFP_ATOMIC);
317
	if (unlikely(!mca_dev))
L
Linus Torvalds 已提交
318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333
		goto out_unlock_nomem;

	/* Put motherboard into video setup mode, read integrated video
	 * POS registers, and turn motherboard setup off.
	 */

	mca_dev->pos_register = 0xdf;
	outb_p(mca_dev->pos_register, MCA_MOTHERBOARD_SETUP_REG);
	mca_dev->name[0] = 0;
	mca_read_and_store_pos(mca_dev->pos);
	mca_configure_adapter_status(mca_dev);
	/* fake POS and slot for the integrated video */
	mca_dev->pos_id = MCA_INTEGVIDEO_POS;
	mca_dev->slot = MCA_INTEGVIDEO;
	mca_register_device(MCA_PRIMARY_BUS, mca_dev);

334 335
	/*
	 * Put motherboard into scsi setup mode, read integrated scsi
L
Linus Torvalds 已提交
336 337 338 339 340 341 342 343 344 345 346
	 * POS registers, and turn motherboard setup off.
	 *
	 * It seems there are two possible SCSI registers. Martin says that
	 * for the 56,57, 0xf7 is the one, but fails on the 76.
	 * Alfredo (apena@vnet.ibm.com) says
	 * 0xfd works on his machine. We'll try both of them. I figure it's
	 * a good bet that only one could be valid at a time. This could
	 * screw up though if one is used for something else on the other
	 * machine.
	 */

347
	for (i = 0; (which_scsi = mca_builtin_scsi_ports[i]) != 0; i++) {
L
Linus Torvalds 已提交
348
		outb_p(which_scsi, MCA_MOTHERBOARD_SETUP_REG);
349
		if (mca_read_and_store_pos(pos))
L
Linus Torvalds 已提交
350 351
			break;
	}
352
	if (which_scsi) {
L
Linus Torvalds 已提交
353
		/* found a scsi card */
354
		mca_dev = kzalloc(sizeof(struct mca_device), GFP_ATOMIC);
355
		if (unlikely(!mca_dev))
L
Linus Torvalds 已提交
356 357
			goto out_unlock_nomem;

358
		for (j = 0; j < 8; j++)
L
Linus Torvalds 已提交
359 360 361 362 363 364 365 366 367 368 369 370 371 372
			mca_dev->pos[j] = pos[j];

		mca_configure_adapter_status(mca_dev);
		/* fake POS and slot for integrated SCSI controller */
		mca_dev->pos_id = MCA_INTEGSCSI_POS;
		mca_dev->slot = MCA_INTEGSCSI;
		mca_dev->pos_register = which_scsi;
		mca_register_device(MCA_PRIMARY_BUS, mca_dev);
	}

	/* Turn off motherboard setup */

	outb_p(0xff, MCA_MOTHERBOARD_SETUP_REG);

373 374
	/*
	 * Now loop over MCA slots: put each adapter into setup mode, and
L
Linus Torvalds 已提交
375 376 377
	 * read its POS registers. Then put adapter setup off.
	 */

378
	for (i = 0; i < MCA_MAX_SLOT_NR; i++) {
L
Linus Torvalds 已提交
379
		outb_p(0x8|(i&0xf), MCA_ADAPTER_SETUP_REG);
380
		if (!mca_read_and_store_pos(pos))
L
Linus Torvalds 已提交
381 382
			continue;

383
		mca_dev = kzalloc(sizeof(struct mca_device), GFP_ATOMIC);
384
		if (unlikely(!mca_dev))
L
Linus Torvalds 已提交
385 386
			goto out_unlock_nomem;

387 388
		for (j = 0; j < 8; j++)
			mca_dev->pos[j] = pos[j];
L
Linus Torvalds 已提交
389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418

		mca_dev->driver_loaded = 0;
		mca_dev->slot = i;
		mca_dev->pos_register = 0;
		mca_configure_adapter_status(mca_dev);
		mca_register_device(MCA_PRIMARY_BUS, mca_dev);
	}
	outb_p(0, MCA_ADAPTER_SETUP_REG);

	/* Enable interrupts and return memory start */
	spin_unlock_irq(&mca_lock);

	for (i = 0; i < MCA_STANDARD_RESOURCES; i++)
		request_resource(&ioport_resource, mca_standard_resources + i);

	mca_do_proc_init();

	return 0;

 out_unlock_nomem:
	spin_unlock_irq(&mca_lock);
 out_nomem:
	printk(KERN_EMERG "Failed memory allocation in MCA setup!\n");
	return -ENOMEM;
}

subsys_initcall(mca_init);

/*--------------------------------------------------------------------*/

419 420
static __kprobes void
mca_handle_nmi_device(struct mca_device *mca_dev, int check_flag)
L
Linus Torvalds 已提交
421 422 423
{
	int slot = mca_dev->slot;

424
	if (slot == MCA_INTEGSCSI) {
L
Linus Torvalds 已提交
425 426
		printk(KERN_CRIT "NMI: caused by MCA integrated SCSI adapter (%s)\n",
			mca_dev->name);
427
	} else if (slot == MCA_INTEGVIDEO) {
L
Linus Torvalds 已提交
428 429
		printk(KERN_CRIT "NMI: caused by MCA integrated video adapter (%s)\n",
			mca_dev->name);
430
	} else if (slot == MCA_MOTHERBOARD) {
L
Linus Torvalds 已提交
431 432 433 434 435 436
		printk(KERN_CRIT "NMI: caused by motherboard (%s)\n",
			mca_dev->name);
	}

	/* More info available in POS 6 and 7? */

437
	if (check_flag) {
L
Linus Torvalds 已提交
438 439 440 441 442 443 444 445 446 447 448 449
		unsigned char pos6, pos7;

		pos6 = mca_device_read_pos(mca_dev, 6);
		pos7 = mca_device_read_pos(mca_dev, 7);

		printk(KERN_CRIT "NMI: POS 6 = 0x%x, POS 7 = 0x%x\n", pos6, pos7);
	}

} /* mca_handle_nmi_slot */

/*--------------------------------------------------------------------*/

450
static int __kprobes mca_handle_nmi_callback(struct device *dev, void *data)
L
Linus Torvalds 已提交
451 452 453 454 455 456
{
	struct mca_device *mca_dev = to_mca_device(dev);
	unsigned char pos5;

	pos5 = mca_device_read_pos(mca_dev, 5);

457 458 459
	if (!(pos5 & 0x80)) {
		/*
		 *  Bit 7 of POS 5 is reset when this adapter has a hardware
L
Linus Torvalds 已提交
460 461 462 463 464 465 466 467 468
		 * error. Bit 7 it reset if there's error information
		 * available in POS 6 and 7.
		 */
		mca_handle_nmi_device(mca_dev, !(pos5 & 0x40));
		return 1;
	}
	return 0;
}

469
void __kprobes mca_handle_nmi(void)
L
Linus Torvalds 已提交
470
{
471 472
	/*
	 *  First try - scan the various adapters and see if a specific
L
Linus Torvalds 已提交
473 474 475
	 * adapter was responsible for the error.
	 */
	bus_for_each_dev(&mca_bus_type, NULL, NULL, mca_handle_nmi_callback);
476
}