ioc4.c 13.6 KB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
1
2
3
4
5
6
7
8
/*
 * This file is subject to the terms and conditions of the GNU General Public
 * License.  See the file "COPYING" in the main directory of this archive
 * for more details.
 *
 * Copyright (C) 2005 Silicon Graphics, Inc.  All Rights Reserved.
 */

9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/* This file contains the master driver module for use by SGI IOC4 subdrivers.
 *
 * It allocates any resources shared between multiple subdevices, and
 * provides accessor functions (where needed) and the like for those
 * resources.  It also provides a mechanism for the subdevice modules
 * to support loading and unloading.
 *
 * Non-shared resources (e.g. external interrupt A_INT_OUT register page
 * alias, serial port and UART registers) are handled by the subdevice
 * modules themselves.
 *
 * This is all necessary because IOC4 is not implemented as a multi-function
 * PCI device, but an amalgamation of disparate registers for several
 * types of device (ATA, serial, external interrupts).  The normal
 * resource management in the kernel doesn't have quite the right interfaces
 * to handle this situation (e.g. multiple modules can't claim the same
 * PCI ID), thus this IOC4 master module.
Linus Torvalds's avatar
Linus Torvalds committed
26
27
28
29
30
 */

#include <linux/errno.h>
#include <linux/module.h>
#include <linux/pci.h>
31
#include <linux/ioc4.h>
32
33
#include <linux/mmtimer.h>
#include <linux/rtc.h>
Jes Sorensen's avatar
Jes Sorensen committed
34
#include <linux/mutex.h>
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
#include <asm/sn/addrs.h>
#include <asm/sn/clksupport.h>
#include <asm/sn/shub_mmr.h>

/***************
 * Definitions *
 ***************/

/* Tweakable values */

/* PCI bus speed detection/calibration */
#define IOC4_CALIBRATE_COUNT 63	/* Calibration cycle period */
#define IOC4_CALIBRATE_CYCLES 256	/* Average over this many cycles */
#define IOC4_CALIBRATE_DISCARD 2	/* Discard first few cycles */
#define IOC4_CALIBRATE_LOW_MHZ 25	/* Lower bound on bus speed sanity */
#define IOC4_CALIBRATE_HIGH_MHZ 75	/* Upper bound on bus speed sanity */
#define IOC4_CALIBRATE_DEFAULT_MHZ 66	/* Assumed if sanity check fails */
Linus Torvalds's avatar
Linus Torvalds committed
52

53
54
55
/************************
 * Submodule management *
 ************************/
Linus Torvalds's avatar
Linus Torvalds committed
56

Jes Sorensen's avatar
Jes Sorensen committed
57
static DEFINE_MUTEX(ioc4_mutex);
58

Jes Sorensen's avatar
Jes Sorensen committed
59
static LIST_HEAD(ioc4_devices);
60
61
62
63
64
65
66
67
static LIST_HEAD(ioc4_submodules);

/* Register an IOC4 submodule */
int
ioc4_register_submodule(struct ioc4_submodule *is)
{
	struct ioc4_driver_data *idd;

Jes Sorensen's avatar
Jes Sorensen committed
68
	mutex_lock(&ioc4_mutex);
69
70
71
72
	list_add(&is->is_list, &ioc4_submodules);

	/* Initialize submodule for each IOC4 */
	if (!is->is_probe)
Jes Sorensen's avatar
Jes Sorensen committed
73
		goto out;
74
75
76
77
78
79
80
81
82
83

	list_for_each_entry(idd, &ioc4_devices, idd_list) {
		if (is->is_probe(idd)) {
			printk(KERN_WARNING
			       "%s: IOC4 submodule %s probe failed "
			       "for pci_dev %s",
			       __FUNCTION__, module_name(is->is_owner),
			       pci_name(idd->idd_pdev));
		}
	}
Jes Sorensen's avatar
Jes Sorensen committed
84
85
 out:
	mutex_unlock(&ioc4_mutex);
86
87
88
89
90
91
	return 0;
}

