Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * UIO driver fo Humusoft MF624 DAQ card.
0004  * Copyright (C) 2011 Rostislav Lisovy <lisovy@gmail.com>,
0005  *                    Czech Technical University in Prague
0006  */
0007 
0008 #include <linux/init.h>
0009 #include <linux/module.h>
0010 #include <linux/device.h>
0011 #include <linux/pci.h>
0012 #include <linux/slab.h>
0013 #include <linux/io.h>
0014 #include <linux/kernel.h>
0015 #include <linux/uio_driver.h>
0016 
0017 #define PCI_VENDOR_ID_HUMUSOFT      0x186c
0018 #define PCI_DEVICE_ID_MF624     0x0624
0019 #define PCI_SUBVENDOR_ID_HUMUSOFT   0x186c
0020 #define PCI_SUBDEVICE_DEVICE        0x0624
0021 
0022 /* BAR0 Interrupt control/status register */
0023 #define INTCSR              0x4C
0024 #define INTCSR_ADINT_ENABLE     (1 << 0)
0025 #define INTCSR_CTR4INT_ENABLE       (1 << 3)
0026 #define INTCSR_PCIINT_ENABLE        (1 << 6)
0027 #define INTCSR_ADINT_STATUS     (1 << 2)
0028 #define INTCSR_CTR4INT_STATUS       (1 << 5)
0029 
0030 enum mf624_interrupt_source {ADC, CTR4, ALL};
0031 
0032 static void mf624_disable_interrupt(enum mf624_interrupt_source source,
0033                  struct uio_info *info)
0034 {
0035     void __iomem *INTCSR_reg = info->mem[0].internal_addr + INTCSR;
0036 
0037     switch (source) {
0038     case ADC:
0039         iowrite32(ioread32(INTCSR_reg)
0040             & ~(INTCSR_ADINT_ENABLE | INTCSR_PCIINT_ENABLE),
0041             INTCSR_reg);
0042         break;
0043 
0044     case CTR4:
0045         iowrite32(ioread32(INTCSR_reg)
0046             & ~(INTCSR_CTR4INT_ENABLE | INTCSR_PCIINT_ENABLE),
0047             INTCSR_reg);
0048         break;
0049 
0050     case ALL:
0051     default:
0052         iowrite32(ioread32(INTCSR_reg)
0053             & ~(INTCSR_ADINT_ENABLE | INTCSR_CTR4INT_ENABLE
0054                 | INTCSR_PCIINT_ENABLE),
0055             INTCSR_reg);
0056         break;
0057     }
0058 }
0059 
0060 static void mf624_enable_interrupt(enum mf624_interrupt_source source,
0061                 struct uio_info *info)
0062 {
0063     void __iomem *INTCSR_reg = info->mem[0].internal_addr + INTCSR;
0064 
0065     switch (source) {
0066     case ADC:
0067         iowrite32(ioread32(INTCSR_reg)
0068             | INTCSR_ADINT_ENABLE | INTCSR_PCIINT_ENABLE,
0069             INTCSR_reg);
0070         break;
0071 
0072     case CTR4:
0073         iowrite32(ioread32(INTCSR_reg)
0074             | INTCSR_CTR4INT_ENABLE | INTCSR_PCIINT_ENABLE,
0075             INTCSR_reg);
0076         break;
0077 
0078     case ALL:
0079     default:
0080         iowrite32(ioread32(INTCSR_reg)
0081             | INTCSR_ADINT_ENABLE | INTCSR_CTR4INT_ENABLE
0082             | INTCSR_PCIINT_ENABLE,
0083             INTCSR_reg);
0084         break;
0085     }
0086 }
0087 
0088 static irqreturn_t mf624_irq_handler(int irq, struct uio_info *info)
0089 {
0090     void __iomem *INTCSR_reg = info->mem[0].internal_addr + INTCSR;
0091 
0092     if ((ioread32(INTCSR_reg) & INTCSR_ADINT_ENABLE)
0093         && (ioread32(INTCSR_reg) & INTCSR_ADINT_STATUS)) {
0094         mf624_disable_interrupt(ADC, info);
0095         return IRQ_HANDLED;
0096     }
0097 
0098     if ((ioread32(INTCSR_reg) & INTCSR_CTR4INT_ENABLE)
0099         && (ioread32(INTCSR_reg) & INTCSR_CTR4INT_STATUS)) {
0100         mf624_disable_interrupt(CTR4, info);
0101         return IRQ_HANDLED;
0102     }
0103 
0104     return IRQ_NONE;
0105 }
0106 
0107 static int mf624_irqcontrol(struct uio_info *info, s32 irq_on)
0108 {
0109     if (irq_on == 0)
0110         mf624_disable_interrupt(ALL, info);
0111     else if (irq_on == 1)
0112         mf624_enable_interrupt(ALL, info);
0113 
0114     return 0;
0115 }
0116 
0117 static int mf624_setup_mem(struct pci_dev *dev, int bar, struct uio_mem *mem, const char *name)
0118 {
0119     resource_size_t start = pci_resource_start(dev, bar);
0120     resource_size_t len = pci_resource_len(dev, bar);
0121 
0122     mem->name = name;
0123     mem->addr = start & PAGE_MASK;
0124     mem->offs = start & ~PAGE_MASK;
0125     if (!mem->addr)
0126         return -ENODEV;
0127     mem->size = ((start & ~PAGE_MASK) + len + PAGE_SIZE - 1) & PAGE_MASK;
0128     mem->memtype = UIO_MEM_PHYS;
0129     mem->internal_addr = pci_ioremap_bar(dev, bar);
0130     if (!mem->internal_addr)
0131         return -ENODEV;
0132     return 0;
0133 }
0134 
0135 static int mf624_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
0136 {
0137     struct uio_info *info;
0138 
0139     info = devm_kzalloc(&dev->dev, sizeof(struct uio_info), GFP_KERNEL);
0140     if (!info)
0141         return -ENOMEM;
0142 
0143     if (pci_enable_device(dev))
0144         return -ENODEV;
0145 
0146     if (pci_request_regions(dev, "mf624"))
0147         goto out_disable;
0148 
0149     info->name = "mf624";
0150     info->version = "0.0.1";
0151 
0152     /* Note: Datasheet says device uses BAR0, BAR1, BAR2 -- do not trust it */
0153 
0154     /* BAR0 */
0155     if (mf624_setup_mem(dev, 0, &info->mem[0], "PCI chipset, interrupts, status "
0156                 "bits, special functions"))
0157         goto out_release;
0158     /* BAR2 */
0159     if (mf624_setup_mem(dev, 2, &info->mem[1], "ADC, DAC, DIO"))
0160         goto out_unmap0;
0161 
0162     /* BAR4 */
0163     if (mf624_setup_mem(dev, 4, &info->mem[2], "Counter/timer chip"))
0164         goto out_unmap1;
0165 
0166     info->irq = dev->irq;
0167     info->irq_flags = IRQF_SHARED;
0168     info->handler = mf624_irq_handler;
0169 
0170     info->irqcontrol = mf624_irqcontrol;
0171 
0172     if (uio_register_device(&dev->dev, info))
0173         goto out_unmap2;
0174 
0175     pci_set_drvdata(dev, info);
0176 
0177     return 0;
0178 
0179 out_unmap2:
0180     iounmap(info->mem[2].internal_addr);
0181 out_unmap1:
0182     iounmap(info->mem[1].internal_addr);
0183 out_unmap0:
0184     iounmap(info->mem[0].internal_addr);
0185 
0186 out_release:
0187     pci_release_regions(dev);
0188 
0189 out_disable:
0190     pci_disable_device(dev);
0191 
0192     return -ENODEV;
0193 }
0194 
0195 static void mf624_pci_remove(struct pci_dev *dev)
0196 {
0197     struct uio_info *info = pci_get_drvdata(dev);
0198 
0199     mf624_disable_interrupt(ALL, info);
0200 
0201     uio_unregister_device(info);
0202     pci_release_regions(dev);
0203     pci_disable_device(dev);
0204 
0205     iounmap(info->mem[0].internal_addr);
0206     iounmap(info->mem[1].internal_addr);
0207     iounmap(info->mem[2].internal_addr);
0208 }
0209 
0210 static const struct pci_device_id mf624_pci_id[] = {
0211     { PCI_DEVICE(PCI_VENDOR_ID_HUMUSOFT, PCI_DEVICE_ID_MF624) },
0212     { 0, }
0213 };
0214 
0215 static struct pci_driver mf624_pci_driver = {
0216     .name = "mf624",
0217     .id_table = mf624_pci_id,
0218     .probe = mf624_pci_probe,
0219     .remove = mf624_pci_remove,
0220 };
0221 MODULE_DEVICE_TABLE(pci, mf624_pci_id);
0222 
0223 module_pci_driver(mf624_pci_driver);
0224 MODULE_LICENSE("GPL v2");
0225 MODULE_AUTHOR("Rostislav Lisovy <lisovy@gmail.com>");