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