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_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
 131{
 132        struct uio_info *info;
 133
 134        info = kzalloc(sizeof(struct uio_info), GFP_KERNEL);
 135        if (!info)
 136                return -ENOMEM;
 137
 138        if (pci_enable_device(dev))
 139                goto out_free;
 140
 141        if (pci_request_regions(dev, "mf624"))
 142                goto out_disable;
 143
 144        info->name = "mf624";
 145        info->version = "0.0.1";
 146
 147        /* Note: Datasheet says device uses BAR0, BAR1, BAR2 -- do not trust it */
 148
 149        /* BAR0 */
 150        info->mem[0].name = "PCI chipset, interrupts, status "
 151                        "bits, special functions";
 152        info->mem[0].addr = pci_resource_start(dev, 0);
 153        if (!info->mem[0].addr)
 154                goto out_release;
 155        info->mem[0].size = pci_resource_len(dev, 0);
 156        info->mem[0].memtype = UIO_MEM_PHYS;
 157        info->mem[0].internal_addr = pci_ioremap_bar(dev, 0);
 158        if (!info->mem[0].internal_addr)
 159                goto out_release;
 160
 161        /* BAR2 */
 162        info->mem[1].name = "ADC, DAC, DIO";
 163        info->mem[1].addr = pci_resource_start(dev, 2);
 164        if (!info->mem[1].addr)
 165                goto out_unmap0;
 166        info->mem[1].size = pci_resource_len(dev, 2);
 167        info->mem[1].memtype = UIO_MEM_PHYS;
 168        info->mem[1].internal_addr = pci_ioremap_bar(dev, 2);
 169        if (!info->mem[1].internal_addr)
 170                goto out_unmap0;
 171
 172        /* BAR4 */
 173        info->mem[2].name = "Counter/timer chip";
 174        info->mem[2].addr = pci_resource_start(dev, 4);
 175        if (!info->mem[2].addr)
 176                goto out_unmap1;
 177        info->mem[2].size = pci_resource_len(dev, 4);
 178        info->mem[2].memtype = UIO_MEM_PHYS;
 179        info->mem[2].internal_addr = pci_ioremap_bar(dev, 4);
 180        if (!info->mem[2].internal_addr)
 181                goto out_unmap1;
 182
 183        info->irq = dev->irq;
 184        info->irq_flags = IRQF_SHARED;
 185        info->handler = mf624_irq_handler;
 186
 187        info->irqcontrol = mf624_irqcontrol;
 188
 189        if (uio_register_device(&dev->dev, info))
 190                goto out_unmap2;
 191
 192        pci_set_drvdata(dev, info);
 193
 194        return 0;
 195
 196out_unmap2:
 197        iounmap(info->mem[2].internal_addr);
 198out_unmap1:
 199        iounmap(info->mem[1].internal_addr);
 200out_unmap0:
 201        iounmap(info->mem[0].internal_addr);
 202
 203out_release:
 204        pci_release_regions(dev);
 205
 206out_disable:
 207        pci_disable_device(dev);
 208
 209out_free:
 210        kfree(info);
 211        return -ENODEV;
 212}
 213
 214static void mf624_pci_remove(struct pci_dev *dev)
 215{
 216        struct uio_info *info = pci_get_drvdata(dev);
 217
 218        mf624_disable_interrupt(ALL, info);
 219
 220        uio_unregister_device(info);
 221        pci_release_regions(dev);
 222        pci_disable_device(dev);
 223
 224        iounmap(info->mem[0].internal_addr);
 225        iounmap(info->mem[1].internal_addr);
 226        iounmap(info->mem[2].internal_addr);
 227
 228        kfree(info);
 229}
 230
 231static const struct pci_device_id mf624_pci_id[] = {
 232        { PCI_DEVICE(PCI_VENDOR_ID_HUMUSOFT, PCI_DEVICE_ID_MF624) },
 233        { 0, }
 234};
 235
 236static struct pci_driver mf624_pci_driver = {
 237        .name = "mf624",
 238        .id_table = mf624_pci_id,
 239        .probe = mf624_pci_probe,
 240        .remove = mf624_pci_remove,
 241};
 242MODULE_DEVICE_TABLE(pci, mf624_pci_id);
 243
 244module_pci_driver(mf624_pci_driver);
 245MODULE_LICENSE("GPL v2");
 246MODULE_AUTHOR("Rostislav Lisovy <lisovy@gmail.com>");
 247