[go: nahoru, domu]

11da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
21da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * This file is subject to the terms and conditions of the GNU General Public
31da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * License.  See the file "COPYING" in the main directory of this archive
41da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * for more details.
51da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
6107d5a72f2c6a6819b66eebcb0281c7a67b6baaaBrent Casavant * Copyright (C) 2005-2006 Silicon Graphics, Inc.  All Rights Reserved.
71da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
81da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
922329b511a97557b293583194037d1f4c71e1504Brent Casavant/* This file contains the master driver module for use by SGI IOC4 subdrivers.
1022329b511a97557b293583194037d1f4c71e1504Brent Casavant *
1122329b511a97557b293583194037d1f4c71e1504Brent Casavant * It allocates any resources shared between multiple subdevices, and
1222329b511a97557b293583194037d1f4c71e1504Brent Casavant * provides accessor functions (where needed) and the like for those
1322329b511a97557b293583194037d1f4c71e1504Brent Casavant * resources.  It also provides a mechanism for the subdevice modules
1422329b511a97557b293583194037d1f4c71e1504Brent Casavant * to support loading and unloading.
1522329b511a97557b293583194037d1f4c71e1504Brent Casavant *
1622329b511a97557b293583194037d1f4c71e1504Brent Casavant * Non-shared resources (e.g. external interrupt A_INT_OUT register page
1722329b511a97557b293583194037d1f4c71e1504Brent Casavant * alias, serial port and UART registers) are handled by the subdevice
1822329b511a97557b293583194037d1f4c71e1504Brent Casavant * modules themselves.
1922329b511a97557b293583194037d1f4c71e1504Brent Casavant *
2022329b511a97557b293583194037d1f4c71e1504Brent Casavant * This is all necessary because IOC4 is not implemented as a multi-function
2122329b511a97557b293583194037d1f4c71e1504Brent Casavant * PCI device, but an amalgamation of disparate registers for several
2222329b511a97557b293583194037d1f4c71e1504Brent Casavant * types of device (ATA, serial, external interrupts).  The normal
2322329b511a97557b293583194037d1f4c71e1504Brent Casavant * resource management in the kernel doesn't have quite the right interfaces
2422329b511a97557b293583194037d1f4c71e1504Brent Casavant * to handle this situation (e.g. multiple modules can't claim the same
2522329b511a97557b293583194037d1f4c71e1504Brent Casavant * PCI ID), thus this IOC4 master module.
261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/errno.h>
291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/module.h>
301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/pci.h>
3122329b511a97557b293583194037d1f4c71e1504Brent Casavant#include <linux/ioc4.h>
32107d5a72f2c6a6819b66eebcb0281c7a67b6baaaBrent Casavant#include <linux/ktime.h>
335a0e3ad6af8660be21ca98a971cd00f331318c05Tejun Heo#include <linux/slab.h>
346e586f32931d6c98431d54cd0430d4366195b0baJes Sorensen#include <linux/mutex.h>
35107d5a72f2c6a6819b66eebcb0281c7a67b6baaaBrent Casavant#include <linux/time.h>
362099c99e3b24f86b131566aa9854249189ae9ea2Al Viro#include <asm/io.h>
37d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant
38d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant/***************
39d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant * Definitions *
40d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant ***************/
41d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant
42d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant/* Tweakable values */
43d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant
44d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant/* PCI bus speed detection/calibration */
45107d5a72f2c6a6819b66eebcb0281c7a67b6baaaBrent Casavant#define IOC4_CALIBRATE_COUNT 63		/* Calibration cycle period */
46d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant#define IOC4_CALIBRATE_CYCLES 256	/* Average over this many cycles */
47d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant#define IOC4_CALIBRATE_DISCARD 2	/* Discard first few cycles */
48d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant#define IOC4_CALIBRATE_LOW_MHZ 25	/* Lower bound on bus speed sanity */
49d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant#define IOC4_CALIBRATE_HIGH_MHZ 75	/* Upper bound on bus speed sanity */
50d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant#define IOC4_CALIBRATE_DEFAULT_MHZ 66	/* Assumed if sanity check fails */
511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5222329b511a97557b293583194037d1f4c71e1504Brent Casavant/************************
5322329b511a97557b293583194037d1f4c71e1504Brent Casavant * Submodule management *
5422329b511a97557b293583194037d1f4c71e1504Brent Casavant ************************/
551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
566e586f32931d6c98431d54cd0430d4366195b0baJes Sorensenstatic DEFINE_MUTEX(ioc4_mutex);
5722329b511a97557b293583194037d1f4c71e1504Brent Casavant
586e586f32931d6c98431d54cd0430d4366195b0baJes Sorensenstatic LIST_HEAD(ioc4_devices);
5922329b511a97557b293583194037d1f4c71e1504Brent Casavantstatic LIST_HEAD(ioc4_submodules);
6022329b511a97557b293583194037d1f4c71e1504Brent Casavant
6122329b511a97557b293583194037d1f4c71e1504Brent Casavant/* Register an IOC4 submodule */
6222329b511a97557b293583194037d1f4c71e1504Brent Casavantint
6322329b511a97557b293583194037d1f4c71e1504Brent Casavantioc4_register_submodule(struct ioc4_submodule *is)
6422329b511a97557b293583194037d1f4c71e1504Brent Casavant{
6522329b511a97557b293583194037d1f4c71e1504Brent Casavant	struct ioc4_driver_data *idd;
6622329b511a97557b293583194037d1f4c71e1504Brent Casavant
676e586f32931d6c98431d54cd0430d4366195b0baJes Sorensen	mutex_lock(&ioc4_mutex);
6822329b511a97557b293583194037d1f4c71e1504Brent Casavant	list_add(&is->is_list, &ioc4_submodules);
6922329b511a97557b293583194037d1f4c71e1504Brent Casavant
7022329b511a97557b293583194037d1f4c71e1504Brent Casavant	/* Initialize submodule for each IOC4 */
7122329b511a97557b293583194037d1f4c71e1504Brent Casavant	if (!is->is_probe)
726e586f32931d6c98431d54cd0430d4366195b0baJes Sorensen		goto out;
7322329b511a97557b293583194037d1f4c71e1504Brent Casavant
7422329b511a97557b293583194037d1f4c71e1504Brent Casavant	list_for_each_entry(idd, &ioc4_devices, idd_list) {
7522329b511a97557b293583194037d1f4c71e1504Brent Casavant		if (is->is_probe(idd)) {
7622329b511a97557b293583194037d1f4c71e1504Brent Casavant			printk(KERN_WARNING
7722329b511a97557b293583194037d1f4c71e1504Brent Casavant			       "%s: IOC4 submodule %s probe failed "
7822329b511a97557b293583194037d1f4c71e1504Brent Casavant			       "for pci_dev %s",
796e574195b75543bc6a6240306313988b1952470cHarvey Harrison			       __func__, module_name(is->is_owner),
8022329b511a97557b293583194037d1f4c71e1504Brent Casavant			       pci_name(idd->idd_pdev));
8122329b511a97557b293583194037d1f4c71e1504Brent Casavant		}
8222329b511a97557b293583194037d1f4c71e1504Brent Casavant	}
836e586f32931d6c98431d54cd0430d4366195b0baJes Sorensen out:
846e586f32931d6c98431d54cd0430d4366195b0baJes Sorensen	mutex_unlock(&ioc4_mutex);
8522329b511a97557b293583194037d1f4c71e1504Brent Casavant	return 0;
8622329b511a97557b293583194037d1f4c71e1504Brent Casavant}
8722329b511a97557b293583194037d1f4c71e1504Brent Casavant
8822329b511a97557b293583194037d1f4c71e1504Brent Casavant/* Unregister an IOC4 submodule */
8922329b511a97557b293583194037d1f4c71e1504Brent Casavantvoid
9022329b511a97557b293583194037d1f4c71e1504Brent Casavantioc4_unregister_submodule(struct ioc4_submodule *is)
911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
9222329b511a97557b293583194037d1f4c71e1504Brent Casavant	struct ioc4_driver_data *idd;
9322329b511a97557b293583194037d1f4c71e1504Brent Casavant
946e586f32931d6c98431d54cd0430d4366195b0baJes Sorensen	mutex_lock(&ioc4_mutex);
9522329b511a97557b293583194037d1f4c71e1504Brent Casavant	list_del(&is->is_list);
9622329b511a97557b293583194037d1f4c71e1504Brent Casavant
9722329b511a97557b293583194037d1f4c71e1504Brent Casavant	/* Remove submodule for each IOC4 */
9822329b511a97557b293583194037d1f4c71e1504Brent Casavant	if (!is->is_remove)
996e586f32931d6c98431d54cd0430d4366195b0baJes Sorensen		goto out;
10022329b511a97557b293583194037d1f4c71e1504Brent Casavant
10122329b511a97557b293583194037d1f4c71e1504Brent Casavant	list_for_each_entry(idd, &ioc4_devices, idd_list) {
10222329b511a97557b293583194037d1f4c71e1504Brent Casavant		if (is->is_remove(idd)) {
10322329b511a97557b293583194037d1f4c71e1504Brent Casavant			printk(KERN_WARNING
10422329b511a97557b293583194037d1f4c71e1504Brent Casavant			       "%s: IOC4 submodule %s remove failed "
10522329b511a97557b293583194037d1f4c71e1504Brent Casavant			       "for pci_dev %s.\n",
1066e574195b75543bc6a6240306313988b1952470cHarvey Harrison			       __func__, module_name(is->is_owner),
10722329b511a97557b293583194037d1f4c71e1504Brent Casavant			       pci_name(idd->idd_pdev));
10822329b511a97557b293583194037d1f4c71e1504Brent Casavant		}
10922329b511a97557b293583194037d1f4c71e1504Brent Casavant	}
1106e586f32931d6c98431d54cd0430d4366195b0baJes Sorensen out:
1116e586f32931d6c98431d54cd0430d4366195b0baJes Sorensen	mutex_unlock(&ioc4_mutex);
11222329b511a97557b293583194037d1f4c71e1504Brent Casavant}
11322329b511a97557b293583194037d1f4c71e1504Brent Casavant
11422329b511a97557b293583194037d1f4c71e1504Brent Casavant/*********************
11522329b511a97557b293583194037d1f4c71e1504Brent Casavant * Device management *
11622329b511a97557b293583194037d1f4c71e1504Brent Casavant *********************/
11722329b511a97557b293583194037d1f4c71e1504Brent Casavant
118d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant#define IOC4_CALIBRATE_LOW_LIMIT \
119d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant	(1000*IOC4_EXTINT_COUNT_DIVISOR/IOC4_CALIBRATE_LOW_MHZ)
120d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant#define IOC4_CALIBRATE_HIGH_LIMIT \
121d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant	(1000*IOC4_EXTINT_COUNT_DIVISOR/IOC4_CALIBRATE_HIGH_MHZ)
122d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant#define IOC4_CALIBRATE_DEFAULT \
123d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant	(1000*IOC4_EXTINT_COUNT_DIVISOR/IOC4_CALIBRATE_DEFAULT_MHZ)
124d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant
125d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant#define IOC4_CALIBRATE_END \
126d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant	(IOC4_CALIBRATE_CYCLES + IOC4_CALIBRATE_DISCARD)
127d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant
128d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant#define IOC4_INT_OUT_MODE_TOGGLE 0x7	/* Toggle INT_OUT every COUNT+1 ticks */
129d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant
130d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant/* Determines external interrupt output clock period of the PCI bus an
131d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant * IOC4 is attached to.  This value can be used to determine the PCI
132d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant * bus speed.
133d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant *
134d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant * IOC4 has a design feature that various internal timers are derived from
135d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant * the PCI bus clock.  This causes IOC4 device drivers to need to take the
136d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant * bus speed into account when setting various register values (e.g. INT_OUT
137d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant * register COUNT field, UART divisors, etc).  Since this information is
138d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant * needed by several subdrivers, it is determined by the main IOC4 driver,
139d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant * even though the following code utilizes external interrupt registers
140d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant * to perform the speed calculation.
141d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant */
14280c8ae289266529445fad030fabf5fcf01ccda0dBill Pembertonstatic void
143d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavantioc4_clock_calibrate(struct ioc4_driver_data *idd)
144d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant{
145d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant	union ioc4_int_out int_out;
146d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant	union ioc4_gpcr gpcr;
147d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant	unsigned int state, last_state = 1;
148107d5a72f2c6a6819b66eebcb0281c7a67b6baaaBrent Casavant	uint64_t start, end, period;
149d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant	unsigned int count = 0;
150d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant
151d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant	/* Enable output */
152d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant	gpcr.raw = 0;
153d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant	gpcr.fields.dir = IOC4_GPCR_DIR_0;
154d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant	gpcr.fields.int_out_en = 1;
155d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant	writel(gpcr.raw, &idd->idd_misc_regs->gpcr_s.raw);
156d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant
157d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant	/* Reset to power-on state */
158d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant	writel(0, &idd->idd_misc_regs->int_out.raw);
159d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant	mmiowb();
160d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant
161d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant	/* Set up square wave */
162d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant	int_out.raw = 0;
163d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant	int_out.fields.count = IOC4_CALIBRATE_COUNT;
164d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant	int_out.fields.mode = IOC4_INT_OUT_MODE_TOGGLE;
165d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant	int_out.fields.diag = 0;
166d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant	writel(int_out.raw, &idd->idd_misc_regs->int_out.raw);
167d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant	mmiowb();
168d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant
169d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant	/* Check square wave period averaged over some number of cycles */
170d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant	do {
171d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant		int_out.raw = readl(&idd->idd_misc_regs->int_out.raw);
172d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant		state = int_out.fields.int_out;
173d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant		if (!last_state && state) {
174d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant			count++;
175d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant			if (count == IOC4_CALIBRATE_END) {
1766d9b757c6ce850d0fb61d31f48e7bae56240643bThomas Gleixner				end = ktime_get_ns();
177d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant				break;
178d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant			} else if (count == IOC4_CALIBRATE_DISCARD)
1796d9b757c6ce850d0fb61d31f48e7bae56240643bThomas Gleixner				start = ktime_get_ns();
180d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant		}
181d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant		last_state = state;
182d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant	} while (1);
183d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant
184d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant	/* Calculation rearranged to preserve intermediate precision.
185d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant	 * Logically:
186107d5a72f2c6a6819b66eebcb0281c7a67b6baaaBrent Casavant	 * 1. "end - start" gives us the measurement period over all
187107d5a72f2c6a6819b66eebcb0281c7a67b6baaaBrent Casavant	 *    the square wave cycles.
188107d5a72f2c6a6819b66eebcb0281c7a67b6baaaBrent Casavant	 * 2. Divide by number of square wave cycles to get the period
189107d5a72f2c6a6819b66eebcb0281c7a67b6baaaBrent Casavant	 *    of a square wave cycle.
190d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant	 * 3. Divide by 2*(int_out.fields.count+1), which is the formula
191d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant	 *    by which the IOC4 generates the square wave, to get the
192107d5a72f2c6a6819b66eebcb0281c7a67b6baaaBrent Casavant	 *    period of an IOC4 INT_OUT count.
193d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant	 */
194107d5a72f2c6a6819b66eebcb0281c7a67b6baaaBrent Casavant	period = (end - start) /
195107d5a72f2c6a6819b66eebcb0281c7a67b6baaaBrent Casavant		(IOC4_CALIBRATE_CYCLES * 2 * (IOC4_CALIBRATE_COUNT + 1));
196d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant
197d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant	/* Bounds check the result. */
198d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant	if (period > IOC4_CALIBRATE_LOW_LIMIT ||
199d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant	    period < IOC4_CALIBRATE_HIGH_LIMIT) {
200f5befceb5cfecba49fdf61f8e0eb4d453200eac9Brent Casavant		printk(KERN_INFO
201f5befceb5cfecba49fdf61f8e0eb4d453200eac9Brent Casavant		       "IOC4 %s: Clock calibration failed.  Assuming"
202f5befceb5cfecba49fdf61f8e0eb4d453200eac9Brent Casavant		       "PCI clock is %d ns.\n",
203f5befceb5cfecba49fdf61f8e0eb4d453200eac9Brent Casavant		       pci_name(idd->idd_pdev),
204d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant		       IOC4_CALIBRATE_DEFAULT / IOC4_EXTINT_COUNT_DIVISOR);
205d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant		period = IOC4_CALIBRATE_DEFAULT;
206d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant	} else {
20759f148005cfd3d41537a4b872c266213d5fe4dc6Brent Casavant		u64 ns = period;
20859f148005cfd3d41537a4b872c266213d5fe4dc6Brent Casavant
20959f148005cfd3d41537a4b872c266213d5fe4dc6Brent Casavant		do_div(ns, IOC4_EXTINT_COUNT_DIVISOR);
210f5befceb5cfecba49fdf61f8e0eb4d453200eac9Brent Casavant		printk(KERN_DEBUG
211760fe9ad1692361770bb56fa5c69cf6b3354858cRandy Dunlap		       "IOC4 %s: PCI clock is %llu ns.\n",
212760fe9ad1692361770bb56fa5c69cf6b3354858cRandy Dunlap		       pci_name(idd->idd_pdev), (unsigned long long)ns);
213d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant	}
214d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant
215d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant	/* Remember results.  We store the extint clock period rather
216d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant	 * than the PCI clock period so that greater precision is
217d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant	 * retained.  Divide by IOC4_EXTINT_COUNT_DIVISOR to get
218d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant	 * PCI clock period.
219d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant	 */
220d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant	idd->count_period = period;
221d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant}
222d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant
223f5befceb5cfecba49fdf61f8e0eb4d453200eac9Brent Casavant/* There are three variants of IOC4 cards: IO9, IO10, and PCI-RT.
224f5befceb5cfecba49fdf61f8e0eb4d453200eac9Brent Casavant * Each brings out different combinations of IOC4 signals, thus.
225f5befceb5cfecba49fdf61f8e0eb4d453200eac9Brent Casavant * the IOC4 subdrivers need to know to which we're attached.
226f5befceb5cfecba49fdf61f8e0eb4d453200eac9Brent Casavant *
227f5befceb5cfecba49fdf61f8e0eb4d453200eac9Brent Casavant * We look for the presence of a SCSI (IO9) or SATA (IO10) controller
228f5befceb5cfecba49fdf61f8e0eb4d453200eac9Brent Casavant * on the same PCI bus at slot number 3 to differentiate IO9 from IO10.
229f5befceb5cfecba49fdf61f8e0eb4d453200eac9Brent Casavant * If neither is present, it's a PCI-RT.
230f5befceb5cfecba49fdf61f8e0eb4d453200eac9Brent Casavant */
23180c8ae289266529445fad030fabf5fcf01ccda0dBill Pembertonstatic unsigned int
232f5befceb5cfecba49fdf61f8e0eb4d453200eac9Brent Casavantioc4_variant(struct ioc4_driver_data *idd)
233f5befceb5cfecba49fdf61f8e0eb4d453200eac9Brent Casavant{
234f5befceb5cfecba49fdf61f8e0eb4d453200eac9Brent Casavant	struct pci_dev *pdev = NULL;
235f5befceb5cfecba49fdf61f8e0eb4d453200eac9Brent Casavant	int found = 0;
236f5befceb5cfecba49fdf61f8e0eb4d453200eac9Brent Casavant
237f5befceb5cfecba49fdf61f8e0eb4d453200eac9Brent Casavant	/* IO9: Look for a QLogic ISP 12160 at the same bus and slot 3. */
238f5befceb5cfecba49fdf61f8e0eb4d453200eac9Brent Casavant	do {
239f5befceb5cfecba49fdf61f8e0eb4d453200eac9Brent Casavant		pdev = pci_get_device(PCI_VENDOR_ID_QLOGIC,
240f5befceb5cfecba49fdf61f8e0eb4d453200eac9Brent Casavant				      PCI_DEVICE_ID_QLOGIC_ISP12160, pdev);
241f5befceb5cfecba49fdf61f8e0eb4d453200eac9Brent Casavant		if (pdev &&
242f5befceb5cfecba49fdf61f8e0eb4d453200eac9Brent Casavant		    idd->idd_pdev->bus->number == pdev->bus->number &&
243f5befceb5cfecba49fdf61f8e0eb4d453200eac9Brent Casavant		    3 == PCI_SLOT(pdev->devfn))
244f5befceb5cfecba49fdf61f8e0eb4d453200eac9Brent Casavant			found = 1;
245f5befceb5cfecba49fdf61f8e0eb4d453200eac9Brent Casavant	} while (pdev && !found);
24690d8dabf74179e6615bd4688a118e12ec29ab7aaJulia Lawall	if (NULL != pdev) {
24790d8dabf74179e6615bd4688a118e12ec29ab7aaJulia Lawall		pci_dev_put(pdev);
248f5befceb5cfecba49fdf61f8e0eb4d453200eac9Brent Casavant		return IOC4_VARIANT_IO9;
24990d8dabf74179e6615bd4688a118e12ec29ab7aaJulia Lawall	}
250f5befceb5cfecba49fdf61f8e0eb4d453200eac9Brent Casavant
251f5befceb5cfecba49fdf61f8e0eb4d453200eac9Brent Casavant	/* IO10: Look for a Vitesse VSC 7174 at the same bus and slot 3. */
252f5befceb5cfecba49fdf61f8e0eb4d453200eac9Brent Casavant	pdev = NULL;
253f5befceb5cfecba49fdf61f8e0eb4d453200eac9Brent Casavant	do {
254f5befceb5cfecba49fdf61f8e0eb4d453200eac9Brent Casavant		pdev = pci_get_device(PCI_VENDOR_ID_VITESSE,
255f5befceb5cfecba49fdf61f8e0eb4d453200eac9Brent Casavant				      PCI_DEVICE_ID_VITESSE_VSC7174, pdev);
256f5befceb5cfecba49fdf61f8e0eb4d453200eac9Brent Casavant		if (pdev &&
257f5befceb5cfecba49fdf61f8e0eb4d453200eac9Brent Casavant		    idd->idd_pdev->bus->number == pdev->bus->number &&
258f5befceb5cfecba49fdf61f8e0eb4d453200eac9Brent Casavant		    3 == PCI_SLOT(pdev->devfn))
259f5befceb5cfecba49fdf61f8e0eb4d453200eac9Brent Casavant			found = 1;
260f5befceb5cfecba49fdf61f8e0eb4d453200eac9Brent Casavant	} while (pdev && !found);
26190d8dabf74179e6615bd4688a118e12ec29ab7aaJulia Lawall	if (NULL != pdev) {
26290d8dabf74179e6615bd4688a118e12ec29ab7aaJulia Lawall		pci_dev_put(pdev);
263f5befceb5cfecba49fdf61f8e0eb4d453200eac9Brent Casavant		return IOC4_VARIANT_IO10;
26490d8dabf74179e6615bd4688a118e12ec29ab7aaJulia Lawall	}
265f5befceb5cfecba49fdf61f8e0eb4d453200eac9Brent Casavant
266f5befceb5cfecba49fdf61f8e0eb4d453200eac9Brent Casavant	/* PCI-RT: No SCSI/SATA controller will be present */
267f5befceb5cfecba49fdf61f8e0eb4d453200eac9Brent Casavant	return IOC4_VARIANT_PCI_RT;
268f5befceb5cfecba49fdf61f8e0eb4d453200eac9Brent Casavant}
269f5befceb5cfecba49fdf61f8e0eb4d453200eac9Brent Casavant
2701fc6e987d8f606371337211f52ff74c6753298a6Ralf Baechlestatic void
27108adefd4791772d8b3fe23cc9d2554123e21dfa3Brent Casavantioc4_load_modules(struct work_struct *work)
27208adefd4791772d8b3fe23cc9d2554123e21dfa3Brent Casavant{
27308adefd4791772d8b3fe23cc9d2554123e21dfa3Brent Casavant	request_module("sgiioc4");
27408adefd4791772d8b3fe23cc9d2554123e21dfa3Brent Casavant}
27508adefd4791772d8b3fe23cc9d2554123e21dfa3Brent Casavant
276883624a08cb4144343e7362d9fff0e2c69613ebfTejun Heostatic DECLARE_WORK(ioc4_load_modules_work, ioc4_load_modules);
277883624a08cb4144343e7362d9fff0e2c69613ebfTejun Heo
27822329b511a97557b293583194037d1f4c71e1504Brent Casavant/* Adds a new instance of an IOC4 card */
27980c8ae289266529445fad030fabf5fcf01ccda0dBill Pembertonstatic int
28022329b511a97557b293583194037d1f4c71e1504Brent Casavantioc4_probe(struct pci_dev *pdev, const struct pci_device_id *pci_id)
28122329b511a97557b293583194037d1f4c71e1504Brent Casavant{
28222329b511a97557b293583194037d1f4c71e1504Brent Casavant	struct ioc4_driver_data *idd;
28322329b511a97557b293583194037d1f4c71e1504Brent Casavant	struct ioc4_submodule *is;
28422329b511a97557b293583194037d1f4c71e1504Brent Casavant	uint32_t pcmd;
2851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int ret;
2861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
28722329b511a97557b293583194037d1f4c71e1504Brent Casavant	/* Enable IOC4 and take ownership of it */
2881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if ((ret = pci_enable_device(pdev))) {
2891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		printk(KERN_WARNING
29022329b511a97557b293583194037d1f4c71e1504Brent Casavant		       "%s: Failed to enable IOC4 device for pci_dev %s.\n",
2916e574195b75543bc6a6240306313988b1952470cHarvey Harrison		       __func__, pci_name(pdev));
29222329b511a97557b293583194037d1f4c71e1504Brent Casavant		goto out;
2931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
2941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	pci_set_master(pdev);
2951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
29622329b511a97557b293583194037d1f4c71e1504Brent Casavant	/* Set up per-IOC4 data */
29722329b511a97557b293583194037d1f4c71e1504Brent Casavant	idd = kmalloc(sizeof(struct ioc4_driver_data), GFP_KERNEL);
29822329b511a97557b293583194037d1f4c71e1504Brent Casavant	if (!idd) {
29922329b511a97557b293583194037d1f4c71e1504Brent Casavant		printk(KERN_WARNING
30022329b511a97557b293583194037d1f4c71e1504Brent Casavant		       "%s: Failed to allocate IOC4 data for pci_dev %s.\n",
3016e574195b75543bc6a6240306313988b1952470cHarvey Harrison		       __func__, pci_name(pdev));
30222329b511a97557b293583194037d1f4c71e1504Brent Casavant		ret = -ENODEV;
30322329b511a97557b293583194037d1f4c71e1504Brent Casavant		goto out_idd;
30422329b511a97557b293583194037d1f4c71e1504Brent Casavant	}
30522329b511a97557b293583194037d1f4c71e1504Brent Casavant	idd->idd_pdev = pdev;
30622329b511a97557b293583194037d1f4c71e1504Brent Casavant	idd->idd_pci_id = pci_id;
30722329b511a97557b293583194037d1f4c71e1504Brent Casavant
30822329b511a97557b293583194037d1f4c71e1504Brent Casavant	/* Map IOC4 misc registers.  These are shared between subdevices
30922329b511a97557b293583194037d1f4c71e1504Brent Casavant	 * so the main IOC4 module manages them.
31022329b511a97557b293583194037d1f4c71e1504Brent Casavant	 */
31122329b511a97557b293583194037d1f4c71e1504Brent Casavant	idd->idd_bar0 = pci_resource_start(idd->idd_pdev, 0);
31222329b511a97557b293583194037d1f4c71e1504Brent Casavant	if (!idd->idd_bar0) {
31322329b511a97557b293583194037d1f4c71e1504Brent Casavant		printk(KERN_WARNING
31422329b511a97557b293583194037d1f4c71e1504Brent Casavant		       "%s: Unable to find IOC4 misc resource "
31522329b511a97557b293583194037d1f4c71e1504Brent Casavant		       "for pci_dev %s.\n",
3166e574195b75543bc6a6240306313988b1952470cHarvey Harrison		       __func__, pci_name(idd->idd_pdev));
31722329b511a97557b293583194037d1f4c71e1504Brent Casavant		ret = -ENODEV;
31822329b511a97557b293583194037d1f4c71e1504Brent Casavant		goto out_pci;
31922329b511a97557b293583194037d1f4c71e1504Brent Casavant	}
32052c9ae0ac7576c94f6a2371b44039e7ba12a0439Brent Casavant	if (!request_mem_region(idd->idd_bar0, sizeof(struct ioc4_misc_regs),
32122329b511a97557b293583194037d1f4c71e1504Brent Casavant			    "ioc4_misc")) {
32222329b511a97557b293583194037d1f4c71e1504Brent Casavant		printk(KERN_WARNING
32322329b511a97557b293583194037d1f4c71e1504Brent Casavant		       "%s: Unable to request IOC4 misc region "
32422329b511a97557b293583194037d1f4c71e1504Brent Casavant		       "for pci_dev %s.\n",
3256e574195b75543bc6a6240306313988b1952470cHarvey Harrison		       __func__, pci_name(idd->idd_pdev));
32622329b511a97557b293583194037d1f4c71e1504Brent Casavant		ret = -ENODEV;
32722329b511a97557b293583194037d1f4c71e1504Brent Casavant		goto out_pci;
32822329b511a97557b293583194037d1f4c71e1504Brent Casavant	}
32922329b511a97557b293583194037d1f4c71e1504Brent Casavant	idd->idd_misc_regs = ioremap(idd->idd_bar0,
33022329b511a97557b293583194037d1f4c71e1504Brent Casavant				     sizeof(struct ioc4_misc_regs));
33122329b511a97557b293583194037d1f4c71e1504Brent Casavant	if (!idd->idd_misc_regs) {
33222329b511a97557b293583194037d1f4c71e1504Brent Casavant		printk(KERN_WARNING
33322329b511a97557b293583194037d1f4c71e1504Brent Casavant		       "%s: Unable to remap IOC4 misc region "
33422329b511a97557b293583194037d1f4c71e1504Brent Casavant		       "for pci_dev %s.\n",
3356e574195b75543bc6a6240306313988b1952470cHarvey Harrison		       __func__, pci_name(idd->idd_pdev));
33622329b511a97557b293583194037d1f4c71e1504Brent Casavant		ret = -ENODEV;
33722329b511a97557b293583194037d1f4c71e1504Brent Casavant		goto out_misc_region;
33822329b511a97557b293583194037d1f4c71e1504Brent Casavant	}
33922329b511a97557b293583194037d1f4c71e1504Brent Casavant
34022329b511a97557b293583194037d1f4c71e1504Brent Casavant	/* Failsafe portion of per-IOC4 initialization */
34122329b511a97557b293583194037d1f4c71e1504Brent Casavant
342f5befceb5cfecba49fdf61f8e0eb4d453200eac9Brent Casavant	/* Detect card variant */
343f5befceb5cfecba49fdf61f8e0eb4d453200eac9Brent Casavant	idd->idd_variant = ioc4_variant(idd);
344f5befceb5cfecba49fdf61f8e0eb4d453200eac9Brent Casavant	printk(KERN_INFO "IOC4 %s: %s card detected.\n", pci_name(pdev),
345f5befceb5cfecba49fdf61f8e0eb4d453200eac9Brent Casavant	       idd->idd_variant == IOC4_VARIANT_IO9 ? "IO9" :
346f5befceb5cfecba49fdf61f8e0eb4d453200eac9Brent Casavant	       idd->idd_variant == IOC4_VARIANT_PCI_RT ? "PCI-RT" :
347f5befceb5cfecba49fdf61f8e0eb4d453200eac9Brent Casavant	       idd->idd_variant == IOC4_VARIANT_IO10 ? "IO10" : "unknown");
348f5befceb5cfecba49fdf61f8e0eb4d453200eac9Brent Casavant
34922329b511a97557b293583194037d1f4c71e1504Brent Casavant	/* Initialize IOC4 */
35022329b511a97557b293583194037d1f4c71e1504Brent Casavant	pci_read_config_dword(idd->idd_pdev, PCI_COMMAND, &pcmd);
35122329b511a97557b293583194037d1f4c71e1504Brent Casavant	pci_write_config_dword(idd->idd_pdev, PCI_COMMAND,
35222329b511a97557b293583194037d1f4c71e1504Brent Casavant			       pcmd | PCI_COMMAND_PARITY | PCI_COMMAND_SERR);
35322329b511a97557b293583194037d1f4c71e1504Brent Casavant
354d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant	/* Determine PCI clock */
355d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant	ioc4_clock_calibrate(idd);
356d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant
35722329b511a97557b293583194037d1f4c71e1504Brent Casavant	/* Disable/clear all interrupts.  Need to do this here lest
35822329b511a97557b293583194037d1f4c71e1504Brent Casavant	 * one submodule request the shared IOC4 IRQ, but interrupt
35922329b511a97557b293583194037d1f4c71e1504Brent Casavant	 * is generated by a different subdevice.
36022329b511a97557b293583194037d1f4c71e1504Brent Casavant	 */
36122329b511a97557b293583194037d1f4c71e1504Brent Casavant	/* Disable */
36222329b511a97557b293583194037d1f4c71e1504Brent Casavant	writel(~0, &idd->idd_misc_regs->other_iec.raw);
36322329b511a97557b293583194037d1f4c71e1504Brent Casavant	writel(~0, &idd->idd_misc_regs->sio_iec);
36422329b511a97557b293583194037d1f4c71e1504Brent Casavant	/* Clear (i.e. acknowledge) */
36522329b511a97557b293583194037d1f4c71e1504Brent Casavant	writel(~0, &idd->idd_misc_regs->other_ir.raw);
36622329b511a97557b293583194037d1f4c71e1504Brent Casavant	writel(~0, &idd->idd_misc_regs->sio_ir);
36722329b511a97557b293583194037d1f4c71e1504Brent Casavant
36822329b511a97557b293583194037d1f4c71e1504Brent Casavant	/* Track PCI-device specific data */
36922329b511a97557b293583194037d1f4c71e1504Brent Casavant	idd->idd_serial_data = NULL;
37022329b511a97557b293583194037d1f4c71e1504Brent Casavant	pci_set_drvdata(idd->idd_pdev, idd);
3716e586f32931d6c98431d54cd0430d4366195b0baJes Sorensen
3726e586f32931d6c98431d54cd0430d4366195b0baJes Sorensen	mutex_lock(&ioc4_mutex);
3738683dc9990158c221e05959935e7dd50a956c574Brent Casavant	list_add_tail(&idd->idd_list, &ioc4_devices);
37422329b511a97557b293583194037d1f4c71e1504Brent Casavant
37522329b511a97557b293583194037d1f4c71e1504Brent Casavant	/* Add this IOC4 to all submodules */
37622329b511a97557b293583194037d1f4c71e1504Brent Casavant	list_for_each_entry(is, &ioc4_submodules, is_list) {
37722329b511a97557b293583194037d1f4c71e1504Brent Casavant		if (is->is_probe && is->is_probe(idd)) {
37822329b511a97557b293583194037d1f4c71e1504Brent Casavant			printk(KERN_WARNING
37922329b511a97557b293583194037d1f4c71e1504Brent Casavant			       "%s: IOC4 submodule 0x%s probe failed "
38022329b511a97557b293583194037d1f4c71e1504Brent Casavant			       "for pci_dev %s.\n",
3816e574195b75543bc6a6240306313988b1952470cHarvey Harrison			       __func__, module_name(is->is_owner),
38222329b511a97557b293583194037d1f4c71e1504Brent Casavant			       pci_name(idd->idd_pdev));
38322329b511a97557b293583194037d1f4c71e1504Brent Casavant		}
38422329b511a97557b293583194037d1f4c71e1504Brent Casavant	}
3856e586f32931d6c98431d54cd0430d4366195b0baJes Sorensen	mutex_unlock(&ioc4_mutex);
38622329b511a97557b293583194037d1f4c71e1504Brent Casavant
38708adefd4791772d8b3fe23cc9d2554123e21dfa3Brent Casavant	/* Request sgiioc4 IDE driver on boards that bring that functionality
38808adefd4791772d8b3fe23cc9d2554123e21dfa3Brent Casavant	 * off of IOC4.  The root filesystem may be hosted on a drive connected
38908adefd4791772d8b3fe23cc9d2554123e21dfa3Brent Casavant	 * to IOC4, so we need to make sure the sgiioc4 driver is loaded as it
39008adefd4791772d8b3fe23cc9d2554123e21dfa3Brent Casavant	 * won't be picked up by modprobes due to the ioc4 module owning the
39108adefd4791772d8b3fe23cc9d2554123e21dfa3Brent Casavant	 * PCI device.
39208adefd4791772d8b3fe23cc9d2554123e21dfa3Brent Casavant	 */
39308adefd4791772d8b3fe23cc9d2554123e21dfa3Brent Casavant	if (idd->idd_variant != IOC4_VARIANT_PCI_RT) {
394883624a08cb4144343e7362d9fff0e2c69613ebfTejun Heo		/* Request the module from a work procedure as the modprobe
395883624a08cb4144343e7362d9fff0e2c69613ebfTejun Heo		 * goes out to a userland helper and that will hang if done
396883624a08cb4144343e7362d9fff0e2c69613ebfTejun Heo		 * directly from ioc4_probe().
397883624a08cb4144343e7362d9fff0e2c69613ebfTejun Heo		 */
398883624a08cb4144343e7362d9fff0e2c69613ebfTejun Heo		printk(KERN_INFO "IOC4 loading sgiioc4 submodule\n");
399883624a08cb4144343e7362d9fff0e2c69613ebfTejun Heo		schedule_work(&ioc4_load_modules_work);
40008adefd4791772d8b3fe23cc9d2554123e21dfa3Brent Casavant	}
40108adefd4791772d8b3fe23cc9d2554123e21dfa3Brent Casavant
40222329b511a97557b293583194037d1f4c71e1504Brent Casavant	return 0;
40322329b511a97557b293583194037d1f4c71e1504Brent Casavant
40422329b511a97557b293583194037d1f4c71e1504Brent Casavantout_misc_region:
40552c9ae0ac7576c94f6a2371b44039e7ba12a0439Brent Casavant	release_mem_region(idd->idd_bar0, sizeof(struct ioc4_misc_regs));
40622329b511a97557b293583194037d1f4c71e1504Brent Casavantout_pci:
40722329b511a97557b293583194037d1f4c71e1504Brent Casavant	kfree(idd);
40822329b511a97557b293583194037d1f4c71e1504Brent Casavantout_idd:
40922329b511a97557b293583194037d1f4c71e1504Brent Casavant	pci_disable_device(pdev);
41022329b511a97557b293583194037d1f4c71e1504Brent Casavantout:
41122329b511a97557b293583194037d1f4c71e1504Brent Casavant	return ret;
4121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
41422329b511a97557b293583194037d1f4c71e1504Brent Casavant/* Removes a particular instance of an IOC4 card. */
415486a5c28c2e7d6a80c393ac7d612b77d80447b84Bill Pembertonstatic void
41622329b511a97557b293583194037d1f4c71e1504Brent Casavantioc4_remove(struct pci_dev *pdev)
41722329b511a97557b293583194037d1f4c71e1504Brent Casavant{
41822329b511a97557b293583194037d1f4c71e1504Brent Casavant	struct ioc4_submodule *is;
41922329b511a97557b293583194037d1f4c71e1504Brent Casavant	struct ioc4_driver_data *idd;
42022329b511a97557b293583194037d1f4c71e1504Brent Casavant
42122329b511a97557b293583194037d1f4c71e1504Brent Casavant	idd = pci_get_drvdata(pdev);
42222329b511a97557b293583194037d1f4c71e1504Brent Casavant
42322329b511a97557b293583194037d1f4c71e1504Brent Casavant	/* Remove this IOC4 from all submodules */
4246e586f32931d6c98431d54cd0430d4366195b0baJes Sorensen	mutex_lock(&ioc4_mutex);
42522329b511a97557b293583194037d1f4c71e1504Brent Casavant	list_for_each_entry(is, &ioc4_submodules, is_list) {
42622329b511a97557b293583194037d1f4c71e1504Brent Casavant		if (is->is_remove && is->is_remove(idd)) {
42722329b511a97557b293583194037d1f4c71e1504Brent Casavant			printk(KERN_WARNING
42822329b511a97557b293583194037d1f4c71e1504Brent Casavant			       "%s: IOC4 submodule 0x%s remove failed "
42922329b511a97557b293583194037d1f4c71e1504Brent Casavant			       "for pci_dev %s.\n",
4306e574195b75543bc6a6240306313988b1952470cHarvey Harrison			       __func__, module_name(is->is_owner),
43122329b511a97557b293583194037d1f4c71e1504Brent Casavant			       pci_name(idd->idd_pdev));
43222329b511a97557b293583194037d1f4c71e1504Brent Casavant		}
43322329b511a97557b293583194037d1f4c71e1504Brent Casavant	}
4346e586f32931d6c98431d54cd0430d4366195b0baJes Sorensen	mutex_unlock(&ioc4_mutex);
43522329b511a97557b293583194037d1f4c71e1504Brent Casavant
43622329b511a97557b293583194037d1f4c71e1504Brent Casavant	/* Release resources */
43722329b511a97557b293583194037d1f4c71e1504Brent Casavant	iounmap(idd->idd_misc_regs);
43822329b511a97557b293583194037d1f4c71e1504Brent Casavant	if (!idd->idd_bar0) {
43922329b511a97557b293583194037d1f4c71e1504Brent Casavant		printk(KERN_WARNING
44022329b511a97557b293583194037d1f4c71e1504Brent Casavant		       "%s: Unable to get IOC4 misc mapping for pci_dev %s. "
44122329b511a97557b293583194037d1f4c71e1504Brent Casavant		       "Device removal may be incomplete.\n",
4426e574195b75543bc6a6240306313988b1952470cHarvey Harrison		       __func__, pci_name(idd->idd_pdev));
44322329b511a97557b293583194037d1f4c71e1504Brent Casavant	}
44452c9ae0ac7576c94f6a2371b44039e7ba12a0439Brent Casavant	release_mem_region(idd->idd_bar0, sizeof(struct ioc4_misc_regs));
44522329b511a97557b293583194037d1f4c71e1504Brent Casavant
44622329b511a97557b293583194037d1f4c71e1504Brent Casavant	/* Disable IOC4 and relinquish */
44722329b511a97557b293583194037d1f4c71e1504Brent Casavant	pci_disable_device(pdev);
44822329b511a97557b293583194037d1f4c71e1504Brent Casavant
44922329b511a97557b293583194037d1f4c71e1504Brent Casavant	/* Remove and free driver data */
4506e586f32931d6c98431d54cd0430d4366195b0baJes Sorensen	mutex_lock(&ioc4_mutex);
45122329b511a97557b293583194037d1f4c71e1504Brent Casavant	list_del(&idd->idd_list);
4526e586f32931d6c98431d54cd0430d4366195b0baJes Sorensen	mutex_unlock(&ioc4_mutex);
45322329b511a97557b293583194037d1f4c71e1504Brent Casavant	kfree(idd);
45422329b511a97557b293583194037d1f4c71e1504Brent Casavant}
45522329b511a97557b293583194037d1f4c71e1504Brent Casavant
45622329b511a97557b293583194037d1f4c71e1504Brent Casavantstatic struct pci_device_id ioc4_id_table[] = {
4571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{PCI_VENDOR_ID_SGI, PCI_DEVICE_ID_SGI_IOC4, PCI_ANY_ID,
4581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 PCI_ANY_ID, 0x0b4000, 0xFFFFFF},
4591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{0}
4601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
4611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
46285bd84345f64cb953101ddd8ab1340dde351c579Dave Jonesstatic struct pci_driver ioc4_driver = {
46322329b511a97557b293583194037d1f4c71e1504Brent Casavant	.name = "IOC4",
46422329b511a97557b293583194037d1f4c71e1504Brent Casavant	.id_table = ioc4_id_table,
46522329b511a97557b293583194037d1f4c71e1504Brent Casavant	.probe = ioc4_probe,
4662d6bed9ca93e98685bc5038d686984fd449cd978Bill Pemberton	.remove = ioc4_remove,
4671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
4681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
46922329b511a97557b293583194037d1f4c71e1504Brent CasavantMODULE_DEVICE_TABLE(pci, ioc4_id_table);
47022329b511a97557b293583194037d1f4c71e1504Brent Casavant
47122329b511a97557b293583194037d1f4c71e1504Brent Casavant/*********************
47222329b511a97557b293583194037d1f4c71e1504Brent Casavant * Module management *
47322329b511a97557b293583194037d1f4c71e1504Brent Casavant *********************/
47422329b511a97557b293583194037d1f4c71e1504Brent Casavant
47522329b511a97557b293583194037d1f4c71e1504Brent Casavant/* Module load */
4762ea5d35a49f5c89d1d2d677fe90c71ad5a6278b6Jean Delvarestatic int __init
47722329b511a97557b293583194037d1f4c71e1504Brent Casavantioc4_init(void)
4781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
47922329b511a97557b293583194037d1f4c71e1504Brent Casavant	return pci_register_driver(&ioc4_driver);
48022329b511a97557b293583194037d1f4c71e1504Brent Casavant}
4811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
48222329b511a97557b293583194037d1f4c71e1504Brent Casavant/* Module unload */
4832ea5d35a49f5c89d1d2d677fe90c71ad5a6278b6Jean Delvarestatic void __exit
48422329b511a97557b293583194037d1f4c71e1504Brent Casavantioc4_exit(void)
48522329b511a97557b293583194037d1f4c71e1504Brent Casavant{
48608adefd4791772d8b3fe23cc9d2554123e21dfa3Brent Casavant	/* Ensure ioc4_load_modules() has completed before exiting */
48743829731dd372d04d6706c51052b9dabab9ca356Tejun Heo	flush_work(&ioc4_load_modules_work);
48822329b511a97557b293583194037d1f4c71e1504Brent Casavant	pci_unregister_driver(&ioc4_driver);
4891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
49122329b511a97557b293583194037d1f4c71e1504Brent Casavantmodule_init(ioc4_init);
49222329b511a97557b293583194037d1f4c71e1504Brent Casavantmodule_exit(ioc4_exit);
49322329b511a97557b293583194037d1f4c71e1504Brent Casavant
49422329b511a97557b293583194037d1f4c71e1504Brent CasavantMODULE_AUTHOR("Brent Casavant - Silicon Graphics, Inc. <bcasavan@sgi.com>");
49522329b511a97557b293583194037d1f4c71e1504Brent CasavantMODULE_DESCRIPTION("PCI driver master module for SGI IOC4 Base-IO Card");
4961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_LICENSE("GPL");
49722329b511a97557b293583194037d1f4c71e1504Brent Casavant
49822329b511a97557b293583194037d1f4c71e1504Brent CasavantEXPORT_SYMBOL(ioc4_register_submodule);
49922329b511a97557b293583194037d1f4c71e1504Brent CasavantEXPORT_SYMBOL(ioc4_unregister_submodule);
500