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_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 196 out_unmap2: 197 iounmap(info->mem[2].internal_addr); 198 out_unmap1: 199 iounmap(info->mem[1].internal_addr); 200 out_unmap0: 201 iounmap(info->mem[0].internal_addr); 202 203 out_release: 204 pci_release_regions(dev); 205 206 out_disable: 207 pci_disable_device(dev); 208 209 out_free: 210 kfree(info); 211 return -ENODEV; 212 } 213 214 static 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 231 static const struct pci_device_id mf624_pci_id[] = { 232 { PCI_DEVICE(PCI_VENDOR_ID_HUMUSOFT, PCI_DEVICE_ID_MF624) }, 233 { 0, } 234 }; 235 236 static 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 }; 242 MODULE_DEVICE_TABLE(pci, mf624_pci_id); 243 244 module_pci_driver(mf624_pci_driver); 245 MODULE_LICENSE("GPL v2"); 246 MODULE_AUTHOR("Rostislav Lisovy <lisovy@gmail.com>"); 247