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

Subversion Repositories or1k

[/] [or1k/] [trunk/] [linux/] [linux-2.4/] [drivers/] [scsi/] [aic7xxx/] [aic79xx_osm_pci.c] - Blame information for rev 1275

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

Line No. Rev Author Line
1 1275 phoenix
/*
2
 * Linux driver attachment glue for PCI based U320 controllers.
3
 *
4
 * Copyright (c) 2000-2001 Adaptec Inc.
5
 * All rights reserved.
6
 *
7
 * Redistribution and use in source and binary forms, with or without
8
 * modification, are permitted provided that the following conditions
9
 * are met:
10
 * 1. Redistributions of source code must retain the above copyright
11
 *    notice, this list of conditions, and the following disclaimer,
12
 *    without modification.
13
 * 2. Redistributions in binary form must reproduce at minimum a disclaimer
14
 *    substantially similar to the "NO WARRANTY" disclaimer below
15
 *    ("Disclaimer") and any redistribution must be conditioned upon
16
 *    including a substantially similar Disclaimer requirement for further
17
 *    binary redistribution.
18
 * 3. Neither the names of the above-listed copyright holders nor the names
19
 *    of any contributors may be used to endorse or promote products derived
20
 *    from this software without specific prior written permission.
21
 *
22
 * Alternatively, this software may be distributed under the terms of the
23
 * GNU General Public License ("GPL") version 2 as published by the Free
24
 * Software Foundation.
25
 *
26
 * NO WARRANTY
27
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
28
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
29
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
30
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
31
 * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
35
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
36
 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
37
 * POSSIBILITY OF SUCH DAMAGES.
38
 *
39
 * $Id: aic79xx_osm_pci.c,v 1.1.1.1 2004-04-15 02:15:51 phoenix Exp $
40
 */
41
 
42
#include "aic79xx_osm.h"
43
#include "aic79xx_inline.h"
44
 
45
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0)
46
struct pci_device_id
47
{
48
};
49
#endif
50
 
51
static int      ahd_linux_pci_dev_probe(struct pci_dev *pdev,
52
                                        const struct pci_device_id *ent);
53
static int      ahd_linux_pci_reserve_io_regions(struct ahd_softc *ahd,
54
                                                 u_long *base, u_long *base2);
55
static int      ahd_linux_pci_reserve_mem_region(struct ahd_softc *ahd,
56
                                                 u_long *bus_addr,
57
                                                 uint8_t **maddr);
58
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
59
static void     ahd_linux_pci_dev_remove(struct pci_dev *pdev);
60
 
61
/* We do our own ID filtering.  So, grab all SCSI storage class devices. */
62
static struct pci_device_id ahd_linux_pci_id_table[] = {
63
        {
64
                0x9005, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID,
65
                PCI_CLASS_STORAGE_SCSI << 8, 0xFFFF00, 0
66
        },
67
        { 0 }
68
};
69
 
70
MODULE_DEVICE_TABLE(pci, ahd_linux_pci_id_table);
71
 
72
struct pci_driver aic79xx_pci_driver = {
73
        name:           "aic79xx",
74
        probe:          ahd_linux_pci_dev_probe,
75
        remove:         ahd_linux_pci_dev_remove,
76
        id_table:       ahd_linux_pci_id_table
77
};
78
 
79
static void
80
ahd_linux_pci_dev_remove(struct pci_dev *pdev)
81
{
82
        struct ahd_softc *ahd;
83
        u_long l;
84
 
85
        /*
86
         * We should be able to just perform
87
         * the free directly, but check our
88
         * list for extra sanity.
89
         */
90
        ahd_list_lock(&l);
91
        ahd = ahd_find_softc((struct ahd_softc *)pci_get_drvdata(pdev));
92
        if (ahd != NULL) {
93
                u_long s;
94
 
95
                ahd_lock(ahd, &s);
96
                ahd_intr_enable(ahd, FALSE);
97
                ahd_unlock(ahd, &s);
98
                ahd_free(ahd);
99
        }
100
        ahd_list_unlock(&l);
101
}
102
#endif /* !LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0) */
103
 
