OpenCores
URL https://opencores.org/ocsvn/test_project/test_project/trunk

Subversion Repositories test_project

[/] [test_project/] [trunk/] [linux_sd_driver/] [drivers/] [misc/] [ioc4.c] - Blame information for rev 78

Go to most recent revision | Details | Compare with Previous | View Log

Line No. Rev Author Line
1 62 marcus.erl
/*
2
 * This file is subject to the terms and conditions of the GNU General Public
3
 * License.  See the file "COPYING" in the main directory of this archive
4
 * for more details.
5
 *
6
 * Copyright (C) 2005-2006 Silicon Graphics, Inc.  All Rights Reserved.
7
 */
8
 
9
/* This file contains the master driver module for use by SGI IOC4 subdrivers.
10
 *
11
 * It allocates any resources shared between multiple subdevices, and
12
 * provides accessor functions (where needed) and the like for those
13
 * resources.  It also provides a mechanism for the subdevice modules
14
 * to support loading and unloading.
15
 *
16
 * Non-shared resources (e.g. external interrupt A_INT_OUT register page
17
 * alias, serial port and UART registers) are handled by the subdevice
18
 * modules themselves.
19
 *
20
 * This is all necessary because IOC4 is not implemented as a multi-function
21
 * PCI device, but an amalgamation of disparate registers for several
22
 * types of device (ATA, serial, external interrupts).  The normal
23
 * resource management in the kernel doesn't have quite the right interfaces
24
 * to handle this situation (e.g. multiple modules can't claim the same
25
 * PCI ID), thus this IOC4 master module.
26
 */
27
 
28
#include <linux/errno.h>
29
#include <linux/module.h>
30
#include <linux/pci.h>
31
#include <linux/ioc4.h>
32
#include <linux/ktime.h>
33
#include <linux/mutex.h>
34
#include <linux/time.h>
35
#include <asm/io.h>
36
 
37
/***************
38
 * Definitions *
39
 ***************/
40
 
41
/* Tweakable values */
42
 
43
/* PCI bus speed detection/calibration */
44
#define IOC4_CALIBRATE_COUNT 63         /* Calibration cycle period */
45
#define IOC4_CALIBRATE_CYCLES 256       /* Average over this many cycles */
46
#define IOC4_CALIBRATE_DISCARD 2        /* Discard first few cycles */
47
#define IOC4_CALIBRATE_LOW_MHZ 25       /* Lower bound on bus speed sanity */
48
#define IOC4_CALIBRATE_HIGH_MHZ 75      /* Upper bound on bus speed sanity */
49
#define IOC4_CALIBRATE_DEFAULT_MHZ 66   /* Assumed if sanity check fails */
50
 
51
/************************
52
 * Submodule management *
53
 ************************/
54
 
55
static DEFINE_MUTEX(ioc4_mutex);
56
 
57
static LIST_HEAD(ioc4_devices);
58
static LIST_HEAD(ioc4_submodules);
59
 
60
/* Register an IOC4 submodule */
61
int
62
ioc4_register_submodule(struct ioc4_submodule *is)
63
{
64
        struct ioc4_driver_data *idd;
65
 
66
        mutex_lock(&ioc4_mutex);
67
        list_add(&is->is_list, &ioc4_submodules);
68
 
69
        /* Initialize submodule for each IOC4 */
70
        if (!is->is_probe)
71
                goto out;
72
 
73
        list_for_each_entry(idd, &ioc4_devices, idd_list) {
74
                if (is->is_probe(idd)) {
75
                        printk(KERN_WARNING
76
                               "%s: IOC4 submodule %s probe failed "
77
                               "for pci_dev %s",
78
                               __FUNCTION__, module_name(is->is_owner),
79
                               pci_name(idd->idd_pdev));
80
                }
81
        }
82
 out:
83
        mutex_unlock(&ioc4_mutex);
84
        return 0;
85
}
86
 
