diff --git a/drivers/dahdi/wctdm.c b/drivers/dahdi/wctdm.c index 26e3e7a2..6bc0e787 100644 --- a/drivers/dahdi/wctdm.c +++ b/drivers/dahdi/wctdm.c @@ -32,6 +32,7 @@ #include #include #include +#include #include #include "proslic.h" @@ -266,13 +267,17 @@ struct wctdm { unsigned char reg0shadow[NUM_CARDS]; unsigned char reg1shadow[NUM_CARDS]; - unsigned long ioaddr; + void __iomem *ioaddr; dma_addr_t readdma; dma_addr_t writedma; volatile unsigned int *writechunk; /* Double-word aligned write memory */ volatile unsigned int *readchunk; /* Double-word aligned read memory */ struct dahdi_chan _chans[NUM_CARDS]; struct dahdi_chan *chans[NUM_CARDS]; + +#if LINUX_VERSION_CODE > KERNEL_VERSION(5, 6, 19) + struct pm_qos_request pm_qos_req; +#endif }; @@ -466,7 +471,7 @@ static inline void __write_8bits(struct wctdm *wc, unsigned char bits) int x; /* Drop chip select */ wc->ios &= ~BIT_CS; - outb(wc->ios, wc->ioaddr + WC_AUXD); + iowrite8(wc->ios, wc->ioaddr + WC_AUXD); for (x=0;x<8;x++) { /* Send out each bit, MSB first, drop SCLK as we do so */ if (bits & 0x80) @@ -474,40 +479,40 @@ static inline void __write_8bits(struct wctdm *wc, unsigned char bits) else wc->ios &= ~BIT_SDI; wc->ios &= ~BIT_SCLK; - outb(wc->ios, wc->ioaddr + WC_AUXD); + iowrite8(wc->ios, wc->ioaddr + WC_AUXD); /* Now raise SCLK high again and repeat */ wc->ios |= BIT_SCLK; - outb(wc->ios, wc->ioaddr + WC_AUXD); + iowrite8(wc->ios, wc->ioaddr + WC_AUXD); bits <<= 1; } /* Finally raise CS back high again */ wc->ios |= BIT_CS; - outb(wc->ios, wc->ioaddr + WC_AUXD); + iowrite8(wc->ios, wc->ioaddr + WC_AUXD); } static inline void __reset_spi(struct wctdm *wc) { /* Drop chip select and clock once and raise and clock once */ wc->ios |= BIT_SCLK; - outb(wc->ios, wc->ioaddr + WC_AUXD); + iowrite8(wc->ios, wc->ioaddr + WC_AUXD); wc->ios &= ~BIT_CS; - outb(wc->ios, wc->ioaddr + WC_AUXD); + iowrite8(wc->ios, wc->ioaddr + WC_AUXD); wc->ios |= BIT_SDI; wc->ios &= ~BIT_SCLK; - outb(wc->ios, wc->ioaddr + WC_AUXD); + iowrite8(wc->ios, wc->ioaddr + WC_AUXD); /* Now raise SCLK high again and repeat */ wc->ios |= BIT_SCLK; - outb(wc->ios, wc->ioaddr + WC_AUXD); + iowrite8(wc->ios, wc->ioaddr + WC_AUXD); /* Finally raise CS back high again */ wc->ios |= BIT_CS; - outb(wc->ios, wc->ioaddr + WC_AUXD); + iowrite8(wc->ios, wc->ioaddr + WC_AUXD); /* Clock again */ wc->ios &= ~BIT_SCLK; - outb(wc->ios, wc->ioaddr + WC_AUXD); + iowrite8(wc->ios, wc->ioaddr + WC_AUXD); /* Now raise SCLK high again and repeat */ wc->ios |= BIT_SCLK; - outb(wc->ios, wc->ioaddr + WC_AUXD); + iowrite8(wc->ios, wc->ioaddr + WC_AUXD); } @@ -525,24 +530,24 @@ static inline unsigned char __read_8bits(struct wctdm *wc) int x; /* Drop chip select */ wc->ios &= ~BIT_CS; - outb(wc->ios, wc->ioaddr + WC_AUXD); + iowrite8(wc->ios, wc->ioaddr + WC_AUXD); for (x=0;x<8;x++) { res <<= 1; /* Drop SCLK */ wc->ios &= ~BIT_SCLK; - outb(wc->ios, wc->ioaddr + WC_AUXD); + iowrite8(wc->ios, wc->ioaddr + WC_AUXD); /* Now raise SCLK high again */ wc->ios |= BIT_SCLK; - outb(wc->ios, wc->ioaddr + WC_AUXD); + iowrite8(wc->ios, wc->ioaddr + WC_AUXD); /* Read back the value */ - c = inb(wc->ioaddr + WC_AUXR); + c = ioread8(wc->ioaddr + WC_AUXR); if (c & BIT_SDO) res |= 1; } /* Finally raise CS back high again */ wc->ios |= BIT_CS; - outb(wc->ios, wc->ioaddr + WC_AUXD); + iowrite8(wc->ios, wc->ioaddr + WC_AUXD); /* And return our result */ return res; @@ -550,12 +555,12 @@ static inline unsigned char __read_8bits(struct wctdm *wc) static void __wctdm_setcreg(struct wctdm *wc, unsigned char reg, unsigned char val) { - outb(val, wc->ioaddr + WC_REGBASE + ((reg & 0xf) << 2)); + iowrite8(val, wc->ioaddr + WC_REGBASE + ((reg & 0xf) << 2)); } static unsigned char __wctdm_getcreg(struct wctdm *wc, unsigned char reg) { - return inb(wc->ioaddr + WC_REGBASE + ((reg & 0xf) << 2)); + return ioread8(wc->ioaddr + WC_REGBASE + ((reg & 0xf) << 2)); } static inline void __wctdm_setcard(struct wctdm *wc, int card) @@ -1139,12 +1144,12 @@ static irqreturn_t wctdm_interrupt(int irq, void *dev_id) int x; int mode; - ints = inb(wc->ioaddr + WC_INTSTAT); + ints = ioread8(wc->ioaddr + WC_INTSTAT); if (!ints) return IRQ_NONE; - outb(ints, wc->ioaddr + WC_INTSTAT); + iowrite8(ints, wc->ioaddr + WC_INTSTAT); if (ints & 0x10) { /* Stop DMA, wait for watchdog */ @@ -2449,10 +2454,10 @@ static int wctdm_hardware_init(struct wctdm *wc) int failed; /* Signal Reset */ - outb(0x01, wc->ioaddr + WC_CNTL); + iowrite8(0x01, wc->ioaddr + WC_CNTL); /* Check Freshmaker chip */ - x=inb(wc->ioaddr + WC_CNTL); + x=ioread8(wc->ioaddr + WC_CNTL); ver = __wctdm_getcreg(wc, WC_VER); failed = 0; if (ver != 0x59) { @@ -2485,44 +2490,44 @@ static int wctdm_hardware_init(struct wctdm *wc) } /* Reset PCI Interface chip and registers (and serial) */ - outb(0x06, wc->ioaddr + WC_CNTL); + iowrite8(0x06, wc->ioaddr + WC_CNTL); /* Setup our proper outputs for when we switch for our "serial" port */ wc->ios = BIT_CS | BIT_SCLK | BIT_SDI; - outb(wc->ios, wc->ioaddr + WC_AUXD); + iowrite8(wc->ios, wc->ioaddr + WC_AUXD); /* Set all to outputs except AUX 5, which is an input */ - outb(0xdf, wc->ioaddr + WC_AUXC); + iowrite8(0xdf, wc->ioaddr + WC_AUXC); /* Select alternate function for AUX0 */ - outb(0x4, wc->ioaddr + WC_AUXFUNC); + iowrite8(0x4, wc->ioaddr + WC_AUXFUNC); /* Wait 1/4 of a sec */ wait_just_a_bit(HZ/4); /* Back to normal, with automatic DMA wrap around */ - outb(0x30 | 0x01, wc->ioaddr + WC_CNTL); + iowrite8(0x30 | 0x01, wc->ioaddr + WC_CNTL); /* Make sure serial port and DMA are out of reset */ - outb(inb(wc->ioaddr + WC_CNTL) & 0xf9, wc->ioaddr + WC_CNTL); + iowrite8(ioread8(wc->ioaddr + WC_CNTL) & 0xf9, wc->ioaddr + WC_CNTL); /* Configure serial port for MSB->LSB operation */ - outb(0xc1, wc->ioaddr + WC_SERCTL); + iowrite8(0xc1, wc->ioaddr + WC_SERCTL); /* Delay FSC by 0 so it's properly aligned */ - outb(0x0, wc->ioaddr + WC_FSCDELAY); + iowrite8(0x0, wc->ioaddr + WC_FSCDELAY); /* Setup DMA Addresses */ - outl(wc->writedma, wc->ioaddr + WC_DMAWS); /* Write start */ - outl(wc->writedma + DAHDI_CHUNKSIZE * 4 - 4, wc->ioaddr + WC_DMAWI); /* Middle (interrupt) */ - outl(wc->writedma + DAHDI_CHUNKSIZE * 8 - 4, wc->ioaddr + WC_DMAWE); /* End */ + iowrite32(wc->writedma, wc->ioaddr + WC_DMAWS); /* Write start */ + iowrite32(wc->writedma + DAHDI_CHUNKSIZE * 4 - 4, wc->ioaddr + WC_DMAWI); /* Middle (interrupt) */ + iowrite32(wc->writedma + DAHDI_CHUNKSIZE * 8 - 4, wc->ioaddr + WC_DMAWE); /* End */ - outl(wc->readdma, wc->ioaddr + WC_DMARS); /* Read start */ - outl(wc->readdma + DAHDI_CHUNKSIZE * 4 - 4, wc->ioaddr + WC_DMARI); /* Middle (interrupt) */ - outl(wc->readdma + DAHDI_CHUNKSIZE * 8 - 4, wc->ioaddr + WC_DMARE); /* End */ + iowrite32(wc->readdma, wc->ioaddr + WC_DMARS); /* Read start */ + iowrite32(wc->readdma + DAHDI_CHUNKSIZE * 4 - 4, wc->ioaddr + WC_DMARI); /* Middle (interrupt) */ + iowrite32(wc->readdma + DAHDI_CHUNKSIZE * 8 - 4, wc->ioaddr + WC_DMARE); /* End */ /* Clear interrupts */ - outb(0xff, wc->ioaddr + WC_INTSTAT); + iowrite8(0xff, wc->ioaddr + WC_INTSTAT); /* Wait 1/4 of a second more */ wait_just_a_bit(HZ/4); @@ -2574,48 +2579,49 @@ static int wctdm_hardware_init(struct wctdm *wc) static void wctdm_enable_interrupts(struct wctdm *wc) { /* Enable interrupts (we care about all of them) */ - outb(0x3f, wc->ioaddr + WC_MASK0); + iowrite8(0x3f, wc->ioaddr + WC_MASK0); /* No external interrupts */ - outb(0x00, wc->ioaddr + WC_MASK1); + iowrite8(0x00, wc->ioaddr + WC_MASK1); } static void wctdm_restart_dma(struct wctdm *wc) { /* Reset Master and TDM */ - outb(0x01, wc->ioaddr + WC_CNTL); - outb(0x01, wc->ioaddr + WC_OPER); + iowrite8(0x01, wc->ioaddr + WC_CNTL); + iowrite8(0x01, wc->ioaddr + WC_OPER); } static void wctdm_start_dma(struct wctdm *wc) { /* Reset Master and TDM */ - outb(0x0f, wc->ioaddr + WC_CNTL); + iowrite8(0x0f, wc->ioaddr + WC_CNTL); set_current_state(TASK_INTERRUPTIBLE); schedule_timeout(1); - outb(0x01, wc->ioaddr + WC_CNTL); - outb(0x01, wc->ioaddr + WC_OPER); + iowrite8(0x01, wc->ioaddr + WC_CNTL); + iowrite8(0x01, wc->ioaddr + WC_OPER); } static void wctdm_stop_dma(struct wctdm *wc) { - outb(0x00, wc->ioaddr + WC_OPER); + iowrite8(0x00, wc->ioaddr + WC_OPER); } static void wctdm_reset_tdm(struct wctdm *wc) { /* Reset TDM */ - outb(0x0f, wc->ioaddr + WC_CNTL); + iowrite8(0x0f, wc->ioaddr + WC_CNTL); } static void wctdm_disable_interrupts(struct wctdm *wc) { - outb(0x00, wc->ioaddr + WC_MASK0); - outb(0x00, wc->ioaddr + WC_MASK1); + iowrite8(0x00, wc->ioaddr + WC_MASK0); + iowrite8(0x00, wc->ioaddr + WC_MASK1); } static int __devinit wctdm_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) { int res; + int use_legacy_io = 1; struct wctdm *wc; struct wctdm_desc *d = (struct wctdm_desc *)ent->driver_data; int x; @@ -2642,23 +2648,46 @@ static int __devinit wctdm_init_one(struct pci_dev *pdev, const struct pci_devic } spin_lock_init(&wc->lock); wc->curcard = -1; - wc->ioaddr = pci_resource_start(pdev, 0); + if (!(pci_resource_flags(pdev, 0) & IORESOURCE_IO)) { + printk(KERN_NOTICE "wctdm: Switching to PCI MMIO resources instead of legacy I/O cycles\n"); + use_legacy_io = 0; + } + if (!use_legacy_io) { + if (!(pci_resource_flags(pdev, 1) & IORESOURCE_MEM)) { + printk(KERN_NOTICE "wctdm: Unable to access PCI memory resource type\n"); + return -EIO; + } + } + if (pci_request_regions(pdev, "wctdm")) { + printk(KERN_NOTICE "wctdm: Unable to reserve PCI resources\n"); + return -EIO; + } + if (use_legacy_io) + wc->ioaddr = pci_iomap(pdev, 0, 0); + else + wc->ioaddr = pci_iomap(pdev, 1, 0); + if (!wc->ioaddr) { + pci_release_regions(pdev); + printk(KERN_NOTICE "wctdm: Unable to access PCI BAR0/1 resource\n"); + return -EIO; + } + /* Keep track of whether we need to free the region */ + wc->freeregion = 1; wc->dev = pdev; wc->pos = x; wc->variety = d->name; for (y=0;yflags[y] = d->flags; - /* Keep track of whether we need to free the region */ - if (request_region(wc->ioaddr, 0xff, "wctdm")) - wc->freeregion = 1; /* Allocate enough memory for two zt chunks, receive and transmit. Each sample uses 32 bits. Allocate an extra set just for control too */ wc->writechunk = pci_alloc_consistent(pdev, DAHDI_MAX_CHUNKSIZE * 2 * 2 * 2 * 4, &wc->writedma); if (!wc->writechunk) { printk(KERN_NOTICE "wctdm: Unable to allocate DMA-able memory\n"); - if (wc->freeregion) - release_region(wc->ioaddr, 0xff); + if (wc->freeregion) { + pci_iounmap(pdev, wc->ioaddr); + pci_release_regions(pdev); + } return -ENOMEM; } @@ -2668,12 +2697,14 @@ static int __devinit wctdm_init_one(struct pci_dev *pdev, const struct pci_devic if (wctdm_initialize(wc)) { printk(KERN_NOTICE "wctdm: Unable to intialize FXS\n"); /* Set Reset Low */ - x=inb(wc->ioaddr + WC_CNTL); - outb((~0x1)&x, wc->ioaddr + WC_CNTL); + x=ioread8(wc->ioaddr + WC_CNTL); + iowrite8((~0x1)&x, wc->ioaddr + WC_CNTL); /* Free Resources */ free_irq(pdev->irq, wc); - if (wc->freeregion) - release_region(wc->ioaddr, 0xff); + if (wc->freeregion) { + pci_iounmap(pdev, wc->ioaddr); + pci_release_regions(pdev); + } pci_free_consistent(pdev, DAHDI_MAX_CHUNKSIZE * 2 * 2 * 2 * 4, (void *)wc->writechunk, wc->writedma); kfree(wc); return -EIO; @@ -2688,8 +2719,10 @@ static int __devinit wctdm_init_one(struct pci_dev *pdev, const struct pci_devic if (request_irq(pdev->irq, wctdm_interrupt, IRQF_SHARED, "wctdm", wc)) { printk(KERN_NOTICE "wctdm: Unable to request IRQ %d\n", pdev->irq); - if (wc->freeregion) - release_region(wc->ioaddr, 0xff); + if (wc->freeregion) { + pci_iounmap(pdev, wc->ioaddr); + pci_release_regions(pdev); + } pci_free_consistent(pdev, DAHDI_MAX_CHUNKSIZE * 2 * 2 * 2 * 4, (void *)wc->writechunk, wc->writedma); pci_set_drvdata(pdev, NULL); kfree(wc); @@ -2699,12 +2732,14 @@ static int __devinit wctdm_init_one(struct pci_dev *pdev, const struct pci_devic if (wctdm_hardware_init(wc)) { /* Set Reset Low */ - x=inb(wc->ioaddr + WC_CNTL); - outb((~0x1)&x, wc->ioaddr + WC_CNTL); + x=ioread8(wc->ioaddr + WC_CNTL); + iowrite8((~0x1)&x, wc->ioaddr + WC_CNTL); /* Free Resources */ free_irq(pdev->irq, wc); - if (wc->freeregion) - release_region(wc->ioaddr, 0xff); + if (wc->freeregion) { + pci_iounmap(pdev, wc->ioaddr); + pci_release_regions(pdev); + } pci_free_consistent(pdev, DAHDI_MAX_CHUNKSIZE * 2 * 2 * 2 * 4, (void *)wc->writechunk, wc->writedma); pci_set_drvdata(pdev, NULL); dahdi_unregister_device(wc->ddev); @@ -2717,6 +2752,11 @@ static int __devinit wctdm_init_one(struct pci_dev *pdev, const struct pci_devic wctdm_post_initialize(wc); +#if LINUX_VERSION_CODE > KERNEL_VERSION(5, 6, 19) + // Enforce maximum sleep state latency of 10us to prevent modern deep idle CPU states from causing severe audio distortions + cpu_latency_qos_add_request(&wc->pm_qos_req, 10); +#endif + /* Enable interrupts */ wctdm_enable_interrupts(wc); /* Initialize Write/Buffers to all blank data */ @@ -2741,8 +2781,10 @@ static int __devinit wctdm_init_one(struct pci_dev *pdev, const struct pci_devic static void wctdm_release(struct wctdm *wc) { dahdi_unregister_device(wc->ddev); - if (wc->freeregion) - release_region(wc->ioaddr, 0xff); + if (wc->freeregion) { + pci_iounmap(wc->dev, wc->ioaddr); + pci_release_regions(wc->dev); + } kfree(wc->ddev->location); dahdi_free_device(wc->ddev); @@ -2762,13 +2804,18 @@ static void __devexit wctdm_remove_one(struct pci_dev *pdev) /* In case hardware is still there */ wctdm_disable_interrupts(wc); + +#if LINUX_VERSION_CODE > KERNEL_VERSION(5, 6, 19) + /* Remove CPU latency requirement */ + cpu_latency_qos_remove_request(&wc->pm_qos_req); +#endif /* Immediately free resources */ pci_free_consistent(pdev, DAHDI_MAX_CHUNKSIZE * 2 * 2 * 2 * 4, (void *)wc->writechunk, wc->writedma); free_irq(pdev->irq, wc); /* Reset PCI chip and registers */ - outb(0x0e, wc->ioaddr + WC_CNTL); + iowrite8(0x0e, wc->ioaddr + WC_CNTL); /* Release span, possibly delayed */ if (!wc->usecount)