104
static int
105
ahd_linux_pci_dev_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
106
{
107
        char             buf[80];
108
        struct           ahd_softc *ahd;
109
        ahd_dev_softc_t  pci;
110
        struct           ahd_pci_identity *entry;
111
        char            *name;
112
        int              error;
113
 
114
        /*
115
         * Some BIOSen report the same device multiple times.
116
         */
117
        TAILQ_FOREACH(ahd, &ahd_tailq, links) {
118
                struct pci_dev *probed_pdev;
119
 
120
                probed_pdev = ahd->dev_softc;
121
                if (probed_pdev->bus->number == pdev->bus->number
122
                 && probed_pdev->devfn == pdev->devfn)
123
                        break;
124
        }
125
        if (ahd != NULL) {
126
                /* Skip duplicate. */
127
                return (-ENODEV);
128
        }
129
 
130
        pci = pdev;
131
        entry = ahd_find_pci_device(pci);
132
        if (entry == NULL)
133
                return (-ENODEV);
134
 
135
        /*
136
         * Allocate a softc for this card and
137
         * set it up for attachment by our
138
         * common detect routine.
139
         */
140
        sprintf(buf, "ahd_pci:%d:%d:%d",
141
                ahd_get_pci_bus(pci),
142
                ahd_get_pci_slot(pci),
143
                ahd_get_pci_function(pci));
144
        name = malloc(strlen(buf) + 1, M_DEVBUF, M_NOWAIT);
145
        if (name == NULL)
146
                return (-ENOMEM);
147
        strcpy(name, buf);
148
        ahd = ahd_alloc(NULL, name);
149
        if (ahd == NULL)
150
                return (-ENOMEM);
151
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
152
        if (pci_enable_device(pdev)) {
153
                ahd_free(ahd);
154
                return (-ENODEV);
155
        }
156
        pci_set_master(pdev);
157
 
158
        if (sizeof(bus_addr_t) > 4) {
159
                uint64_t   memsize;
160
                bus_addr_t mask_64bit;
161
                bus_addr_t mask_39bit;
162
 
163
                memsize = ahd_linux_get_memsize();
164
                mask_64bit = (bus_addr_t)0xFFFFFFFFFFFFFFFFULL;
165
                mask_39bit = (bus_addr_t)0x7FFFFFFFFFULL;
166
                if (memsize >= 0x8000000000ULL
167
                 && ahd_pci_set_dma_mask(pdev, mask_64bit) == 0) {
168
                        ahd->flags |= AHD_64BIT_ADDRESSING;
169
                        ahd->platform_data->hw_dma_mask = mask_64bit;
170
                } else if (memsize > 0x80000000
171
                        && ahd_pci_set_dma_mask(pdev, mask_39bit) == 0) {
172
                        ahd->flags |= AHD_39BIT_ADDRESSING;
173
                        ahd->platform_data->hw_dma_mask = mask_39bit;
174
                }
175
        } else {
176
                ahd_pci_set_dma_mask(pdev, 0xFFFFFFFF);
177
                ahd->platform_data->hw_dma_mask = 0xFFFFFFFF;
178
        }
179
#endif
180
        ahd->dev_softc = pci;
181
        error = ahd_pci_config(ahd, entry);
182
        if (error != 0) {
183
                ahd_free(ahd);
184
                return (-error);
185
        }
186
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
187
        pci_set_drvdata(pdev, ahd);
188
        if (aic79xx_detect_complete) {
189
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
190
                ahd_linux_register_host(ahd, &aic79xx_driver_template);
191
#else
192
                printf("aic79xx: ignoring PCI device found after "
193
                       "initialization\n");
194
                return (-ENODEV);
195
#endif
196
        }
197
#endif
198
        return (0);
199
}
200
 
201
int
202
ahd_linux_pci_init(void)
203
{
204
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
205
        return (pci_module_init(&aic79xx_pci_driver));
206
#else
207
        struct pci_dev *pdev;
208
        u_int class;
209
        int found;
210
 
211
        /* If we don't have a PCI bus, we can't find any adapters. */
212
        if (pci_present() == 0)
213
                return (0);
214
 
215
        found = 0;
216
        pdev = NULL;
217
        class = PCI_CLASS_STORAGE_SCSI << 8;
218
        while ((pdev = pci_find_class(class, pdev)) != NULL) {
219
                ahd_dev_softc_t pci;
220
                int error;
221
 
222
                pci = pdev;
223
                error = ahd_linux_pci_dev_probe(pdev, /*pci_devid*/NULL);
224
                if (error == 0)
225
                        found++;
226
        }
227
        return (found);
228
#endif
229
}
230
 
