162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * UIO driver fo Humusoft MF624 DAQ card. 462306a36Sopenharmony_ci * Copyright (C) 2011 Rostislav Lisovy <lisovy@gmail.com>, 562306a36Sopenharmony_ci * Czech Technical University in Prague 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/init.h> 962306a36Sopenharmony_ci#include <linux/module.h> 1062306a36Sopenharmony_ci#include <linux/device.h> 1162306a36Sopenharmony_ci#include <linux/pci.h> 1262306a36Sopenharmony_ci#include <linux/slab.h> 1362306a36Sopenharmony_ci#include <linux/io.h> 1462306a36Sopenharmony_ci#include <linux/kernel.h> 1562306a36Sopenharmony_ci#include <linux/uio_driver.h> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#define PCI_VENDOR_ID_HUMUSOFT 0x186c 1862306a36Sopenharmony_ci#define PCI_DEVICE_ID_MF624 0x0624 1962306a36Sopenharmony_ci#define PCI_SUBVENDOR_ID_HUMUSOFT 0x186c 2062306a36Sopenharmony_ci#define PCI_SUBDEVICE_DEVICE 0x0624 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci/* BAR0 Interrupt control/status register */ 2362306a36Sopenharmony_ci#define INTCSR 0x4C 2462306a36Sopenharmony_ci#define INTCSR_ADINT_ENABLE (1 << 0) 2562306a36Sopenharmony_ci#define INTCSR_CTR4INT_ENABLE (1 << 3) 2662306a36Sopenharmony_ci#define INTCSR_PCIINT_ENABLE (1 << 6) 2762306a36Sopenharmony_ci#define INTCSR_ADINT_STATUS (1 << 2) 2862306a36Sopenharmony_ci#define INTCSR_CTR4INT_STATUS (1 << 5) 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_cienum mf624_interrupt_source {ADC, CTR4, ALL}; 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_cistatic void mf624_disable_interrupt(enum mf624_interrupt_source source, 3362306a36Sopenharmony_ci struct uio_info *info) 3462306a36Sopenharmony_ci{ 3562306a36Sopenharmony_ci void __iomem *INTCSR_reg = info->mem[0].internal_addr + INTCSR; 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci switch (source) { 3862306a36Sopenharmony_ci case ADC: 3962306a36Sopenharmony_ci iowrite32(ioread32(INTCSR_reg) 4062306a36Sopenharmony_ci & ~(INTCSR_ADINT_ENABLE | INTCSR_PCIINT_ENABLE), 4162306a36Sopenharmony_ci INTCSR_reg); 4262306a36Sopenharmony_ci break; 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci case CTR4: 4562306a36Sopenharmony_ci iowrite32(ioread32(INTCSR_reg) 4662306a36Sopenharmony_ci & ~(INTCSR_CTR4INT_ENABLE | INTCSR_PCIINT_ENABLE), 4762306a36Sopenharmony_ci INTCSR_reg); 4862306a36Sopenharmony_ci break; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci case ALL: 5162306a36Sopenharmony_ci default: 5262306a36Sopenharmony_ci iowrite32(ioread32(INTCSR_reg) 5362306a36Sopenharmony_ci & ~(INTCSR_ADINT_ENABLE | INTCSR_CTR4INT_ENABLE 5462306a36Sopenharmony_ci | INTCSR_PCIINT_ENABLE), 5562306a36Sopenharmony_ci INTCSR_reg); 5662306a36Sopenharmony_ci break; 5762306a36Sopenharmony_ci } 5862306a36Sopenharmony_ci} 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_cistatic void mf624_enable_interrupt(enum mf624_interrupt_source source, 6162306a36Sopenharmony_ci struct uio_info *info) 6262306a36Sopenharmony_ci{ 6362306a36Sopenharmony_ci void __iomem *INTCSR_reg = info->mem[0].internal_addr + INTCSR; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci switch (source) { 6662306a36Sopenharmony_ci case ADC: 6762306a36Sopenharmony_ci iowrite32(ioread32(INTCSR_reg) 6862306a36Sopenharmony_ci | INTCSR_ADINT_ENABLE | INTCSR_PCIINT_ENABLE, 6962306a36Sopenharmony_ci INTCSR_reg); 7062306a36Sopenharmony_ci break; 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci case CTR4: 7362306a36Sopenharmony_ci iowrite32(ioread32(INTCSR_reg) 7462306a36Sopenharmony_ci | INTCSR_CTR4INT_ENABLE | INTCSR_PCIINT_ENABLE, 7562306a36Sopenharmony_ci INTCSR_reg); 7662306a36Sopenharmony_ci break; 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci case ALL: 7962306a36Sopenharmony_ci default: 8062306a36Sopenharmony_ci iowrite32(ioread32(INTCSR_reg) 8162306a36Sopenharmony_ci | INTCSR_ADINT_ENABLE | INTCSR_CTR4INT_ENABLE 8262306a36Sopenharmony_ci | INTCSR_PCIINT_ENABLE, 8362306a36Sopenharmony_ci INTCSR_reg); 8462306a36Sopenharmony_ci break; 8562306a36Sopenharmony_ci } 8662306a36Sopenharmony_ci} 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_cistatic irqreturn_t mf624_irq_handler(int irq, struct uio_info *info) 8962306a36Sopenharmony_ci{ 9062306a36Sopenharmony_ci void __iomem *INTCSR_reg = info->mem[0].internal_addr + INTCSR; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci if ((ioread32(INTCSR_reg) & INTCSR_ADINT_ENABLE) 9362306a36Sopenharmony_ci && (ioread32(INTCSR_reg) & INTCSR_ADINT_STATUS)) { 9462306a36Sopenharmony_ci mf624_disable_interrupt(ADC, info); 9562306a36Sopenharmony_ci return IRQ_HANDLED; 9662306a36Sopenharmony_ci } 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci if ((ioread32(INTCSR_reg) & INTCSR_CTR4INT_ENABLE) 9962306a36Sopenharmony_ci && (ioread32(INTCSR_reg) & INTCSR_CTR4INT_STATUS)) { 10062306a36Sopenharmony_ci mf624_disable_interrupt(CTR4, info); 10162306a36Sopenharmony_ci return IRQ_HANDLED; 10262306a36Sopenharmony_ci } 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci return IRQ_NONE; 10562306a36Sopenharmony_ci} 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_cistatic int mf624_irqcontrol(struct uio_info *info, s32 irq_on) 10862306a36Sopenharmony_ci{ 10962306a36Sopenharmony_ci if (irq_on == 0) 11062306a36Sopenharmony_ci mf624_disable_interrupt(ALL, info); 11162306a36Sopenharmony_ci else if (irq_on == 1) 11262306a36Sopenharmony_ci mf624_enable_interrupt(ALL, info); 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci return 0; 11562306a36Sopenharmony_ci} 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_cistatic int mf624_setup_mem(struct pci_dev *dev, int bar, struct uio_mem *mem, const char *name) 11862306a36Sopenharmony_ci{ 11962306a36Sopenharmony_ci resource_size_t start = pci_resource_start(dev, bar); 12062306a36Sopenharmony_ci resource_size_t len = pci_resource_len(dev, bar); 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci mem->name = name; 12362306a36Sopenharmony_ci mem->addr = start & PAGE_MASK; 12462306a36Sopenharmony_ci mem->offs = start & ~PAGE_MASK; 12562306a36Sopenharmony_ci if (!mem->addr) 12662306a36Sopenharmony_ci return -ENODEV; 12762306a36Sopenharmony_ci mem->size = ((start & ~PAGE_MASK) + len + PAGE_SIZE - 1) & PAGE_MASK; 12862306a36Sopenharmony_ci mem->memtype = UIO_MEM_PHYS; 12962306a36Sopenharmony_ci mem->internal_addr = pci_ioremap_bar(dev, bar); 13062306a36Sopenharmony_ci if (!mem->internal_addr) 13162306a36Sopenharmony_ci return -ENODEV; 13262306a36Sopenharmony_ci return 0; 13362306a36Sopenharmony_ci} 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_cistatic int mf624_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) 13662306a36Sopenharmony_ci{ 13762306a36Sopenharmony_ci struct uio_info *info; 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci info = devm_kzalloc(&dev->dev, sizeof(struct uio_info), GFP_KERNEL); 14062306a36Sopenharmony_ci if (!info) 14162306a36Sopenharmony_ci return -ENOMEM; 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci if (pci_enable_device(dev)) 14462306a36Sopenharmony_ci return -ENODEV; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci if (pci_request_regions(dev, "mf624")) 14762306a36Sopenharmony_ci goto out_disable; 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci info->name = "mf624"; 15062306a36Sopenharmony_ci info->version = "0.0.1"; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci /* Note: Datasheet says device uses BAR0, BAR1, BAR2 -- do not trust it */ 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci /* BAR0 */ 15562306a36Sopenharmony_ci if (mf624_setup_mem(dev, 0, &info->mem[0], "PCI chipset, interrupts, status " 15662306a36Sopenharmony_ci "bits, special functions")) 15762306a36Sopenharmony_ci goto out_release; 15862306a36Sopenharmony_ci /* BAR2 */ 15962306a36Sopenharmony_ci if (mf624_setup_mem(dev, 2, &info->mem[1], "ADC, DAC, DIO")) 16062306a36Sopenharmony_ci goto out_unmap0; 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci /* BAR4 */ 16362306a36Sopenharmony_ci if (mf624_setup_mem(dev, 4, &info->mem[2], "Counter/timer chip")) 16462306a36Sopenharmony_ci goto out_unmap1; 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci info->irq = dev->irq; 16762306a36Sopenharmony_ci info->irq_flags = IRQF_SHARED; 16862306a36Sopenharmony_ci info->handler = mf624_irq_handler; 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci info->irqcontrol = mf624_irqcontrol; 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci if (uio_register_device(&dev->dev, info)) 17362306a36Sopenharmony_ci goto out_unmap2; 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci pci_set_drvdata(dev, info); 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci return 0; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ciout_unmap2: 18062306a36Sopenharmony_ci iounmap(info->mem[2].internal_addr); 18162306a36Sopenharmony_ciout_unmap1: 18262306a36Sopenharmony_ci iounmap(info->mem[1].internal_addr); 18362306a36Sopenharmony_ciout_unmap0: 18462306a36Sopenharmony_ci iounmap(info->mem[0].internal_addr); 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ciout_release: 18762306a36Sopenharmony_ci pci_release_regions(dev); 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ciout_disable: 19062306a36Sopenharmony_ci pci_disable_device(dev); 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci return -ENODEV; 19362306a36Sopenharmony_ci} 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_cistatic void mf624_pci_remove(struct pci_dev *dev) 19662306a36Sopenharmony_ci{ 19762306a36Sopenharmony_ci struct uio_info *info = pci_get_drvdata(dev); 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci mf624_disable_interrupt(ALL, info); 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci uio_unregister_device(info); 20262306a36Sopenharmony_ci pci_release_regions(dev); 20362306a36Sopenharmony_ci pci_disable_device(dev); 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci iounmap(info->mem[0].internal_addr); 20662306a36Sopenharmony_ci iounmap(info->mem[1].internal_addr); 20762306a36Sopenharmony_ci iounmap(info->mem[2].internal_addr); 20862306a36Sopenharmony_ci} 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_cistatic const struct pci_device_id mf624_pci_id[] = { 21162306a36Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_HUMUSOFT, PCI_DEVICE_ID_MF624) }, 21262306a36Sopenharmony_ci { 0, } 21362306a36Sopenharmony_ci}; 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_cistatic struct pci_driver mf624_pci_driver = { 21662306a36Sopenharmony_ci .name = "mf624", 21762306a36Sopenharmony_ci .id_table = mf624_pci_id, 21862306a36Sopenharmony_ci .probe = mf624_pci_probe, 21962306a36Sopenharmony_ci .remove = mf624_pci_remove, 22062306a36Sopenharmony_ci}; 22162306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, mf624_pci_id); 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_cimodule_pci_driver(mf624_pci_driver); 22462306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 22562306a36Sopenharmony_ciMODULE_AUTHOR("Rostislav Lisovy <lisovy@gmail.com>"); 226