/* Unregister an IOC4 submodule */
void
ioc4_unregister_submodule(struct ioc4_submodule *is)
Linus Torvalds's avatar
Linus Torvalds committed
92
{
93
94
	struct ioc4_driver_data *idd;

Jes Sorensen's avatar
Jes Sorensen committed
95
	mutex_lock(&ioc4_mutex);
96
97
98
99
	list_del(&is->is_list);

	/* Remove submodule for each IOC4 */
	if (!is->is_remove)
Jes Sorensen's avatar
Jes Sorensen committed
100
		goto out;
101
102
103
104
105
106
107
108
109
110

	list_for_each_entry(idd, &ioc4_devices, idd_list) {
		if (is->is_remove(idd)) {
			printk(KERN_WARNING
			       "%s: IOC4 submodule %s remove failed "
			       "for pci_dev %s.\n",
			       __FUNCTION__, module_name(is->is_owner),
			       pci_name(idd->idd_pdev));
		}
	}
Jes Sorensen's avatar
Jes Sorensen committed
111
112
 out:
	mutex_unlock(&ioc4_mutex);
113
114
115
116
117
118
}

/*********************
 * Device management *
 *********************/

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
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
#define IOC4_CALIBRATE_LOW_LIMIT \
	(1000*IOC4_EXTINT_COUNT_DIVISOR/IOC4_CALIBRATE_LOW_MHZ)
#define IOC4_CALIBRATE_HIGH_LIMIT \
	(1000*IOC4_EXTINT_COUNT_DIVISOR/IOC4_CALIBRATE_HIGH_MHZ)
#define IOC4_CALIBRATE_DEFAULT \
	(1000*IOC4_EXTINT_COUNT_DIVISOR/IOC4_CALIBRATE_DEFAULT_MHZ)

#define IOC4_CALIBRATE_END \
	(IOC4_CALIBRATE_CYCLES + IOC4_CALIBRATE_DISCARD)

#define IOC4_INT_OUT_MODE_TOGGLE 0x7	/* Toggle INT_OUT every COUNT+1 ticks */

/* Determines external interrupt output clock period of the PCI bus an
 * IOC4 is attached to.  This value can be used to determine the PCI
 * bus speed.
 *
 * IOC4 has a design feature that various internal timers are derived from
 * the PCI bus clock.  This causes IOC4 device drivers to need to take the
 * bus speed into account when setting various register values (e.g. INT_OUT
 * register COUNT field, UART divisors, etc).  Since this information is
 * needed by several subdrivers, it is determined by the main IOC4 driver,
 * even though the following code utilizes external interrupt registers
 * to perform the speed calculation.
 */
static void
ioc4_clock_calibrate(struct ioc4_driver_data *idd)
{
	extern unsigned long sn_rtc_cycles_per_second;
	union ioc4_int_out int_out;
	union ioc4_gpcr gpcr;
	unsigned int state, last_state = 1;
	uint64_t start = 0, end, period;
	unsigned int count = 0;

	/* Enable output */
	gpcr.raw = 0;
	gpcr.fields.dir = IOC4_GPCR_DIR_0;
	gpcr.fields.int_out_en = 1;
	writel(gpcr.raw, &idd->idd_misc_regs->gpcr_s.raw);

	/* Reset to power-on state */
	writel(0, &idd->idd_misc_regs->int_out.raw);
	mmiowb();

	/* Set up square wave */
	int_out.raw = 0;
	int_out.fields.count = IOC4_CALIBRATE_COUNT;
	int_out.fields.mode = IOC4_INT_OUT_MODE_TOGGLE;
	int_out.fields.diag = 0;
	writel(int_out.raw, &idd->idd_misc_regs->int_out.raw);
	mmiowb();

	/* Check square wave period averaged over some number of cycles */
	do {
		int_out.raw = readl(&idd->idd_misc_regs->int_out.raw);
		state = int_out.fields.int_out;
		if (!last_state && state) {
			count++;
			if (count == IOC4_CALIBRATE_END) {
				end = rtc_time();
				break;
			} else if (count == IOC4_CALIBRATE_DISCARD)
				start = rtc_time();
		}
		last_state = state;
	} while (1);

	/* Calculation rearranged to preserve intermediate precision.
	 * Logically:
	 * 1. "end - start" gives us number of RTC cycles over all the
	 *    square wave cycles measured.
	 * 2. Divide by number of square wave cycles to get number of
	 *    RTC cycles per square wave cycle.
	 * 3. Divide by 2*(int_out.fields.count+1), which is the formula
	 *    by which the IOC4 generates the square wave, to get the
	 *    number of RTC cycles per IOC4 INT_OUT count.
	 * 4. Divide by sn_rtc_cycles_per_second to get seconds per
	 *    count.
	 * 5. Multiply by 1E9 to get nanoseconds per count.
	 */
	period = ((end - start) * 1000000000) /
	    (IOC4_CALIBRATE_CYCLES * 2 * (IOC4_CALIBRATE_COUNT + 1)
	     * sn_rtc_cycles_per_second);

	/* Bounds check the result. */
	if (period > IOC4_CALIBRATE_LOW_LIMIT ||
	    period < IOC4_CALIBRATE_HIGH_LIMIT) {
206
207
208
209
		printk(KERN_INFO
		       "IOC4 %s: Clock calibration failed.  Assuming"
		       "PCI clock is %d ns.\n",
		       pci_name(idd->idd_pdev),
210
211
212
		       IOC4_CALIBRATE_DEFAULT / IOC4_EXTINT_COUNT_DIVISOR);
		period = IOC4_CALIBRATE_DEFAULT;
	} else {
213
214
215
		printk(KERN_DEBUG
		       "IOC4 %s: PCI clock is %ld ns.\n",
		       pci_name(idd->idd_pdev),
216
217
218
219
220
221
222
223
224
225
226
		       period / IOC4_EXTINT_COUNT_DIVISOR);
	}

	/* Remember results.  We store the extint clock period rather
	 * than the PCI clock period so that greater precision is
	 * retained.  Divide by IOC4_EXTINT_COUNT_DIVISOR to get
	 * PCI clock period.
	 */
	idd->count_period = period;
}