231
void
232
ahd_linux_pci_exit(void)
233
{
234
        pci_unregister_driver(&aic79xx_pci_driver);
235
}
236
 
237
static int
238
ahd_linux_pci_reserve_io_regions(struct ahd_softc *ahd, u_long *base,
239
                                 u_long *base2)
240
{
241
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)
242
        *base = pci_resource_start(ahd->dev_softc, 0);
243
        /*
244
         * This is really the 3rd bar and should be at index 2,
245
         * but the Linux PCI code doesn't know how to "count" 64bit
246
         * bars.
247
         */
248
        *base2 = pci_resource_start(ahd->dev_softc, 3);
249
#else
250
        *base = ahd_pci_read_config(ahd->dev_softc, AHD_PCI_IOADDR0, 4);
251
        *base2 = ahd_pci_read_config(ahd->dev_softc, AHD_PCI_IOADDR1, 4);
252
        *base &= PCI_BASE_ADDRESS_IO_MASK;
253
        *base2 &= PCI_BASE_ADDRESS_IO_MASK;
254
#endif
255
        if (*base == 0 || *base2 == 0)
256
                return (ENOMEM);
257
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0)
258
        if (check_region(*base, 256) != 0
259
         || check_region(*base2, 256) != 0)
260
                return (ENOMEM);
261
        request_region(*base, 256, "aic79xx");
262
        request_region(*base2, 256, "aic79xx");
263
#else
264
        if (request_region(*base, 256, "aic79xx") == 0)
265
                return (ENOMEM);
266
        if (request_region(*base2, 256, "aic79xx") == 0) {
267
                release_region(*base2, 256);
268
                return (ENOMEM);
269
        }
270
#endif
271
        return (0);
272
}
273
 
274
static int
275
ahd_linux_pci_reserve_mem_region(struct ahd_softc *ahd,
276
                                 u_long *bus_addr,
277
                                 uint8_t **maddr)
278
{
279
        u_long  start;
280
        u_long  base_page;
281
        u_long  base_offset;
282
        int     error;
283
 
284
        if (aic79xx_allow_memio == 0)
285
                return (ENOMEM);
286
 
287
        if ((ahd->bugs & AHD_PCIX_MMAPIO_BUG) != 0)
288
                return (ENOMEM);
289
 
290
        error = 0;
291
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)
292
        start = pci_resource_start(ahd->dev_softc, 1);
293
        base_page = start & PAGE_MASK;
294
        base_offset = start - base_page;
295
#else
296
        start = ahd_pci_read_config(ahd->dev_softc, PCIR_MAPS+4, 4);
297
        base_offset = start & PCI_BASE_ADDRESS_MEM_MASK;
298
        base_page = base_offset & PAGE_MASK;
299
        base_offset -= base_page;
300
#endif
301
        if (start != 0) {
302
                *bus_addr = start;
303
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
304
                if (request_mem_region(start, 0x1000, "aic79xx") == 0)
305
                        error = ENOMEM;
306
#endif
307
                if (error == 0) {
308
                        *maddr = ioremap_nocache(base_page, base_offset + 256);
309
                        if (*maddr == NULL) {
310
                                error = ENOMEM;
311
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
312
                                release_mem_region(start, 0x1000);
313
#endif
314
                        } else
315
                                *maddr += base_offset;
316
                }
317
        } else
318
                error = ENOMEM;
319
        return (error);
320
}
321
 
