linux-bk/drivers/scsi/aic7xxx/aic79xx_osm_pci.c
<<
>>
Prefs
   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)
  46struct pci_device_id
  47{
  48};
  49#endif
  50
  51static int      ahd_linux_pci_dev_probe(struct pci_dev *pdev,
  52                                        const struct pci_device_id *ent);
  53static int      ahd_linux_pci_reserve_io_regions(struct ahd_softc *ahd,
  54                                                 u_long *base, u_long *base2);
  55static 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)
  59static 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. */
  62static 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
  70MODULE_DEVICE_TABLE(pci, ahd_linux_pci_id_table);
  71
  72struct 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
  79static void
  80ahd_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
 104static int
 105ahd_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
 201int
 202ahd_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
 231void
 232ahd_linux_pci_exit(void)
 233{
 234        pci_unregister_driver(&aic79xx_pci_driver);
 235}
 236
 237static int
 238ahd_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
 274static int
 275ahd_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
 322int
 323ahd_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
 395int
 396ahd_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
 408void
 409ahd_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}
 446
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.