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 43 enum mf624_interrupt_source {ADC, CTR4, ALL}; 44 45 static 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 73 static 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 101 static 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 120 static 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 130 static 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 148 static 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 192 out_unmap2: 193 iounmap(info->mem[2].internal_addr); 194 out_unmap1: 195 iounmap(info->mem[1].internal_addr); 196 out_unmap0: 197 iounmap(info->mem[0].internal_addr); 198 199 out_release: 200 pci_release_regions(dev); 201 202 out_disable: 203 pci_disable_device(dev); 204 205 out_free: 206 kfree(info); 207 return -ENODEV; 208 } 209 210 static 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 227 static const struct pci_device_id mf624_pci_id[] = { 228 { PCI_DEVICE(PCI_VENDOR_ID_HUMUSOFT, PCI_DEVICE_ID_MF624) }, 229 { 0, } 230 }; 231 232 static 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 }; 238 MODULE_DEVICE_TABLE(pci, mf624_pci_id); 239 240 module_pci_driver(mf624_pci_driver); 241 MODULE_LICENSE("GPL v2"); 242 MODULE_AUTHOR("Rostislav Lisovy <lisovy@gmail.com>"); 243