Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * PCI driver for the High Speed UART DMA
0004  *
0005  * Copyright (C) 2015 Intel Corporation
0006  * Author: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
0007  *
0008  * Partially based on the bits found in drivers/tty/serial/mfd.c.
0009  */
0010 
0011 #include <linux/bitops.h>
0012 #include <linux/device.h>
0013 #include <linux/module.h>
0014 #include <linux/pci.h>
0015 
0016 #include "hsu.h"
0017 
0018 #define HSU_PCI_DMASR       0x00
0019 #define HSU_PCI_DMAISR      0x04
0020 
0021 #define HSU_PCI_CHAN_OFFSET 0x100
0022 
0023 #define PCI_DEVICE_ID_INTEL_MFLD_HSU_DMA    0x081e
0024 #define PCI_DEVICE_ID_INTEL_MRFLD_HSU_DMA   0x1192
0025 
0026 static irqreturn_t hsu_pci_irq(int irq, void *dev)
0027 {
0028     struct hsu_dma_chip *chip = dev;
0029     u32 dmaisr;
0030     u32 status;
0031     unsigned short i;
0032     int ret = 0;
0033     int err;
0034 
0035     dmaisr = readl(chip->regs + HSU_PCI_DMAISR);
0036     for (i = 0; i < chip->hsu->nr_channels; i++) {
0037         if (dmaisr & 0x1) {
0038             err = hsu_dma_get_status(chip, i, &status);
0039             if (err > 0)
0040                 ret |= 1;
0041             else if (err == 0)
0042                 ret |= hsu_dma_do_irq(chip, i, status);
0043         }
0044         dmaisr >>= 1;
0045     }
0046 
0047     return IRQ_RETVAL(ret);
0048 }
0049 
0050 static int hsu_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
0051 {
0052     struct hsu_dma_chip *chip;
0053     int ret;
0054 
0055     ret = pcim_enable_device(pdev);
0056     if (ret)
0057         return ret;
0058 
0059     ret = pcim_iomap_regions(pdev, BIT(0), pci_name(pdev));
0060     if (ret) {
0061         dev_err(&pdev->dev, "I/O memory remapping failed\n");
0062         return ret;
0063     }
0064 
0065     pci_set_master(pdev);
0066     pci_try_set_mwi(pdev);
0067 
0068     ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
0069     if (ret)
0070         return ret;
0071 
0072     chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL);
0073     if (!chip)
0074         return -ENOMEM;
0075 
0076     ret = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_ALL_TYPES);
0077     if (ret < 0)
0078         return ret;
0079 
0080     chip->dev = &pdev->dev;
0081     chip->regs = pcim_iomap_table(pdev)[0];
0082     chip->length = pci_resource_len(pdev, 0);
0083     chip->offset = HSU_PCI_CHAN_OFFSET;
0084     chip->irq = pci_irq_vector(pdev, 0);
0085 
0086     ret = hsu_dma_probe(chip);
0087     if (ret)
0088         return ret;
0089 
0090     ret = request_irq(chip->irq, hsu_pci_irq, 0, "hsu_dma_pci", chip);
0091     if (ret)
0092         goto err_register_irq;
0093 
0094     /*
0095      * On Intel Tangier B0 and Anniedale the interrupt line, disregarding
0096      * to have different numbers, is shared between HSU DMA and UART IPs.
0097      * Thus on such SoCs we are expecting that IRQ handler is called in
0098      * UART driver only. Instead of handling the spurious interrupt
0099      * from HSU DMA here and waste CPU time and delay HSU UART interrupt
0100      * handling, disable the interrupt entirely.
0101      */
0102     if (pdev->device == PCI_DEVICE_ID_INTEL_MRFLD_HSU_DMA)
0103         disable_irq_nosync(chip->irq);
0104 
0105     pci_set_drvdata(pdev, chip);
0106 
0107     return 0;
0108 
0109 err_register_irq:
0110     hsu_dma_remove(chip);
0111     return ret;
0112 }
0113 
0114 static void hsu_pci_remove(struct pci_dev *pdev)
0115 {
0116     struct hsu_dma_chip *chip = pci_get_drvdata(pdev);
0117 
0118     free_irq(chip->irq, chip);
0119     hsu_dma_remove(chip);
0120 }
0121 
0122 static const struct pci_device_id hsu_pci_id_table[] = {
0123     { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_MFLD_HSU_DMA), 0 },
0124     { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_MRFLD_HSU_DMA), 0 },
0125     { }
0126 };
0127 MODULE_DEVICE_TABLE(pci, hsu_pci_id_table);
0128 
0129 static struct pci_driver hsu_pci_driver = {
0130     .name       = "hsu_dma_pci",
0131     .id_table   = hsu_pci_id_table,
0132     .probe      = hsu_pci_probe,
0133     .remove     = hsu_pci_remove,
0134 };
0135 
0136 module_pci_driver(hsu_pci_driver);
0137 
0138 MODULE_LICENSE("GPL v2");
0139 MODULE_DESCRIPTION("High Speed UART DMA PCI driver");
0140 MODULE_AUTHOR("Andy Shevchenko <andriy.shevchenko@linux.intel.com>");