ftp://ftp.kernel.org/pub/linux/kernel/v2.6/linux-2.6.6.tar.bz2
[linux-2.6.git] / drivers / scsi / aic7xxx / aic79xx_osm_pci.c
1 /*
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: //depot/aic7xxx/linux/drivers/scsi/aic7xxx/aic79xx_osm_pci.c#25 $
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         error = ahd_linux_pci_reserve_mem_region(ahd, &base, &maddr);
338         if (error == 0) {
339                 ahd->platform_data->mem_busaddr = base;
340                 ahd->tags[0] = BUS_SPACE_MEMIO;
341                 ahd->bshs[0].maddr = maddr;
342                 ahd->tags[1] = BUS_SPACE_MEMIO;
343                 ahd->bshs[1].maddr = maddr + 0x100;
344                 ahd_pci_write_config(ahd->dev_softc, PCIR_COMMAND,
345                                      command | PCIM_CMD_MEMEN, 4);
346
347                 if (ahd_pci_test_register_access(ahd) != 0) {
348
349                         printf("aic79xx: PCI Device %d:%d:%d "
350                                "failed memory mapped test.  Using PIO.\n",
351                                ahd_get_pci_bus(ahd->dev_softc),
352                                ahd_get_pci_slot(ahd->dev_softc),
353                                ahd_get_pci_function(ahd->dev_softc));
354                         iounmap((void *)((u_long)maddr & PAGE_MASK));
355 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
356                         release_mem_region(ahd->platform_data->mem_busaddr,
357                                            0x1000);
358 #endif
359                         ahd->bshs[0].maddr = NULL;
360                         maddr = NULL;
361                 } else
362                         command |= PCIM_CMD_MEMEN;
363         } else if (bootverbose) {
364                 printf("aic79xx: PCI%d:%d:%d MEM region 0x%lx "
365                        "unavailable. Cannot memory map device.\n",
366                        ahd_get_pci_bus(ahd->dev_softc),
367                        ahd_get_pci_slot(ahd->dev_softc),
368                        ahd_get_pci_function(ahd->dev_softc),
369                        base);
370         }
371
372         if (maddr == NULL) {
373                 u_long   base2;
374
375                 error = ahd_linux_pci_reserve_io_regions(ahd, &base, &base2);
376                 if (error == 0) {
377                         ahd->tags[0] = BUS_SPACE_PIO;
378                         ahd->tags[1] = BUS_SPACE_PIO;
379                         ahd->bshs[0].ioport = base;
380                         ahd->bshs[1].ioport = base2;
381                         command |= PCIM_CMD_PORTEN;
382                 } else {
383                         printf("aic79xx: PCI%d:%d:%d IO regions 0x%lx and 0x%lx"
384                                "unavailable. Cannot map device.\n",
385                                ahd_get_pci_bus(ahd->dev_softc),
386                                ahd_get_pci_slot(ahd->dev_softc),
387                                ahd_get_pci_function(ahd->dev_softc),
388                                base, base2);
389                 }
390         }
391         ahd_pci_write_config(ahd->dev_softc, PCIR_COMMAND, command, 4);
392         return (error);
393 }
394
395 int
396 ahd_pci_map_int(struct ahd_softc *ahd)
397 {
398         int error;
399
400         error = request_irq(ahd->dev_softc->irq, ahd_linux_isr,
401                             SA_SHIRQ, "aic79xx", ahd);
402         if (error == 0)
403                 ahd->platform_data->irq = ahd->dev_softc->irq;
404         
405         return (-error);
406 }
407
408 void
409 ahd_power_state_change(struct ahd_softc *ahd, ahd_power_state new_state)
410 {
411 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
412         pci_set_power_state(ahd->dev_softc, new_state);
413 #else
414         uint32_t cap;
415         u_int cap_offset;
416
417         /*
418          * Traverse the capability list looking for
419          * the power management capability.
420          */
421         cap = 0;
422         cap_offset = ahd_pci_read_config(ahd->dev_softc,
423                                          PCIR_CAP_PTR, /*bytes*/1);
424         while (cap_offset != 0) {
425
426                 cap = ahd_pci_read_config(ahd->dev_softc,
427                                           cap_offset, /*bytes*/4);
428                 if ((cap & 0xFF) == 1
429                  && ((cap >> 16) & 0x3) > 0) {
430                         uint32_t pm_control;
431
432                         pm_control = ahd_pci_read_config(ahd->dev_softc,
433                                                          cap_offset + 4,
434                                                          /*bytes*/4);
435                         pm_control &= ~0x3;
436                         pm_control |= new_state;
437                         ahd_pci_write_config(ahd->dev_softc,
438                                              cap_offset + 4,
439                                              pm_control, /*bytes*/2);
440                         break;
441                 }
442                 cap_offset = (cap >> 8) & 0xFF;
443         }
444 #endif 
445 }