322
int
323
ahd_pci_map_registers(struct ahd_softc *ahd)
324
{
325
        uint32_t command;
326
        u_long   base;
327
        uint8_t *maddr;
328
        int      error;
329
 
330
        /*
331
         * If its allowed, we prefer memory mapped access.
332
         */
333
        command = ahd_pci_read_config(ahd->dev_softc, PCIR_COMMAND, 4);
334
        command &= ~(PCIM_CMD_PORTEN|PCIM_CMD_MEMEN);
335
        base = 0;
336
        maddr = NULL;
337
#ifdef MMAPIO
338
        error = ahd_linux_pci_reserve_mem_region(ahd, &base, &maddr);
339
        if (error == 0) {
340
                ahd->platform_data->mem_busaddr = base;
341
                ahd->tags[0] = BUS_SPACE_MEMIO;
342
                ahd->bshs[0].maddr = maddr;
343
                ahd->tags[1] = BUS_SPACE_MEMIO;
344
                ahd->bshs[1].maddr = maddr + 0x100;
345
                ahd_pci_write_config(ahd->dev_softc, PCIR_COMMAND,
346
                                     command | PCIM_CMD_MEMEN, 4);
347
 
348
                if (ahd_pci_test_register_access(ahd) != 0) {
349
 
350
                        printf("aic79xx: PCI Device %d:%d:%d "
351
                               "failed memory mapped test.  Using PIO.\n",
352
                               ahd_get_pci_bus(ahd->dev_softc),
353
                               ahd_get_pci_slot(ahd->dev_softc),
354
                               ahd_get_pci_function(ahd->dev_softc));
355
                        iounmap((void *)((u_long)maddr & PAGE_MASK));
356
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
357
                        release_mem_region(ahd->platform_data->mem_busaddr,
358
                                           0x1000);
359
#endif
360
                        ahd->bshs[0].maddr = NULL;
361
                        maddr = NULL;
362
                } else
363
                        command |= PCIM_CMD_MEMEN;
364
        } else if (bootverbose) {
365
                printf("aic79xx: PCI%d:%d:%d MEM region 0x%lx "
366
                       "unavailable. Cannot memory map device.\n",
367
                       ahd_get_pci_bus(ahd->dev_softc),
368
                       ahd_get_pci_slot(ahd->dev_softc),
369
                       ahd_get_pci_function(ahd->dev_softc),
370
                       base);
371
        }
372
#endif
373
 
374
        if (maddr == NULL) {
375
                u_long   base2;
376
 
377
                error = ahd_linux_pci_reserve_io_regions(ahd, &base, &base2);
378
                if (error == 0) {
379
                        ahd->tags[0] = BUS_SPACE_PIO;
380
                        ahd->tags[1] = BUS_SPACE_PIO;
381
                        ahd->bshs[0].ioport = base;
382
                        ahd->bshs[1].ioport = base2;
383
                        command |= PCIM_CMD_PORTEN;
384
                } else {
385
                        printf("aic79xx: PCI%d:%d:%d IO regions 0x%lx and 0x%lx"
386
                               "unavailable. Cannot map device.\n",
387
                               ahd_get_pci_bus(ahd->dev_softc),
388
                               ahd_get_pci_slot(ahd->dev_softc),
389
                               ahd_get_pci_function(ahd->dev_softc),
390
                               base, base2);
391
                }
392
        }
393
        ahd_pci_write_config(ahd->dev_softc, PCIR_COMMAND, command, 4);
394
        return (error);
395
}
396
 
397
int
398
ahd_pci_map_int(struct ahd_softc *ahd)
399
{
400
        int error;
401
 
402
        error = request_irq(ahd->dev_softc->irq, ahd_linux_isr,
403
                            SA_SHIRQ, "aic79xx", ahd);
404
        if (error == 0)
405
                ahd->platform_data->irq = ahd->dev_softc->irq;
406
 
407
        return (-error);
408
}
409
 
410
void
411
ahd_power_state_change(struct ahd_softc *ahd, ahd_power_state new_state)
412
{
413
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
414
        pci_set_power_state(ahd->dev_softc, new_state);
415
#else
416
        uint32_t cap;
417
        u_int cap_offset;
418
 
419
        /*
420
         * Traverse the capability list looking for
421
         * the power management capability.
422
         */
423
        cap = 0;
424
        cap_offset = ahd_pci_read_config(ahd->dev_softc,
425
                                         PCIR_CAP_PTR, /*bytes*/1);
426
        while (cap_offset != 0) {
427
 
428
                cap = ahd_pci_read_config(ahd->dev_softc,
429
                                          cap_offset, /*bytes*/4);
430
                if ((cap & 0xFF) == 1
431
                 && ((cap >> 16) & 0x3) > 0) {
432
                        uint32_t pm_control;
433
 
434
                        pm_control = ahd_pci_read_config(ahd->dev_softc,
435
                                                         cap_offset + 4,
436
                                                         /*bytes*/4);
437
                        pm_control &= ~0x3;
438
                        pm_control |= new_state;
439
                        ahd_pci_write_config(ahd->dev_softc,
440
                                             cap_offset + 4,
441
                                             pm_control, /*bytes*/2);
442
                        break;
443
                }
444
                cap_offset = (cap >> 8) & 0xFF;
445
        }
446
#endif 
447
}

powered by: WebSVN 2.1.0

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