87
/* Unregister an IOC4 submodule */
88
void
89
ioc4_unregister_submodule(struct ioc4_submodule *is)
90
{
91
        struct ioc4_driver_data *idd;
92
 
93
        mutex_lock(&ioc4_mutex);
94
        list_del(&is->is_list);
95
 
96
        /* Remove submodule for each IOC4 */
97
        if (!is->is_remove)
98
                goto out;
99
 
100
        list_for_each_entry(idd, &ioc4_devices, idd_list) {
101
                if (is->is_remove(idd)) {
102
                        printk(KERN_WARNING
103
                               "%s: IOC4 submodule %s remove failed "
104
                               "for pci_dev %s.\n",
105
                               __FUNCTION__, module_name(is->is_owner),
106
                               pci_name(idd->idd_pdev));
107
                }
108
        }
109
 out:
110
        mutex_unlock(&ioc4_mutex);
111
}
112
 
113
/*********************
114
 * Device management *
115
 *********************/
116
 
117
#define IOC4_CALIBRATE_LOW_LIMIT \
118
        (1000*IOC4_EXTINT_COUNT_DIVISOR/IOC4_CALIBRATE_LOW_MHZ)
119
#define IOC4_CALIBRATE_HIGH_LIMIT \
120
        (1000*IOC4_EXTINT_COUNT_DIVISOR/IOC4_CALIBRATE_HIGH_MHZ)
121
#define IOC4_CALIBRATE_DEFAULT \
122
        (1000*IOC4_EXTINT_COUNT_DIVISOR/IOC4_CALIBRATE_DEFAULT_MHZ)
123
 
124
#define IOC4_CALIBRATE_END \
125
        (IOC4_CALIBRATE_CYCLES + IOC4_CALIBRATE_DISCARD)
126
 
127
#define IOC4_INT_OUT_MODE_TOGGLE 0x7    /* Toggle INT_OUT every COUNT+1 ticks */
128
 
129
/* Determines external interrupt output clock period of the PCI bus an
130
 * IOC4 is attached to.  This value can be used to determine the PCI
131
 * bus speed.
132
 *
133
 * IOC4 has a design feature that various internal timers are derived from
134
 * the PCI bus clock.  This causes IOC4 device drivers to need to take the
135
 * bus speed into account when setting various register values (e.g. INT_OUT
136
 * register COUNT field, UART divisors, etc).  Since this information is
137
 * needed by several subdrivers, it is determined by the main IOC4 driver,
138
 * even though the following code utilizes external interrupt registers
139
 * to perform the speed calculation.
140
 */
141
static void
142
ioc4_clock_calibrate(struct ioc4_driver_data *idd)
143
{
144
        union ioc4_int_out int_out;
145
        union ioc4_gpcr gpcr;
146
        unsigned int state, last_state = 1;
147
        struct timespec start_ts, end_ts;
148
        uint64_t start, end, period;
149
        unsigned int count = 0;
150
 
151
        /* Enable output */
152
        gpcr.raw = 0;
153
        gpcr.fields.dir = IOC4_GPCR_DIR_0;
154
        gpcr.fields.int_out_en = 1;
155
        writel(gpcr.raw, &idd->idd_misc_regs->gpcr_s.raw);
156
 
157
        /* Reset to power-on state */
158
        writel(0, &idd->idd_misc_regs->int_out.raw);
159
        mmiowb();
160
 
161
        /* Set up square wave */
162
        int_out.raw = 0;
163
        int_out.fields.count = IOC4_CALIBRATE_COUNT;
164
        int_out.fields.mode = IOC4_INT_OUT_MODE_TOGGLE;
165
        int_out.fields.diag = 0;
166
        writel(int_out.raw, &idd->idd_misc_regs->int_out.raw);
167
        mmiowb();
168
 
169
        /* Check square wave period averaged over some number of cycles */
170
        do {
171
                int_out.raw = readl(&idd->idd_misc_regs->int_out.raw);
172
                state = int_out.fields.int_out;
173
                if (!last_state && state) {
174
                        count++;
175
                        if (count == IOC4_CALIBRATE_END) {
176
                                ktime_get_ts(&end_ts);
177
                                break;
178
                        } else if (count == IOC4_CALIBRATE_DISCARD)
179
                                ktime_get_ts(&start_ts);
180
                }
181
                last_state = state;
182
        } while (1);
183
 
184
        /* Calculation rearranged to preserve intermediate precision.
185
         * Logically:
186
         * 1. "end - start" gives us the measurement period over all
187
         *    the square wave cycles.
188
         * 2. Divide by number of square wave cycles to get the period
189
         *    of a square wave cycle.
190
         * 3. Divide by 2*(int_out.fields.count+1), which is the formula
191
         *    by which the IOC4 generates the square wave, to get the
192
         *    period of an IOC4 INT_OUT count.
193
         */
194
        end = end_ts.tv_sec * NSEC_PER_SEC + end_ts.tv_nsec;
195
        start = start_ts.tv_sec * NSEC_PER_SEC + start_ts.tv_nsec;
196
        period = (end - start) /
197
                (IOC4_CALIBRATE_CYCLES * 2 * (IOC4_CALIBRATE_COUNT + 1));
198
 
199
        /* Bounds check the result. */
200
        if (period > IOC4_CALIBRATE_LOW_LIMIT ||
201
            period < IOC4_CALIBRATE_HIGH_LIMIT) {
202
                printk(KERN_INFO
203
                       "IOC4 %s: Clock calibration failed.  Assuming"
204
                       "PCI clock is %d ns.\n",
205
                       pci_name(idd->idd_pdev),
206
                       IOC4_CALIBRATE_DEFAULT / IOC4_EXTINT_COUNT_DIVISOR);
207
                period = IOC4_CALIBRATE_DEFAULT;
208
        } else {
209
                u64 ns = period;
210
 
211
                do_div(ns, IOC4_EXTINT_COUNT_DIVISOR);
212
                printk(KERN_DEBUG
213
                       "IOC4 %s: PCI clock is %llu ns.\n",
214
                       pci_name(idd->idd_pdev), (unsigned long long)ns);
215
        }
216
 
217
        /* Remember results.  We store the extint clock period rather
218
         * than the PCI clock period so that greater precision is
219
         * retained.  Divide by IOC4_EXTINT_COUNT_DIVISOR to get
220
         * PCI clock period.
221
         */
222
        idd->count_period = period;
223
}
224
 
