1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 22b49e0c5SAndy Shevchenko /* 32b49e0c5SAndy Shevchenko * PCI driver for the High Speed UART DMA 42b49e0c5SAndy Shevchenko * 52b49e0c5SAndy Shevchenko * Copyright (C) 2015 Intel Corporation 62b49e0c5SAndy Shevchenko * Author: Andy Shevchenko <andriy.shevchenko@linux.intel.com> 72b49e0c5SAndy Shevchenko * 82b49e0c5SAndy Shevchenko * Partially based on the bits found in drivers/tty/serial/mfd.c. 92b49e0c5SAndy Shevchenko */ 102b49e0c5SAndy Shevchenko 112b49e0c5SAndy Shevchenko #include <linux/bitops.h> 122b49e0c5SAndy Shevchenko #include <linux/device.h> 13*9c060026SAndy Shevchenko #include <linux/interrupt.h> 142b49e0c5SAndy Shevchenko #include <linux/module.h> 152b49e0c5SAndy Shevchenko #include <linux/pci.h> 162b49e0c5SAndy Shevchenko 172b49e0c5SAndy Shevchenko #include "hsu.h" 182b49e0c5SAndy Shevchenko 192b49e0c5SAndy Shevchenko #define HSU_PCI_DMASR 0x00 202b49e0c5SAndy Shevchenko #define HSU_PCI_DMAISR 0x04 212b49e0c5SAndy Shevchenko 222b49e0c5SAndy Shevchenko #define HSU_PCI_CHAN_OFFSET 0x100 232b49e0c5SAndy Shevchenko 244831e0d9SAndy Shevchenko #define PCI_DEVICE_ID_INTEL_MFLD_HSU_DMA 0x081e 254831e0d9SAndy Shevchenko #define PCI_DEVICE_ID_INTEL_MRFLD_HSU_DMA 0x1192 264831e0d9SAndy Shevchenko 272b49e0c5SAndy Shevchenko static irqreturn_t hsu_pci_irq(int irq, void *dev) 282b49e0c5SAndy Shevchenko { 292b49e0c5SAndy Shevchenko struct hsu_dma_chip *chip = dev; 30d6b76a45SAndy Shevchenko unsigned long dmaisr; 312b49e0c5SAndy Shevchenko unsigned short i; 32d6b76a45SAndy Shevchenko u32 status; 33d2f5a731SAndy Shevchenko int ret = 0; 34c6f82787SChuah, Kim Tatt int err; 352b49e0c5SAndy Shevchenko 362b49e0c5SAndy Shevchenko dmaisr = readl(chip->regs + HSU_PCI_DMAISR); 37d6b76a45SAndy Shevchenko for_each_set_bit(i, &dmaisr, chip->hsu->nr_channels) { 38c6f82787SChuah, Kim Tatt err = hsu_dma_get_status(chip, i, &status); 39c6f82787SChuah, Kim Tatt if (err > 0) 40d2f5a731SAndy Shevchenko ret |= 1; 41c6f82787SChuah, Kim Tatt else if (err == 0) 42c6f82787SChuah, Kim Tatt ret |= hsu_dma_do_irq(chip, i, status); 43c6f82787SChuah, Kim Tatt } 442b49e0c5SAndy Shevchenko 45d2f5a731SAndy Shevchenko return IRQ_RETVAL(ret); 462b49e0c5SAndy Shevchenko } 472b49e0c5SAndy Shevchenko 48d5988dccSAndy Shevchenko static void hsu_pci_dma_remove(void *chip) 49d5988dccSAndy Shevchenko { 50d5988dccSAndy Shevchenko hsu_dma_remove(chip); 51d5988dccSAndy Shevchenko } 52d5988dccSAndy Shevchenko 532b49e0c5SAndy Shevchenko static int hsu_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) 542b49e0c5SAndy Shevchenko { 55d5988dccSAndy Shevchenko struct device *dev = &pdev->dev; 562b49e0c5SAndy Shevchenko struct hsu_dma_chip *chip; 572b49e0c5SAndy Shevchenko int ret; 582b49e0c5SAndy Shevchenko 592b49e0c5SAndy Shevchenko ret = pcim_enable_device(pdev); 602b49e0c5SAndy Shevchenko if (ret) 612b49e0c5SAndy Shevchenko return ret; 622b49e0c5SAndy Shevchenko 632b49e0c5SAndy Shevchenko ret = pcim_iomap_regions(pdev, BIT(0), pci_name(pdev)); 642b49e0c5SAndy Shevchenko if (ret) { 652b49e0c5SAndy Shevchenko dev_err(&pdev->dev, "I/O memory remapping failed\n"); 662b49e0c5SAndy Shevchenko return ret; 672b49e0c5SAndy Shevchenko } 682b49e0c5SAndy Shevchenko 692b49e0c5SAndy Shevchenko pci_set_master(pdev); 702b49e0c5SAndy Shevchenko pci_try_set_mwi(pdev); 712b49e0c5SAndy Shevchenko 72bec897e0SQing Wang ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); 732b49e0c5SAndy Shevchenko if (ret) 742b49e0c5SAndy Shevchenko return ret; 752b49e0c5SAndy Shevchenko 762b49e0c5SAndy Shevchenko chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL); 772b49e0c5SAndy Shevchenko if (!chip) 782b49e0c5SAndy Shevchenko return -ENOMEM; 792b49e0c5SAndy Shevchenko 80e9bb8a9dSAndy Shevchenko ret = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_ALL_TYPES); 81e9bb8a9dSAndy Shevchenko if (ret < 0) 82e9bb8a9dSAndy Shevchenko return ret; 83e9bb8a9dSAndy Shevchenko 842b49e0c5SAndy Shevchenko chip->dev = &pdev->dev; 852b49e0c5SAndy Shevchenko chip->regs = pcim_iomap_table(pdev)[0]; 862b49e0c5SAndy Shevchenko chip->length = pci_resource_len(pdev, 0); 872b49e0c5SAndy Shevchenko chip->offset = HSU_PCI_CHAN_OFFSET; 88e9bb8a9dSAndy Shevchenko chip->irq = pci_irq_vector(pdev, 0); 892b49e0c5SAndy Shevchenko 902b49e0c5SAndy Shevchenko ret = hsu_dma_probe(chip); 912b49e0c5SAndy Shevchenko if (ret) 922b49e0c5SAndy Shevchenko return ret; 932b49e0c5SAndy Shevchenko 94d5988dccSAndy Shevchenko ret = devm_add_action_or_reset(dev, hsu_pci_dma_remove, chip); 952b49e0c5SAndy Shevchenko if (ret) 96d5988dccSAndy Shevchenko return ret; 97d5988dccSAndy Shevchenko 98d5988dccSAndy Shevchenko ret = devm_request_irq(dev, chip->irq, hsu_pci_irq, 0, "hsu_dma_pci", chip); 99d5988dccSAndy Shevchenko if (ret) 100d5988dccSAndy Shevchenko return ret; 1012b49e0c5SAndy Shevchenko 102035b73b2SFerry Toth /* 103035b73b2SFerry Toth * On Intel Tangier B0 and Anniedale the interrupt line, disregarding 104035b73b2SFerry Toth * to have different numbers, is shared between HSU DMA and UART IPs. 105035b73b2SFerry Toth * Thus on such SoCs we are expecting that IRQ handler is called in 106035b73b2SFerry Toth * UART driver only. Instead of handling the spurious interrupt 107035b73b2SFerry Toth * from HSU DMA here and waste CPU time and delay HSU UART interrupt 108035b73b2SFerry Toth * handling, disable the interrupt entirely. 109035b73b2SFerry Toth */ 110035b73b2SFerry Toth if (pdev->device == PCI_DEVICE_ID_INTEL_MRFLD_HSU_DMA) 111035b73b2SFerry Toth disable_irq_nosync(chip->irq); 112035b73b2SFerry Toth 1132b49e0c5SAndy Shevchenko pci_set_drvdata(pdev, chip); 1142b49e0c5SAndy Shevchenko 1152b49e0c5SAndy Shevchenko return 0; 1162b49e0c5SAndy Shevchenko } 1172b49e0c5SAndy Shevchenko 1182b49e0c5SAndy Shevchenko static const struct pci_device_id hsu_pci_id_table[] = { 1194831e0d9SAndy Shevchenko { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_MFLD_HSU_DMA), 0 }, 1204831e0d9SAndy Shevchenko { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_MRFLD_HSU_DMA), 0 }, 1212b49e0c5SAndy Shevchenko { } 1222b49e0c5SAndy Shevchenko }; 1232b49e0c5SAndy Shevchenko MODULE_DEVICE_TABLE(pci, hsu_pci_id_table); 1242b49e0c5SAndy Shevchenko 1252b49e0c5SAndy Shevchenko static struct pci_driver hsu_pci_driver = { 1262b49e0c5SAndy Shevchenko .name = "hsu_dma_pci", 1272b49e0c5SAndy Shevchenko .id_table = hsu_pci_id_table, 1282b49e0c5SAndy Shevchenko .probe = hsu_pci_probe, 1292b49e0c5SAndy Shevchenko }; 1302b49e0c5SAndy Shevchenko 1312b49e0c5SAndy Shevchenko module_pci_driver(hsu_pci_driver); 1322b49e0c5SAndy Shevchenko 1332b49e0c5SAndy Shevchenko MODULE_LICENSE("GPL v2"); 1342b49e0c5SAndy Shevchenko MODULE_DESCRIPTION("High Speed UART DMA PCI driver"); 1352b49e0c5SAndy Shevchenko MODULE_AUTHOR("Andy Shevchenko <andriy.shevchenko@linux.intel.com>"); 136