--- /dev/null
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/uio_driver.h>
+
+#define PCI_VENDOR_ID_HUMUSOFT 0x186c
+#define PCI_DEVICE_ID_MF624 0x0624
+#define PCI_SUBVENDOR_ID_HUMUSOFT 0x186c
+#define PCI_SUBDEVICE_DEVICE 0x0000
+
+#define INTCSR 0x4C
+#define INTCSR_ADINT_ENABLE (1 << 0)
+#define INTCSR_CTR4INT_ENABLE (1 << 3)
+#define INTCSR_PCIINT_ENABLE (1 << 6)
+#define INTCSR_ADINT_STATUS (1 << 2)
+#define INTCSR_CTR4INT_STATUS (1 << 5)
+
+
+typedef enum {ADC, CTR4, ALL} mf624_interrupt_source_t;
+
+void mf624_disable_interrupt(mf624_interrupt_source_t source, struct uio_info *info)
+{
+ u32 *INTCSR_reg = ((u32*) info->mem[0].internal_addr) + INTCSR;
+
+ switch (source) {
+ case ADC:
+ *INTCSR_reg &= ~(INTCSR_ADINT_ENABLE);
+ break;
+ case CTR4:
+ *INTCSR_reg &= ~(INTCSR_CTR4INT_ENABLE);
+ break;
+ case ALL:
+ *INTCSR_reg &= ~(INTCSR_ADINT_ENABLE | INTCSR_CTR4INT_ENABLE);
+ break;
+ default:
+ *INTCSR_reg &= ~(INTCSR_ADINT_ENABLE | INTCSR_CTR4INT_ENABLE);
+ break;
+ }
+}
+
+void mf624_enable_interrupt(mf624_interrupt_source_t source, struct uio_info *info)
+{
+ u32 *INTCSR_reg = ((u32*) info->mem[0].internal_addr) + INTCSR;
+
+ switch (source) {
+ case ADC:
+ *INTCSR_reg |= (INTCSR_ADINT_ENABLE);
+ break;
+ case CTR4:
+ *INTCSR_reg |= (INTCSR_CTR4INT_ENABLE);
+ break;
+ case ALL:
+ *INTCSR_reg |= (INTCSR_ADINT_ENABLE | INTCSR_CTR4INT_ENABLE);
+ break;
+ default:
+ *INTCSR_reg |= (INTCSR_ADINT_ENABLE | INTCSR_CTR4INT_ENABLE);
+ break;
+ }
+}
+
+static irqreturn_t mf624_irq_handler(int irq, struct uio_info *info)
+{
+ u32 *INTCSR_reg = ((u32*) info->mem[0].internal_addr) + INTCSR;
+
+ if ((*INTCSR_reg & INTCSR_ADINT_ENABLE) && (*INTCSR_reg & INTCSR_ADINT_STATUS))
+ {
+ //disable interrupt
+ mf624_disable_interrupt(ADC, info);
+ return IRQ_HANDLED;
+ }
+
+ if ((*INTCSR_reg & INTCSR_CTR4INT_ENABLE) && (*INTCSR_reg & INTCSR_CTR4INT_STATUS))
+ {
+ //disable interrupt
+ mf624_disable_interrupt(CTR4, info);
+ return IRQ_HANDLED;
+ }
+
+ return IRQ_NONE;
+}
+
+static int mf624_irqcontrol(struct uio_info *info, s32 irq_on)
+{
+ if (irq_on == 0) { /* Disable interrupts */
+ mf624_disable_interrupt(ALL, info);
+ }
+ else if (irq_on == 1) {
+ mf624_enable_interrupt(ALL, info);
+ }
+
+ return 0;
+}
+
+static int __devinit mf624_pci_probe(struct pci_dev *dev,
+ const struct pci_device_id *id)
+{
+ struct uio_info *info;
+
+ info = kzalloc(sizeof(struct uio_info), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ if (pci_enable_device(dev))
+ goto out_free;
+
+ if (pci_request_regions(dev, "mf624"))
+ goto out_disable;
+
+ info->name = "MF624";
+ info->version = "0.0.1";
+
+
+ info->mem[0].name = "PCI chipset, interrupts, status bits, special functions";
+ info->mem[0].addr = pci_resource_start(dev, 0);
+ if (!info->mem[0].addr)
+ goto out_release;
+ info->mem[0].size = pci_resource_len(dev, 0);
+ info->mem[0].memtype = UIO_MEM_PHYS;
+ info->mem[0].internal_addr = pci_ioremap_bar(dev, 0);
+ if (!info->mem[0].internal_addr)
+ goto out_release;
+
+
+ info->mem[1].name = "ADC, DAC, DIO";
+ info->mem[1].addr = pci_resource_start(dev, 1);
+ if (!info->mem[1].addr)
+ goto out_release;
+ info->mem[1].size = pci_resource_len(dev, 1);
+ info->mem[1].memtype = UIO_MEM_PHYS;
+ info->mem[1].internal_addr = pci_ioremap_bar(dev, 1);
+ if (!info->mem[1].internal_addr)
+ goto out_release;
+
+
+ info->mem[2].name = "Counter/timer chip";
+ info->mem[2].addr = pci_resource_start(dev, 2);
+ if (!info->mem[2].addr)
+ goto out_release;
+ info->mem[2].size = pci_resource_len(dev, 2);
+ info->mem[2].memtype = UIO_MEM_PHYS;
+ info->mem[2].internal_addr = pci_ioremap_bar(dev, 2);
+ if (!info->mem[2].internal_addr)
+ goto out_release;
+
+
+ info->irq = dev->irq;
+ info->irq_flags = IRQF_SHARED;
+ info->handler = mf624_irq_handler;
+
+ info->irqcontrol = mf624_irqcontrol;
+
+ if(uio_register_device(&dev->dev, info))
+ goto out_release;
+
+ pci_set_drvdata(dev, info);
+
+ return 0;
+
+out_release:
+ pci_release_regions(dev);
+out_disable:
+ pci_disable_device(dev);
+out_free:
+ kfree(info);
+ return -ENODEV;
+}
+
+static void mf624_pci_remove(struct pci_dev *dev)
+{
+ struct uio_info *info = pci_get_drvdata(dev);
+
+ mf624_disable_interrupt(ALL, info);
+
+ uio_unregister_device(info);
+ pci_release_regions(dev);
+ pci_disable_device(dev);
+ pci_set_drvdata(dev, NULL);
+
+ kfree(info);
+}
+
+static struct pci_device_id mf624_pci_id[] __devinitdata = {
+ {
+ .vendor = PCI_VENDOR_ID_HUMUSOFT,
+ .device = PCI_DEVICE_ID_MF624,
+ .subvendor = PCI_SUBVENDOR_ID_HUMUSOFT,
+ .subdevice = PCI_SUBDEVICE_DEVICE,
+ },
+ { 0, }
+};
+
+static struct pci_driver mf624_pci_driver = {
+ .name = "mf624",
+ .id_table = mf624_pci_id,
+ .probe = mf624_pci_probe,
+ .remove = mf624_pci_remove,
+};
+
+static int __init mf624_init_module(void)
+{
+ return pci_register_driver(&mf624_pci_driver);
+}
+
+static void __exit mf624_exit_module(void)
+{
+ pci_unregister_driver(&mf624_pci_driver);
+}
+
+module_init(mf624_init_module);
+module_exit(mf624_exit_module);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Rostislav Lisovy <lisovy@gmail.com>");