227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
/* There are three variants of IOC4 cards: IO9, IO10, and PCI-RT.
 * Each brings out different combinations of IOC4 signals, thus.
 * the IOC4 subdrivers need to know to which we're attached.
 *
 * We look for the presence of a SCSI (IO9) or SATA (IO10) controller
 * on the same PCI bus at slot number 3 to differentiate IO9 from IO10.
 * If neither is present, it's a PCI-RT.
 */
static unsigned int
ioc4_variant(struct ioc4_driver_data *idd)
{
	struct pci_dev *pdev = NULL;
	int found = 0;

	/* IO9: Look for a QLogic ISP 12160 at the same bus and slot 3. */
	do {
		pdev = pci_get_device(PCI_VENDOR_ID_QLOGIC,
				      PCI_DEVICE_ID_QLOGIC_ISP12160, pdev);
		if (pdev &&
		    idd->idd_pdev->bus->number == pdev->bus->number &&
		    3 == PCI_SLOT(pdev->devfn))
			found = 1;
		pci_dev_put(pdev);
	} while (pdev && !found);
	if (NULL != pdev)
		return IOC4_VARIANT_IO9;

	/* IO10: Look for a Vitesse VSC 7174 at the same bus and slot 3. */
	pdev = NULL;
	do {
		pdev = pci_get_device(PCI_VENDOR_ID_VITESSE,
				      PCI_DEVICE_ID_VITESSE_VSC7174, pdev);
		if (pdev &&
		    idd->idd_pdev->bus->number == pdev->bus->number &&
		    3 == PCI_SLOT(pdev->devfn))
			found = 1;
		pci_dev_put(pdev);
	} while (pdev && !found);
	if (NULL != pdev)
		return IOC4_VARIANT_IO10;

	/* PCI-RT: No SCSI/SATA controller will be present */
	return IOC4_VARIANT_PCI_RT;
}