225
/* There are three variants of IOC4 cards: IO9, IO10, and PCI-RT.
226
 * Each brings out different combinations of IOC4 signals, thus.
227
 * the IOC4 subdrivers need to know to which we're attached.
228
 *
229
 * We look for the presence of a SCSI (IO9) or SATA (IO10) controller
230
 * on the same PCI bus at slot number 3 to differentiate IO9 from IO10.
231
 * If neither is present, it's a PCI-RT.
232
 */
233
static unsigned int
234
ioc4_variant(struct ioc4_driver_data *idd)
235
{
236
        struct pci_dev *pdev = NULL;
237
        int found = 0;
238
 
239
        /* IO9: Look for a QLogic ISP 12160 at the same bus and slot 3. */
240
        do {
241
                pdev = pci_get_device(PCI_VENDOR_ID_QLOGIC,
242
                                      PCI_DEVICE_ID_QLOGIC_ISP12160, pdev);
243
                if (pdev &&
244
                    idd->idd_pdev->bus->number == pdev->bus->number &&
245
                    3 == PCI_SLOT(pdev->devfn))
246
                        found = 1;
247
        } while (pdev && !found);
248
        if (NULL != pdev) {
249
                pci_dev_put(pdev);
250
                return IOC4_VARIANT_IO9;
251
        }
252
 
253
        /* IO10: Look for a Vitesse VSC 7174 at the same bus and slot 3. */
254
        pdev = NULL;
255
        do {
256
                pdev = pci_get_device(PCI_VENDOR_ID_VITESSE,
257
                                      PCI_DEVICE_ID_VITESSE_VSC7174, pdev);
258
                if (pdev &&
259
                    idd->idd_pdev->bus->number == pdev->bus->number &&
260
                    3 == PCI_SLOT(pdev->devfn))
261
                        found = 1;
262
        } while (pdev && !found);
263
        if (NULL != pdev) {
264
                pci_dev_put(pdev);
265
                return IOC4_VARIANT_IO10;
266
        }
267
 
268
        /* PCI-RT: No SCSI/SATA controller will be present */
269
        return IOC4_VARIANT_PCI_RT;
270
}
271
 
