18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * UIO driver fo Humusoft MF624 DAQ card. 48c2ecf20Sopenharmony_ci * Copyright (C) 2011 Rostislav Lisovy <lisovy@gmail.com>, 58c2ecf20Sopenharmony_ci * Czech Technical University in Prague 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/init.h> 98c2ecf20Sopenharmony_ci#include <linux/module.h> 108c2ecf20Sopenharmony_ci#include <linux/device.h> 118c2ecf20Sopenharmony_ci#include <linux/pci.h> 128c2ecf20Sopenharmony_ci#include <linux/slab.h> 138c2ecf20Sopenharmony_ci#include <linux/io.h> 148c2ecf20Sopenharmony_ci#include <linux/kernel.h> 158c2ecf20Sopenharmony_ci#include <linux/uio_driver.h> 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#define PCI_VENDOR_ID_HUMUSOFT 0x186c 188c2ecf20Sopenharmony_ci#define PCI_DEVICE_ID_MF624 0x0624 198c2ecf20Sopenharmony_ci#define PCI_SUBVENDOR_ID_HUMUSOFT 0x186c 208c2ecf20Sopenharmony_ci#define PCI_SUBDEVICE_DEVICE 0x0624 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci/* BAR0 Interrupt control/status register */ 238c2ecf20Sopenharmony_ci#define INTCSR 0x4C 248c2ecf20Sopenharmony_ci#define INTCSR_ADINT_ENABLE (1 << 0) 258c2ecf20Sopenharmony_ci#define INTCSR_CTR4INT_ENABLE (1 << 3) 268c2ecf20Sopenharmony_ci#define INTCSR_PCIINT_ENABLE (1 << 6) 278c2ecf20Sopenharmony_ci#define INTCSR_ADINT_STATUS (1 << 2) 288c2ecf20Sopenharmony_ci#define INTCSR_CTR4INT_STATUS (1 << 5) 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_cienum mf624_interrupt_source {ADC, CTR4, ALL}; 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_cistatic void mf624_disable_interrupt(enum mf624_interrupt_source source, 338c2ecf20Sopenharmony_ci struct uio_info *info) 348c2ecf20Sopenharmony_ci{ 358c2ecf20Sopenharmony_ci void __iomem *INTCSR_reg = info->mem[0].internal_addr + INTCSR; 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci switch (source) { 388c2ecf20Sopenharmony_ci case ADC: 398c2ecf20Sopenharmony_ci iowrite32(ioread32(INTCSR_reg) 408c2ecf20Sopenharmony_ci & ~(INTCSR_ADINT_ENABLE | INTCSR_PCIINT_ENABLE), 418c2ecf20Sopenharmony_ci INTCSR_reg); 428c2ecf20Sopenharmony_ci break; 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci case CTR4: 458c2ecf20Sopenharmony_ci iowrite32(ioread32(INTCSR_reg) 468c2ecf20Sopenharmony_ci & ~(INTCSR_CTR4INT_ENABLE | INTCSR_PCIINT_ENABLE), 478c2ecf20Sopenharmony_ci INTCSR_reg); 488c2ecf20Sopenharmony_ci break; 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci case ALL: 518c2ecf20Sopenharmony_ci default: 528c2ecf20Sopenharmony_ci iowrite32(ioread32(INTCSR_reg) 538c2ecf20Sopenharmony_ci & ~(INTCSR_ADINT_ENABLE | INTCSR_CTR4INT_ENABLE 548c2ecf20Sopenharmony_ci | INTCSR_PCIINT_ENABLE), 558c2ecf20Sopenharmony_ci INTCSR_reg); 568c2ecf20Sopenharmony_ci break; 578c2ecf20Sopenharmony_ci } 588c2ecf20Sopenharmony_ci} 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_cistatic void mf624_enable_interrupt(enum mf624_interrupt_source source, 618c2ecf20Sopenharmony_ci struct uio_info *info) 628c2ecf20Sopenharmony_ci{ 638c2ecf20Sopenharmony_ci void __iomem *INTCSR_reg = info->mem[0].internal_addr + INTCSR; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci switch (source) { 668c2ecf20Sopenharmony_ci case ADC: 678c2ecf20Sopenharmony_ci iowrite32(ioread32(INTCSR_reg) 688c2ecf20Sopenharmony_ci | INTCSR_ADINT_ENABLE | INTCSR_PCIINT_ENABLE, 698c2ecf20Sopenharmony_ci INTCSR_reg); 708c2ecf20Sopenharmony_ci break; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci case CTR4: 738c2ecf20Sopenharmony_ci iowrite32(ioread32(INTCSR_reg) 748c2ecf20Sopenharmony_ci | INTCSR_CTR4INT_ENABLE | INTCSR_PCIINT_ENABLE, 758c2ecf20Sopenharmony_ci INTCSR_reg); 768c2ecf20Sopenharmony_ci break; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci case ALL: 798c2ecf20Sopenharmony_ci default: 808c2ecf20Sopenharmony_ci iowrite32(ioread32(INTCSR_reg) 818c2ecf20Sopenharmony_ci | INTCSR_ADINT_ENABLE | INTCSR_CTR4INT_ENABLE 828c2ecf20Sopenharmony_ci | INTCSR_PCIINT_ENABLE, 838c2ecf20Sopenharmony_ci INTCSR_reg); 848c2ecf20Sopenharmony_ci break; 858c2ecf20Sopenharmony_ci } 868c2ecf20Sopenharmony_ci} 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_cistatic irqreturn_t mf624_irq_handler(int irq, struct uio_info *info) 898c2ecf20Sopenharmony_ci{ 908c2ecf20Sopenharmony_ci void __iomem *INTCSR_reg = info->mem[0].internal_addr + INTCSR; 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci if ((ioread32(INTCSR_reg) & INTCSR_ADINT_ENABLE) 938c2ecf20Sopenharmony_ci && (ioread32(INTCSR_reg) & INTCSR_ADINT_STATUS)) { 948c2ecf20Sopenharmony_ci mf624_disable_interrupt(ADC, info); 958c2ecf20Sopenharmony_ci return IRQ_HANDLED; 968c2ecf20Sopenharmony_ci } 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci if ((ioread32(INTCSR_reg) & INTCSR_CTR4INT_ENABLE) 998c2ecf20Sopenharmony_ci && (ioread32(INTCSR_reg) & INTCSR_CTR4INT_STATUS)) { 1008c2ecf20Sopenharmony_ci mf624_disable_interrupt(CTR4, info); 1018c2ecf20Sopenharmony_ci return IRQ_HANDLED; 1028c2ecf20Sopenharmony_ci } 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci return IRQ_NONE; 1058c2ecf20Sopenharmony_ci} 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_cistatic int mf624_irqcontrol(struct uio_info *info, s32 irq_on) 1088c2ecf20Sopenharmony_ci{ 1098c2ecf20Sopenharmony_ci if (irq_on == 0) 1108c2ecf20Sopenharmony_ci mf624_disable_interrupt(ALL, info); 1118c2ecf20Sopenharmony_ci else if (irq_on == 1) 1128c2ecf20Sopenharmony_ci mf624_enable_interrupt(ALL, info); 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci return 0; 1158c2ecf20Sopenharmony_ci} 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_cistatic int mf624_setup_mem(struct pci_dev *dev, int bar, struct uio_mem *mem, const char *name) 1188c2ecf20Sopenharmony_ci{ 1198c2ecf20Sopenharmony_ci resource_size_t start = pci_resource_start(dev, bar); 1208c2ecf20Sopenharmony_ci resource_size_t len = pci_resource_len(dev, bar); 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci mem->name = name; 1238c2ecf20Sopenharmony_ci mem->addr = start & PAGE_MASK; 1248c2ecf20Sopenharmony_ci mem->offs = start & ~PAGE_MASK; 1258c2ecf20Sopenharmony_ci if (!mem->addr) 1268c2ecf20Sopenharmony_ci return -ENODEV; 1278c2ecf20Sopenharmony_ci mem->size = ((start & ~PAGE_MASK) + len + PAGE_SIZE - 1) & PAGE_MASK; 1288c2ecf20Sopenharmony_ci mem->memtype = UIO_MEM_PHYS; 1298c2ecf20Sopenharmony_ci mem->internal_addr = pci_ioremap_bar(dev, bar); 1308c2ecf20Sopenharmony_ci if (!mem->internal_addr) 1318c2ecf20Sopenharmony_ci return -ENODEV; 1328c2ecf20Sopenharmony_ci return 0; 1338c2ecf20Sopenharmony_ci} 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_cistatic int mf624_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) 1368c2ecf20Sopenharmony_ci{ 1378c2ecf20Sopenharmony_ci struct uio_info *info; 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci info = kzalloc(sizeof(struct uio_info), GFP_KERNEL); 1408c2ecf20Sopenharmony_ci if (!info) 1418c2ecf20Sopenharmony_ci return -ENOMEM; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci if (pci_enable_device(dev)) 1448c2ecf20Sopenharmony_ci goto out_free; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci if (pci_request_regions(dev, "mf624")) 1478c2ecf20Sopenharmony_ci goto out_disable; 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci info->name = "mf624"; 1508c2ecf20Sopenharmony_ci info->version = "0.0.1"; 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci /* Note: Datasheet says device uses BAR0, BAR1, BAR2 -- do not trust it */ 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci /* BAR0 */ 1558c2ecf20Sopenharmony_ci if (mf624_setup_mem(dev, 0, &info->mem[0], "PCI chipset, interrupts, status " 1568c2ecf20Sopenharmony_ci "bits, special functions")) 1578c2ecf20Sopenharmony_ci goto out_release; 1588c2ecf20Sopenharmony_ci /* BAR2 */ 1598c2ecf20Sopenharmony_ci if (mf624_setup_mem(dev, 2, &info->mem[1], "ADC, DAC, DIO")) 1608c2ecf20Sopenharmony_ci goto out_unmap0; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci /* BAR4 */ 1638c2ecf20Sopenharmony_ci if (mf624_setup_mem(dev, 4, &info->mem[2], "Counter/timer chip")) 1648c2ecf20Sopenharmony_ci goto out_unmap1; 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci info->irq = dev->irq; 1678c2ecf20Sopenharmony_ci info->irq_flags = IRQF_SHARED; 1688c2ecf20Sopenharmony_ci info->handler = mf624_irq_handler; 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci info->irqcontrol = mf624_irqcontrol; 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci if (uio_register_device(&dev->dev, info)) 1738c2ecf20Sopenharmony_ci goto out_unmap2; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci pci_set_drvdata(dev, info); 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci return 0; 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ciout_unmap2: 1808c2ecf20Sopenharmony_ci iounmap(info->mem[2].internal_addr); 1818c2ecf20Sopenharmony_ciout_unmap1: 1828c2ecf20Sopenharmony_ci iounmap(info->mem[1].internal_addr); 1838c2ecf20Sopenharmony_ciout_unmap0: 1848c2ecf20Sopenharmony_ci iounmap(info->mem[0].internal_addr); 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ciout_release: 1878c2ecf20Sopenharmony_ci pci_release_regions(dev); 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ciout_disable: 1908c2ecf20Sopenharmony_ci pci_disable_device(dev); 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ciout_free: 1938c2ecf20Sopenharmony_ci kfree(info); 1948c2ecf20Sopenharmony_ci return -ENODEV; 1958c2ecf20Sopenharmony_ci} 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_cistatic void mf624_pci_remove(struct pci_dev *dev) 1988c2ecf20Sopenharmony_ci{ 1998c2ecf20Sopenharmony_ci struct uio_info *info = pci_get_drvdata(dev); 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci mf624_disable_interrupt(ALL, info); 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci uio_unregister_device(info); 2048c2ecf20Sopenharmony_ci pci_release_regions(dev); 2058c2ecf20Sopenharmony_ci pci_disable_device(dev); 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci iounmap(info->mem[0].internal_addr); 2088c2ecf20Sopenharmony_ci iounmap(info->mem[1].internal_addr); 2098c2ecf20Sopenharmony_ci iounmap(info->mem[2].internal_addr); 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci kfree(info); 2128c2ecf20Sopenharmony_ci} 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_cistatic const struct pci_device_id mf624_pci_id[] = { 2158c2ecf20Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_HUMUSOFT, PCI_DEVICE_ID_MF624) }, 2168c2ecf20Sopenharmony_ci { 0, } 2178c2ecf20Sopenharmony_ci}; 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_cistatic struct pci_driver mf624_pci_driver = { 2208c2ecf20Sopenharmony_ci .name = "mf624", 2218c2ecf20Sopenharmony_ci .id_table = mf624_pci_id, 2228c2ecf20Sopenharmony_ci .probe = mf624_pci_probe, 2238c2ecf20Sopenharmony_ci .remove = mf624_pci_remove, 2248c2ecf20Sopenharmony_ci}; 2258c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, mf624_pci_id); 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_cimodule_pci_driver(mf624_pci_driver); 2288c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 2298c2ecf20Sopenharmony_ciMODULE_AUTHOR("Rostislav Lisovy <lisovy@gmail.com>"); 230