272
273
274
275
276
277
278
/* Adds a new instance of an IOC4 card */
static int
ioc4_probe(struct pci_dev *pdev, const struct pci_device_id *pci_id)
{
	struct ioc4_driver_data *idd;
	struct ioc4_submodule *is;
	uint32_t pcmd;
Linus Torvalds's avatar
Linus Torvalds committed
279
280
	int ret;

281
	/* Enable IOC4 and take ownership of it */
Linus Torvalds's avatar
Linus Torvalds committed
282
283
	if ((ret = pci_enable_device(pdev))) {
		printk(KERN_WARNING
284
285
286
		       "%s: Failed to enable IOC4 device for pci_dev %s.\n",
		       __FUNCTION__, pci_name(pdev));
		goto out;
Linus Torvalds's avatar
Linus Torvalds committed
287
288
289
	}
	pci_set_master(pdev);

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
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
	/* Set up per-IOC4 data */
	idd = kmalloc(sizeof(struct ioc4_driver_data), GFP_KERNEL);
	if (!idd) {
		printk(KERN_WARNING
		       "%s: Failed to allocate IOC4 data for pci_dev %s.\n",
		       __FUNCTION__, pci_name(pdev));
		ret = -ENODEV;
		goto out_idd;
	}
	idd->idd_pdev = pdev;
	idd->idd_pci_id = pci_id;

	/* Map IOC4 misc registers.  These are shared between subdevices
	 * so the main IOC4 module manages them.
	 */
	idd->idd_bar0 = pci_resource_start(idd->idd_pdev, 0);
	if (!idd->idd_bar0) {
		printk(KERN_WARNING
		       "%s: Unable to find IOC4 misc resource "
		       "for pci_dev %s.\n",
		       __FUNCTION__, pci_name(idd->idd_pdev));
		ret = -ENODEV;
		goto out_pci;
	}
	if (!request_region(idd->idd_bar0, sizeof(struct ioc4_misc_regs),
			    "ioc4_misc")) {
		printk(KERN_WARNING
		       "%s: Unable to request IOC4 misc region "
		       "for pci_dev %s.\n",
		       __FUNCTION__, pci_name(idd->idd_pdev));
		ret = -ENODEV;
		goto out_pci;
	}
	idd->idd_misc_regs = ioremap(idd->idd_bar0,
				     sizeof(struct ioc4_misc_regs));
	if (!idd->idd_misc_regs) {
		printk(KERN_WARNING
		       "%s: Unable to remap IOC4 misc region "
		       "for pci_dev %s.\n",
		       __FUNCTION__, pci_name(idd->idd_pdev));
		ret = -ENODEV;
		goto out_misc_region;
	}

	/* Failsafe portion of per-IOC4 initialization */

336
337
338
339
340
341
342
	/* Detect card variant */
	idd->idd_variant = ioc4_variant(idd);
	printk(KERN_INFO "IOC4 %s: %s card detected.\n", pci_name(pdev),
	       idd->idd_variant == IOC4_VARIANT_IO9 ? "IO9" :
	       idd->idd_variant == IOC4_VARIANT_PCI_RT ? "PCI-RT" :
	       idd->idd_variant == IOC4_VARIANT_IO10 ? "IO10" : "unknown");

343
344
345
346
347
	/* Initialize IOC4 */
	pci_read_config_dword(idd->idd_pdev, PCI_COMMAND, &pcmd);
	pci_write_config_dword(idd->idd_pdev, PCI_COMMAND,
			       pcmd | PCI_COMMAND_PARITY | PCI_COMMAND_SERR);

348
349
350
	/* Determine PCI clock */
	ioc4_clock_calibrate(idd);

351
352
353
354
355
356
357
358
359
360
361
362
363
364
	/* Disable/clear all interrupts.  Need to do this here lest
	 * one submodule request the shared IOC4 IRQ, but interrupt
	 * is generated by a different subdevice.
	 */
	/* Disable */
	writel(~0, &idd->idd_misc_regs->other_iec.raw);
	writel(~0, &idd->idd_misc_regs->sio_iec);
	/* Clear (i.e. acknowledge) */
	writel(~0, &idd->idd_misc_regs->other_ir.raw);
	writel(~0, &idd->idd_misc_regs->sio_ir);

	/* Track PCI-device specific data */
	idd->idd_serial_data = NULL;
	pci_set_drvdata(idd->idd_pdev, idd);
Jes Sorensen's avatar
Jes Sorensen committed
365
366

	mutex_lock(&ioc4_mutex);
367
	list_add_tail(&idd->idd_list, &ioc4_devices);
368
369
370
371
372
373
374
375
376
377
378

	/* Add this IOC4 to all submodules */
	list_for_each_entry(is, &ioc4_submodules, is_list) {
		if (is->is_probe && is->is_probe(idd)) {
			printk(KERN_WARNING
			       "%s: IOC4 submodule 0x%s probe failed "
			       "for pci_dev %s.\n",
			       __FUNCTION__, module_name(is->is_owner),
			       pci_name(idd->idd_pdev));
		}
	}
Jes Sorensen's avatar
Jes Sorensen committed
379
	mutex_unlock(&ioc4_mutex);
380
381
382
383
384
385
386
387
388
389
390

	return 0;

out_misc_region:
	release_region(idd->idd_bar0, sizeof(struct ioc4_misc_regs));
out_pci:
	kfree(idd);
out_idd:
	pci_disable_device(pdev);
out:
	return ret;
Linus Torvalds's avatar
Linus Torvalds committed
391
392
}