272
/* Adds a new instance of an IOC4 card */
273
static int
274
ioc4_probe(struct pci_dev *pdev, const struct pci_device_id *pci_id)
275
{
276
        struct ioc4_driver_data *idd;
277
        struct ioc4_submodule *is;
278
        uint32_t pcmd;
279
        int ret;
280
 
281
        /* Enable IOC4 and take ownership of it */
282
        if ((ret = pci_enable_device(pdev))) {
283
                printk(KERN_WARNING
284
                       "%s: Failed to enable IOC4 device for pci_dev %s.\n",
285
                       __FUNCTION__, pci_name(pdev));
286
                goto out;
287
        }
288
        pci_set_master(pdev);
289
 
290
        /* Set up per-IOC4 data */
291
        idd = kmalloc(sizeof(struct ioc4_driver_data), GFP_KERNEL);
292
        if (!idd) {
293
                printk(KERN_WARNING
294
                       "%s: Failed to allocate IOC4 data for pci_dev %s.\n",
295
                       __FUNCTION__, pci_name(pdev));
296
                ret = -ENODEV;
297
                goto out_idd;
298
        }
299
        idd->idd_pdev = pdev;
300
        idd->idd_pci_id = pci_id;
301
 
302
        /* Map IOC4 misc registers.  These are shared between subdevices
303
         * so the main IOC4 module manages them.
304
         */
305
        idd->idd_bar0 = pci_resource_start(idd->idd_pdev, 0);
306
        if (!idd->idd_bar0) {
307
                printk(KERN_WARNING
308
                       "%s: Unable to find IOC4 misc resource "
309
                       "for pci_dev %s.\n",
310
                       __FUNCTION__, pci_name(idd->idd_pdev));
311
                ret = -ENODEV;
312
                goto out_pci;
313
        }
314
        if (!request_mem_region(idd->idd_bar0, sizeof(struct ioc4_misc_regs),
315
                            "ioc4_misc")) {
316
                printk(KERN_WARNING
317
                       "%s: Unable to request IOC4 misc region "
318
                       "for pci_dev %s.\n",
319
                       __FUNCTION__, pci_name(idd->idd_pdev));
320
                ret = -ENODEV;
321
                goto out_pci;
322
        }
323
        idd->idd_misc_regs = ioremap(idd->idd_bar0,
324
                                     sizeof(struct ioc4_misc_regs));
325
        if (!idd->idd_misc_regs) {
326
                printk(KERN_WARNING
327
                       "%s: Unable to remap IOC4 misc region "
328
                       "for pci_dev %s.\n",
329
                       __FUNCTION__, pci_name(idd->idd_pdev));
330
                ret = -ENODEV;
331
                goto out_misc_region;
332
        }
333
 
334
        /* Failsafe portion of per-IOC4 initialization */
335
 
336
        /* Detect card variant */
337
        idd->idd_variant = ioc4_variant(idd);
338
        printk(KERN_INFO "IOC4 %s: %s card detected.\n", pci_name(pdev),
339
               idd->idd_variant == IOC4_VARIANT_IO9 ? "IO9" :
340
               idd->idd_variant == IOC4_VARIANT_PCI_RT ? "PCI-RT" :
341
               idd->idd_variant == IOC4_VARIANT_IO10 ? "IO10" : "unknown");
342
 
343
        /* Initialize IOC4 */
344
        pci_read_config_dword(idd->idd_pdev, PCI_COMMAND, &pcmd);
345
        pci_write_config_dword(idd->idd_pdev, PCI_COMMAND,
346
                               pcmd | PCI_COMMAND_PARITY | PCI_COMMAND_SERR);
347
 
348
        /* Determine PCI clock */
349
        ioc4_clock_calibrate(idd);
350
 
351
        /* Disable/clear all interrupts.  Need to do this here lest
352
         * one submodule request the shared IOC4 IRQ, but interrupt
353
         * is generated by a different subdevice.
354
         */
355
        /* Disable */
356
        writel(~0, &idd->idd_misc_regs->other_iec.raw);
357
        writel(~0, &idd->idd_misc_regs->sio_iec);
358
        /* Clear (i.e. acknowledge) */
359
        writel(~0, &idd->idd_misc_regs->other_ir.raw);
360
        writel(~0, &idd->idd_misc_regs->sio_ir);
361
 
362
        /* Track PCI-device specific data */
363
        idd->idd_serial_data = NULL;
364
        pci_set_drvdata(idd->idd_pdev, idd);
365
 
366
        mutex_lock(&ioc4_mutex);
367
        list_add_tail(&idd->idd_list, &ioc4_devices);
368
 
369
        /* Add this IOC4 to all submodules */
370
        list_for_each_entry(is, &ioc4_submodules, is_list) {
371
                if (is->is_probe && is->is_probe(idd)) {
372
                        printk(KERN_WARNING
373
                               "%s: IOC4 submodule 0x%s probe failed "
374
                               "for pci_dev %s.\n",
375
                               __FUNCTION__, module_name(is->is_owner),
376
                               pci_name(idd->idd_pdev));
377
                }
378
        }
379
        mutex_unlock(&ioc4_mutex);
380
 
381
        return 0;
382
 
383
out_misc_region:
384
        release_mem_region(idd->idd_bar0, sizeof(struct ioc4_misc_regs));
385
out_pci:
386
        kfree(idd);
387
out_idd:
388
        pci_disable_device(pdev);
389
out:
390
        return ret;
391
}
392
 
