diff options
Diffstat (limited to 'drivers/scsi/aic7xxx/aic79xx_osm_pci.c')
| -rw-r--r-- | drivers/scsi/aic7xxx/aic79xx_osm_pci.c | 234 |
1 files changed, 132 insertions, 102 deletions
diff --git a/drivers/scsi/aic7xxx/aic79xx_osm_pci.c b/drivers/scsi/aic7xxx/aic79xx_osm_pci.c index 91daf0c7fb1..3c85873b14b 100644 --- a/drivers/scsi/aic7xxx/aic79xx_osm_pci.c +++ b/drivers/scsi/aic7xxx/aic79xx_osm_pci.c @@ -43,25 +43,17 @@ #include "aic79xx_inline.h" #include "aic79xx_pci.h" -static int ahd_linux_pci_dev_probe(struct pci_dev *pdev, - const struct pci_device_id *ent); -static int ahd_linux_pci_reserve_io_regions(struct ahd_softc *ahd, - u_long *base, u_long *base2); -static int ahd_linux_pci_reserve_mem_region(struct ahd_softc *ahd, - u_long *bus_addr, - uint8_t __iomem **maddr); -static void ahd_linux_pci_dev_remove(struct pci_dev *pdev); - /* Define the macro locally since it's different for different class of chips. */ #define ID(x) \ ID2C(x), \ ID2C(IDIROC(x)) -static struct pci_device_id ahd_linux_pci_id_table[] = { +static const struct pci_device_id ahd_linux_pci_id_table[] = { /* aic7901 based controllers */ ID(ID_AHA_29320A), ID(ID_AHA_29320ALP), + ID(ID_AHA_29320LPE), /* aic7902 based controllers */ ID(ID_AHA_29320), ID(ID_AHA_29320B), @@ -82,37 +74,83 @@ static struct pci_device_id ahd_linux_pci_id_table[] = { MODULE_DEVICE_TABLE(pci, ahd_linux_pci_id_table); -struct pci_driver aic79xx_pci_driver = { - .name = "aic79xx", - .probe = ahd_linux_pci_dev_probe, - .remove = ahd_linux_pci_dev_remove, - .id_table = ahd_linux_pci_id_table -}; +#ifdef CONFIG_PM +static int +ahd_linux_pci_dev_suspend(struct pci_dev *pdev, pm_message_t mesg) +{ + struct ahd_softc *ahd = pci_get_drvdata(pdev); + int rc; + + if ((rc = ahd_suspend(ahd))) + return rc; + + ahd_pci_suspend(ahd); + + pci_save_state(pdev); + pci_disable_device(pdev); + + if (mesg.event & PM_EVENT_SLEEP) + pci_set_power_state(pdev, PCI_D3hot); + + return rc; +} + +static int +ahd_linux_pci_dev_resume(struct pci_dev *pdev) +{ + struct ahd_softc *ahd = pci_get_drvdata(pdev); + int rc; + + pci_set_power_state(pdev, PCI_D0); + pci_restore_state(pdev); + + if ((rc = pci_enable_device(pdev))) { + dev_printk(KERN_ERR, &pdev->dev, + "failed to enable device after resume (%d)\n", rc); + return rc; + } + + pci_set_master(pdev); + + ahd_pci_resume(ahd); + + ahd_resume(ahd); + + return rc; +} +#endif static void ahd_linux_pci_dev_remove(struct pci_dev *pdev) { - struct ahd_softc *ahd; - u_long l; + struct ahd_softc *ahd = pci_get_drvdata(pdev); + u_long s; - /* - * We should be able to just perform - * the free directly, but check our - * list for extra sanity. - */ - ahd_list_lock(&l); - ahd = ahd_find_softc((struct ahd_softc *)pci_get_drvdata(pdev)); - if (ahd != NULL) { - u_long s; - - TAILQ_REMOVE(&ahd_tailq, ahd, links); - ahd_list_unlock(&l); - ahd_lock(ahd, &s); - ahd_intr_enable(ahd, FALSE); - ahd_unlock(ahd, &s); - ahd_free(ahd); - } else - ahd_list_unlock(&l); + if (ahd->platform_data && ahd->platform_data->host) + scsi_remove_host(ahd->platform_data->host); + + ahd_lock(ahd, &s); + ahd_intr_enable(ahd, FALSE); + ahd_unlock(ahd, &s); + ahd_free(ahd); +} + +static void +ahd_linux_pci_inherit_flags(struct ahd_softc *ahd) +{ + struct pci_dev *pdev = ahd->dev_softc, *master_pdev; + unsigned int master_devfn = PCI_DEVFN(PCI_SLOT(pdev->devfn), 0); + + master_pdev = pci_get_slot(pdev->bus, master_devfn); + if (master_pdev) { + struct ahd_softc *master = pci_get_drvdata(master_pdev); + if (master) { + ahd->flags &= ~AHD_BIOS_ENABLED; + ahd->flags |= master->flags & AHD_BIOS_ENABLED; + } else + printk(KERN_ERR "aic79xx: no multichannel peer found!\n"); + pci_dev_put(master_pdev); + } } static int @@ -121,25 +159,10 @@ ahd_linux_pci_dev_probe(struct pci_dev *pdev, const struct pci_device_id *ent) char buf[80]; struct ahd_softc *ahd; ahd_dev_softc_t pci; - struct ahd_pci_identity *entry; + const struct ahd_pci_identity *entry; char *name; int error; - - /* - * Some BIOSen report the same device multiple times. - */ - TAILQ_FOREACH(ahd, &ahd_tailq, links) { - struct pci_dev *probed_pdev; - - probed_pdev = ahd->dev_softc; - if (probed_pdev->bus->number == pdev->bus->number - && probed_pdev->devfn == pdev->devfn) - break; - } - if (ahd != NULL) { - /* Skip duplicate. */ - return (-ENODEV); - } + struct device *dev = &pdev->dev; pci = pdev; entry = ahd_find_pci_device(pci); @@ -155,7 +178,7 @@ ahd_linux_pci_dev_probe(struct pci_dev *pdev, const struct pci_device_id *ent) ahd_get_pci_bus(pci), ahd_get_pci_slot(pci), ahd_get_pci_function(pci)); - name = malloc(strlen(buf) + 1, M_DEVBUF, M_NOWAIT); + name = kmalloc(strlen(buf) + 1, GFP_ATOMIC); if (name == NULL) return (-ENOMEM); strcpy(name, buf); @@ -169,23 +192,18 @@ ahd_linux_pci_dev_probe(struct pci_dev *pdev, const struct pci_device_id *ent) pci_set_master(pdev); if (sizeof(dma_addr_t) > 4) { - uint64_t memsize; - const uint64_t mask_39bit = 0x7FFFFFFFFFULL; - - memsize = ahd_linux_get_memsize(); + const u64 required_mask = dma_get_required_mask(dev); - if (memsize >= 0x8000000000ULL - && pci_set_dma_mask(pdev, DMA_64BIT_MASK) == 0) { + if (required_mask > DMA_BIT_MASK(39) && + dma_set_mask(dev, DMA_BIT_MASK(64)) == 0) ahd->flags |= AHD_64BIT_ADDRESSING; - ahd->platform_data->hw_dma_mask = DMA_64BIT_MASK; - } else if (memsize > 0x80000000 - && pci_set_dma_mask(pdev, mask_39bit) == 0) { + else if (required_mask > DMA_BIT_MASK(32) && + dma_set_mask(dev, DMA_BIT_MASK(39)) == 0) ahd->flags |= AHD_39BIT_ADDRESSING; - ahd->platform_data->hw_dma_mask = mask_39bit; - } + else + dma_set_mask(dev, DMA_BIT_MASK(32)); } else { - pci_set_dma_mask(pdev, DMA_32BIT_MASK); - ahd->platform_data->hw_dma_mask = DMA_32BIT_MASK; + dma_set_mask(dev, DMA_BIT_MASK(32)); } ahd->dev_softc = pci; error = ahd_pci_config(ahd, entry); @@ -193,23 +211,35 @@ ahd_linux_pci_dev_probe(struct pci_dev *pdev, const struct pci_device_id *ent) ahd_free(ahd); return (-error); } + + /* + * Second Function PCI devices need to inherit some + * * settings from function 0. + */ + if ((ahd->features & AHD_MULTI_FUNC) && PCI_FUNC(pdev->devfn) != 0) + ahd_linux_pci_inherit_flags(ahd); + pci_set_drvdata(pdev, ahd); - if (aic79xx_detect_complete) { -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0) - ahd_linux_register_host(ahd, &aic79xx_driver_template); -#else - printf("aic79xx: ignoring PCI device found after " - "initialization\n"); - return (-ENODEV); -#endif - } + + ahd_linux_register_host(ahd, &aic79xx_driver_template); return (0); } +static struct pci_driver aic79xx_pci_driver = { + .name = "aic79xx", + .probe = ahd_linux_pci_dev_probe, +#ifdef CONFIG_PM + .suspend = ahd_linux_pci_dev_suspend, + .resume = ahd_linux_pci_dev_resume, +#endif + .remove = ahd_linux_pci_dev_remove, + .id_table = ahd_linux_pci_id_table +}; + int ahd_linux_pci_init(void) { - return (pci_module_init(&aic79xx_pci_driver)); + return pci_register_driver(&aic79xx_pci_driver); } void @@ -219,8 +249,8 @@ ahd_linux_pci_exit(void) } static int -ahd_linux_pci_reserve_io_regions(struct ahd_softc *ahd, u_long *base, - u_long *base2) +ahd_linux_pci_reserve_io_regions(struct ahd_softc *ahd, resource_size_t *base, + resource_size_t *base2) { *base = pci_resource_start(ahd->dev_softc, 0); /* @@ -231,10 +261,10 @@ ahd_linux_pci_reserve_io_regions(struct ahd_softc *ahd, u_long *base, *base2 = pci_resource_start(ahd->dev_softc, 3); if (*base == 0 || *base2 == 0) return (ENOMEM); - if (request_region(*base, 256, "aic79xx") == 0) + if (!request_region(*base, 256, "aic79xx")) return (ENOMEM); - if (request_region(*base2, 256, "aic79xx") == 0) { - release_region(*base2, 256); + if (!request_region(*base2, 256, "aic79xx")) { + release_region(*base, 256); return (ENOMEM); } return (0); @@ -242,13 +272,13 @@ ahd_linux_pci_reserve_io_regions(struct ahd_softc *ahd, u_long *base, static int ahd_linux_pci_reserve_mem_region(struct ahd_softc *ahd, - u_long *bus_addr, + resource_size_t *bus_addr, uint8_t __iomem **maddr) { - u_long start; - u_long base_page; + resource_size_t start; + resource_size_t base_page; u_long base_offset; - int error; + int error = 0; if (aic79xx_allow_memio == 0) return (ENOMEM); @@ -256,16 +286,15 @@ ahd_linux_pci_reserve_mem_region(struct ahd_softc *ahd, if ((ahd->bugs & AHD_PCIX_MMAPIO_BUG) != 0) return (ENOMEM); - error = 0; start = pci_resource_start(ahd->dev_softc, 1); base_page = start & PAGE_MASK; base_offset = start - base_page; if (start != 0) { *bus_addr = start; - if (request_mem_region(start, 0x1000, "aic79xx") == 0) + if (!request_mem_region(start, 0x1000, "aic79xx")) error = ENOMEM; - if (error == 0) { - *maddr = ioremap_nocache(base_page, base_offset + 256); + if (!error) { + *maddr = ioremap_nocache(base_page, base_offset + 512); if (*maddr == NULL) { error = ENOMEM; release_mem_region(start, 0x1000); @@ -281,7 +310,7 @@ int ahd_pci_map_registers(struct ahd_softc *ahd) { uint32_t command; - u_long base; + resource_size_t base; uint8_t __iomem *maddr; int error; @@ -304,7 +333,7 @@ ahd_pci_map_registers(struct ahd_softc *ahd) if (ahd_pci_test_register_access(ahd) != 0) { - printf("aic79xx: PCI Device %d:%d:%d " + printk("aic79xx: PCI Device %d:%d:%d " "failed memory mapped test. Using PIO.\n", ahd_get_pci_bus(ahd->dev_softc), ahd_get_pci_slot(ahd->dev_softc), @@ -317,31 +346,32 @@ ahd_pci_map_registers(struct ahd_softc *ahd) } else command |= PCIM_CMD_MEMEN; } else if (bootverbose) { - printf("aic79xx: PCI%d:%d:%d MEM region 0x%lx " + printk("aic79xx: PCI%d:%d:%d MEM region 0x%llx " "unavailable. Cannot memory map device.\n", ahd_get_pci_bus(ahd->dev_softc), ahd_get_pci_slot(ahd->dev_softc), ahd_get_pci_function(ahd->dev_softc), - base); + (unsigned long long)base); } if (maddr == NULL) { - u_long base2; + resource_size_t base2; error = ahd_linux_pci_reserve_io_regions(ahd, &base, &base2); if (error == 0) { ahd->tags[0] = BUS_SPACE_PIO; ahd->tags[1] = BUS_SPACE_PIO; - ahd->bshs[0].ioport = base; - ahd->bshs[1].ioport = base2; + ahd->bshs[0].ioport = (u_long)base; + ahd->bshs[1].ioport = (u_long)base2; command |= PCIM_CMD_PORTEN; } else { - printf("aic79xx: PCI%d:%d:%d IO regions 0x%lx and 0x%lx" - "unavailable. Cannot map device.\n", + printk("aic79xx: PCI%d:%d:%d IO regions 0x%llx and " + "0x%llx unavailable. Cannot map device.\n", ahd_get_pci_bus(ahd->dev_softc), ahd_get_pci_slot(ahd->dev_softc), ahd_get_pci_function(ahd->dev_softc), - base, base2); + (unsigned long long)base, + (unsigned long long)base2); } } ahd_pci_write_config(ahd->dev_softc, PCIR_COMMAND, command, 4); @@ -354,8 +384,8 @@ ahd_pci_map_int(struct ahd_softc *ahd) int error; error = request_irq(ahd->dev_softc->irq, ahd_linux_isr, - SA_SHIRQ, "aic79xx", ahd); - if (error == 0) + IRQF_SHARED, "aic79xx", ahd); + if (!error) ahd->platform_data->irq = ahd->dev_softc->irq; return (-error); |