393
394
395
396
397
398
399
400
401
402
/* Removes a particular instance of an IOC4 card. */
static void
ioc4_remove(struct pci_dev *pdev)
{
	struct ioc4_submodule *is;
	struct ioc4_driver_data *idd;

	idd = pci_get_drvdata(pdev);

	/* Remove this IOC4 from all submodules */
Jes Sorensen's avatar
Jes Sorensen committed
403
	mutex_lock(&ioc4_mutex);
404
405
406
407
408
409
410
411
412
	list_for_each_entry(is, &ioc4_submodules, is_list) {
		if (is->is_remove && is->is_remove(idd)) {
			printk(KERN_WARNING
			       "%s: IOC4 submodule 0x%s remove failed "
			       "for pci_dev %s.\n",
			       __FUNCTION__, module_name(is->is_owner),
			       pci_name(idd->idd_pdev));
		}
	}
Jes Sorensen's avatar
Jes Sorensen committed
413
	mutex_unlock(&ioc4_mutex);
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428

	/* Release resources */
	iounmap(idd->idd_misc_regs);
	if (!idd->idd_bar0) {
		printk(KERN_WARNING
		       "%s: Unable to get IOC4 misc mapping for pci_dev %s. "
		       "Device removal may be incomplete.\n",
		       __FUNCTION__, pci_name(idd->idd_pdev));
	}
	release_region(idd->idd_bar0, sizeof(struct ioc4_misc_regs));

	/* Disable IOC4 and relinquish */
	pci_disable_device(pdev);

	/* Remove and free driver data */
Jes Sorensen's avatar
Jes Sorensen committed
429
	mutex_lock(&ioc4_mutex);
430
	list_del(&idd->idd_list);
Jes Sorensen's avatar
Jes Sorensen committed
431
	mutex_unlock(&ioc4_mutex);
432
433
434
435
	kfree(idd);
}

static struct pci_device_id ioc4_id_table[] = {
Linus Torvalds's avatar
Linus Torvalds committed
436
437
438
439
440
	{PCI_VENDOR_ID_SGI, PCI_DEVICE_ID_SGI_IOC4, PCI_ANY_ID,
	 PCI_ANY_ID, 0x0b4000, 0xFFFFFF},
	{0}
};

441
static struct pci_driver ioc4_driver = {
442
443
444
445
	.name = "IOC4",
	.id_table = ioc4_id_table,
	.probe = ioc4_probe,
	.remove = ioc4_remove,
Linus Torvalds's avatar
Linus Torvalds committed
446
447
};

448
449
450
451
452
453
454
455
456
MODULE_DEVICE_TABLE(pci, ioc4_id_table);

/*********************
 * Module management *
 *********************/

/* Module load */
static int __devinit
ioc4_init(void)
Linus Torvalds's avatar
Linus Torvalds committed
457
{
458
459
	return pci_register_driver(&ioc4_driver);
}
Linus Torvalds's avatar
Linus Torvalds committed
460

461
462
463
464
465
/* Module unload */
static void __devexit
ioc4_exit(void)
{
	pci_unregister_driver(&ioc4_driver);
Linus Torvalds's avatar
Linus Torvalds committed
466
467
}

468
469
470
471
472
module_init(ioc4_init);
module_exit(ioc4_exit);

MODULE_AUTHOR("Brent Casavant - Silicon Graphics, Inc. <bcasavan@sgi.com>");
MODULE_DESCRIPTION("PCI driver master module for SGI IOC4 Base-IO Card");
Linus Torvalds's avatar
Linus Torvalds committed
473
MODULE_LICENSE("GPL");
474
475
476

EXPORT_SYMBOL(ioc4_register_submodule);
EXPORT_SYMBOL(ioc4_unregister_submodule);