393
/* Removes a particular instance of an IOC4 card. */
394
static void
395
ioc4_remove(struct pci_dev *pdev)
396
{
397
        struct ioc4_submodule *is;
398
        struct ioc4_driver_data *idd;
399
 
400
        idd = pci_get_drvdata(pdev);
401
 
402
        /* Remove this IOC4 from all submodules */
403
        mutex_lock(&ioc4_mutex);
404
        list_for_each_entry(is, &ioc4_submodules, is_list) {
405
                if (is->is_remove && is->is_remove(idd)) {
406
                        printk(KERN_WARNING
407
                               "%s: IOC4 submodule 0x%s remove failed "
408
                               "for pci_dev %s.\n",
409
                               __FUNCTION__, module_name(is->is_owner),
410
                               pci_name(idd->idd_pdev));
411
                }
412
        }
413
        mutex_unlock(&ioc4_mutex);
414
 
415
        /* Release resources */
416
        iounmap(idd->idd_misc_regs);
417
        if (!idd->idd_bar0) {
418
                printk(KERN_WARNING
419
                       "%s: Unable to get IOC4 misc mapping for pci_dev %s. "
420
                       "Device removal may be incomplete.\n",
421
                       __FUNCTION__, pci_name(idd->idd_pdev));
422
        }
423
        release_mem_region(idd->idd_bar0, sizeof(struct ioc4_misc_regs));
424
 
425
        /* Disable IOC4 and relinquish */
426
        pci_disable_device(pdev);
427
 
428
        /* Remove and free driver data */
429
        mutex_lock(&ioc4_mutex);
430
        list_del(&idd->idd_list);
431
        mutex_unlock(&ioc4_mutex);
432
        kfree(idd);
433
}
434
 
435
static struct pci_device_id ioc4_id_table[] = {
436
        {PCI_VENDOR_ID_SGI, PCI_DEVICE_ID_SGI_IOC4, PCI_ANY_ID,
437
         PCI_ANY_ID, 0x0b4000, 0xFFFFFF},
438
        {0}
439
};
440
 
441
static struct pci_driver ioc4_driver = {
442
        .name = "IOC4",
443
        .id_table = ioc4_id_table,
444
        .probe = ioc4_probe,
445
        .remove = ioc4_remove,
446
};
447
 
448
MODULE_DEVICE_TABLE(pci, ioc4_id_table);
449
 
450
/*********************
451
 * Module management *
452
 *********************/
453
 
454
/* Module load */
455
static int __devinit
456
ioc4_init(void)
457
{
458
        return pci_register_driver(&ioc4_driver);
459
}
460
 
461
/* Module unload */
462
static void __devexit
463
ioc4_exit(void)
464
{
465
        pci_unregister_driver(&ioc4_driver);
466
}
467
 
468
module_init(ioc4_init);
469
module_exit(ioc4_exit);
470
 
471
MODULE_AUTHOR("Brent Casavant - Silicon Graphics, Inc. <bcasavan@sgi.com>");
472
MODULE_DESCRIPTION("PCI driver master module for SGI IOC4 Base-IO Card");
473
MODULE_LICENSE("GPL");
474
 
475
EXPORT_SYMBOL(ioc4_register_submodule);
476
EXPORT_SYMBOL(ioc4_unregister_submodule);

powered by: WebSVN 2.1.0

© copyright 1999-2025 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.