linux/drivers/uio/uio_mf624.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 * UIO driver fo Humusoft MF624 DAQ card.
   4 * Copyright (C) 2011 Rostislav Lisovy <lisovy@gmail.com>,
   5 *                    Czech Technical University in Prague
   6 */
   7
   8#include <linux/init.h>
   9#include <linux/module.h>
  10#include <linux/device.h>
  11#include <linux/pci.h>
  12#include <linux/slab.h>
  13#include <linux/io.h>
  14#include <linux/kernel.h>
  15#include <linux/uio_driver.h>
  16
  17#define PCI_VENDOR_ID_HUMUSOFT          0x186c
  18#define PCI_DEVICE_ID_MF624             0x0624
  19#define PCI_SUBVENDOR_ID_HUMUSOFT       0x186c
  20#define PCI_SUBDEVICE_DEVICE            0x0624
  21
  22/* BAR0 Interrupt control/status register */
  23#define INTCSR                          0x4C
  24#define INTCSR_ADINT_ENABLE             (1 << 0)
  25#define INTCSR_CTR4INT_ENABLE           (1 << 3)
  26#define INTCSR_PCIINT_ENABLE            (1 << 6)
  27#define INTCSR_ADINT_STATUS             (1 << 2)
  28#define INTCSR_CTR4INT_STATUS           (1 << 5)
  29
  30enum mf624_interrupt_source {ADC, CTR4, ALL};
  31
  32static void mf624_disable_interrupt(enum mf624_interrupt_source source,
  33                             struct uio_info *info)
  34{
  35        void __iomem *INTCSR_reg = info->mem[0].internal_addr + INTCSR;
  36
  37        switch (source) {
  38        case ADC:
  39                iowrite32(ioread32(INTCSR_reg)
  40                        & ~(INTCSR_ADINT_ENABLE | INTCSR_PCIINT_ENABLE),
  41                        INTCSR_reg);
  42                break;
  43
  44        case CTR4:
  45                iowrite32(ioread32(INTCSR_reg)
  46                        & ~(INTCSR_CTR4INT_ENABLE | INTCSR_PCIINT_ENABLE),
  47                        INTCSR_reg);
  48                break;
  49
  50        case ALL:
  51        default:
  52                iowrite32(ioread32(INTCSR_reg)
  53                        & ~(INTCSR_ADINT_ENABLE | INTCSR_CTR4INT_ENABLE
  54                            | INTCSR_PCIINT_ENABLE),
  55                        INTCSR_reg);
  56                break;
  57        }
  58}
  59
  60static void mf624_enable_interrupt(enum mf624_interrupt_source source,
  61                            struct uio_info *info)
  62{
  63        void __iomem *INTCSR_reg = info->mem[0].internal_addr + INTCSR;
  64
  65        switch (source) {
  66        case ADC:
  67                iowrite32(ioread32(INTCSR_reg)
  68                        | INTCSR_ADINT_ENABLE | INTCSR_PCIINT_ENABLE,
  69                        INTCSR_reg);
  70                break;
  71
  72        case CTR4:
  73                iowrite32(ioread32(INTCSR_reg)
  74                        | INTCSR_CTR4INT_ENABLE | INTCSR_PCIINT_ENABLE,
  75                        INTCSR_reg);
  76                break;
  77
  78        case ALL:
  79        default:
  80                iowrite32(ioread32(INTCSR_reg)
  81                        | INTCSR_ADINT_ENABLE | INTCSR_CTR4INT_ENABLE
  82                        | INTCSR_PCIINT_ENABLE,
  83                        INTCSR_reg);
  84                break;
  85        }
  86}
  87
  88static irqreturn_t mf624_irq_handler(int irq, struct uio_info *info)
  89{
  90        void __iomem *INTCSR_reg = info->mem[0].internal_addr + INTCSR;
  91
  92        if ((ioread32(INTCSR_reg) & INTCSR_ADINT_ENABLE)
  93            && (ioread32(INTCSR_reg) & INTCSR_ADINT_STATUS)) {
  94                mf624_disable_interrupt(ADC, info);
  95                return IRQ_HANDLED;
  96        }
  97
  98        if ((ioread32(INTCSR_reg) & INTCSR_CTR4INT_ENABLE)
  99            && (ioread32(INTCSR_reg) & INTCSR_CTR4INT_STATUS)) {
 100                mf624_disable_interrupt(CTR4, info);
 101                return IRQ_HANDLED;
 102        }
 103
 104        return IRQ_NONE;
 105}
 106
 107static int mf624_irqcontrol(struct uio_info *info, s32 irq_on)
 108{
 109        if (irq_on == 0)
 110                mf624_disable_interrupt(ALL, info);
 111        else if (irq_on == 1)
 112                mf624_enable_interrupt(ALL, info);
 113
 114        return 0;
 115}
 116
 117static int mf624_setup_mem(struct pci_dev *dev, int bar, struct uio_mem *mem, const char *name)
 118{
 119        resource_size_t start = pci_resource_start(dev, bar);
 120        resource_size_t len = pci_resource_len(dev, bar);
 121
 122        mem->name = name;
 123        mem->addr = start & PAGE_MASK;
 124        mem->offs = start & ~PAGE_MASK;
 125        if (!mem->addr)
 126                return -ENODEV;
 127        mem->size = ((start & ~PAGE_MASK) + len + PAGE_SIZE - 1) & PAGE_MASK;
 128        mem->memtype = UIO_MEM_PHYS;
 129        mem->internal_addr = pci_ioremap_bar(dev, bar);
 130        if (!mem->internal_addr)
 131                return -ENODEV;
 132        return 0;
 133}
 134
 135static int mf624_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
 136{
 137        struct uio_info *info;
 138
 139        info = kzalloc(sizeof(struct uio_info), GFP_KERNEL);
 140        if (!info)
 141                return -ENOMEM;
 142
 143        if (pci_enable_device(dev))
 144                goto out_free;
 145
 146        if (pci_request_regions(dev, "mf624"))
 147                goto out_disable;
 148
 149        info->name = "mf624";
 150        info->version = "0.0.1";
 151
 152        /* Note: Datasheet says device uses BAR0, BAR1, BAR2 -- do not trust it */
 153
 154        /* BAR0 */
 155        if (mf624_setup_mem(dev, 0, &info->mem[0], "PCI chipset, interrupts, status "
 156                            "bits, special functions"))
 157                goto out_release;
 158        /* BAR2 */
 159        if (mf624_setup_mem(dev, 2, &info->mem[1], "ADC, DAC, DIO"))
 160                goto out_unmap0;
 161
 162        /* BAR4 */
 163        if (mf624_setup_mem(dev, 4, &info->mem[2], "Counter/timer chip"))
 164                goto out_unmap1;
 165
 166        info->irq = dev->irq;
 167        info->irq_flags = IRQF_SHARED;
 168        info->handler = mf624_irq_handler;
 169
 170        info->irqcontrol = mf624_irqcontrol;
 171
 172        if (uio_register_device(&dev->dev, info))
 173                goto out_unmap2;
 174
 175        pci_set_drvdata(dev, info);
 176
 177        return 0;
 178
 179out_unmap2:
 180        iounmap(info->mem[2].internal_addr);
 181out_unmap1:
 182        iounmap(info->mem[1].internal_addr);
 183out_unmap0:
 184        iounmap(info->mem[0].internal_addr);
 185
 186out_release:
 187        pci_release_regions(dev);
 188
 189out_disable:
 190        pci_disable_device(dev);
 191
 192out_free:
 193        kfree(info);
 194        return -ENODEV;
 195}
 196
 197static void mf624_pci_remove(struct pci_dev *dev)
 198{
 199        struct uio_info *info = pci_get_drvdata(dev);
 200
 201        mf624_disable_interrupt(ALL, info);
 202
 203        uio_unregister_device(info);
 204        pci_release_regions(dev);
 205        pci_disable_device(dev);
 206
 207        iounmap(info->mem[0].internal_addr);
 208        iounmap(info->mem[1].internal_addr);
 209        iounmap(info->mem[2].internal_addr);
 210
 211        kfree(info);
 212}
 213
 214static const struct pci_device_id mf624_pci_id[] = {
 215        { PCI_DEVICE(PCI_VENDOR_ID_HUMUSOFT, PCI_DEVICE_ID_MF624) },
 216        { 0, }
 217};
 218
 219static struct pci_driver mf624_pci_driver = {
 220        .name = "mf624",
 221        .id_table = mf624_pci_id,
 222        .probe = mf624_pci_probe,
 223        .remove = mf624_pci_remove,
 224};
 225MODULE_DEVICE_TABLE(pci, mf624_pci_id);
 226
 227module_pci_driver(mf624_pci_driver);
 228MODULE_LICENSE("GPL v2");
 229MODULE_AUTHOR("Rostislav Lisovy <lisovy@gmail.com>");
 230