174ba9207SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
206849faaSRostislav Lisovy /*
306849faaSRostislav Lisovy * UIO driver fo Humusoft MF624 DAQ card.
406849faaSRostislav Lisovy * Copyright (C) 2011 Rostislav Lisovy <lisovy@gmail.com>,
506849faaSRostislav Lisovy * Czech Technical University in Prague
606849faaSRostislav Lisovy */
706849faaSRostislav Lisovy
806849faaSRostislav Lisovy #include <linux/init.h>
906849faaSRostislav Lisovy #include <linux/module.h>
1006849faaSRostislav Lisovy #include <linux/device.h>
1106849faaSRostislav Lisovy #include <linux/pci.h>
1206849faaSRostislav Lisovy #include <linux/slab.h>
1306849faaSRostislav Lisovy #include <linux/io.h>
1406849faaSRostislav Lisovy #include <linux/kernel.h>
1506849faaSRostislav Lisovy #include <linux/uio_driver.h>
1606849faaSRostislav Lisovy
1706849faaSRostislav Lisovy #define PCI_VENDOR_ID_HUMUSOFT 0x186c
1806849faaSRostislav Lisovy #define PCI_DEVICE_ID_MF624 0x0624
1906849faaSRostislav Lisovy #define PCI_SUBVENDOR_ID_HUMUSOFT 0x186c
2006849faaSRostislav Lisovy #define PCI_SUBDEVICE_DEVICE 0x0624
2106849faaSRostislav Lisovy
2206849faaSRostislav Lisovy /* BAR0 Interrupt control/status register */
2306849faaSRostislav Lisovy #define INTCSR 0x4C
2406849faaSRostislav Lisovy #define INTCSR_ADINT_ENABLE (1 << 0)
2506849faaSRostislav Lisovy #define INTCSR_CTR4INT_ENABLE (1 << 3)
2606849faaSRostislav Lisovy #define INTCSR_PCIINT_ENABLE (1 << 6)
2706849faaSRostislav Lisovy #define INTCSR_ADINT_STATUS (1 << 2)
2806849faaSRostislav Lisovy #define INTCSR_CTR4INT_STATUS (1 << 5)
2906849faaSRostislav Lisovy
3006849faaSRostislav Lisovy enum mf624_interrupt_source {ADC, CTR4, ALL};
3106849faaSRostislav Lisovy
mf624_disable_interrupt(enum mf624_interrupt_source source,struct uio_info * info)32497b46dbSFengguang Wu static void mf624_disable_interrupt(enum mf624_interrupt_source source,
3306849faaSRostislav Lisovy struct uio_info *info)
3406849faaSRostislav Lisovy {
3506849faaSRostislav Lisovy void __iomem *INTCSR_reg = info->mem[0].internal_addr + INTCSR;
3606849faaSRostislav Lisovy
3706849faaSRostislav Lisovy switch (source) {
3806849faaSRostislav Lisovy case ADC:
3906849faaSRostislav Lisovy iowrite32(ioread32(INTCSR_reg)
4006849faaSRostislav Lisovy & ~(INTCSR_ADINT_ENABLE | INTCSR_PCIINT_ENABLE),
4106849faaSRostislav Lisovy INTCSR_reg);
4206849faaSRostislav Lisovy break;
4306849faaSRostislav Lisovy
4406849faaSRostislav Lisovy case CTR4:
4506849faaSRostislav Lisovy iowrite32(ioread32(INTCSR_reg)
4606849faaSRostislav Lisovy & ~(INTCSR_CTR4INT_ENABLE | INTCSR_PCIINT_ENABLE),
4706849faaSRostislav Lisovy INTCSR_reg);
4806849faaSRostislav Lisovy break;
4906849faaSRostislav Lisovy
5006849faaSRostislav Lisovy case ALL:
5106849faaSRostislav Lisovy default:
5206849faaSRostislav Lisovy iowrite32(ioread32(INTCSR_reg)
5306849faaSRostislav Lisovy & ~(INTCSR_ADINT_ENABLE | INTCSR_CTR4INT_ENABLE
5406849faaSRostislav Lisovy | INTCSR_PCIINT_ENABLE),
5506849faaSRostislav Lisovy INTCSR_reg);
5606849faaSRostislav Lisovy break;
5706849faaSRostislav Lisovy }
5806849faaSRostislav Lisovy }
5906849faaSRostislav Lisovy
mf624_enable_interrupt(enum mf624_interrupt_source source,struct uio_info * info)60497b46dbSFengguang Wu static void mf624_enable_interrupt(enum mf624_interrupt_source source,
6106849faaSRostislav Lisovy struct uio_info *info)
6206849faaSRostislav Lisovy {
6306849faaSRostislav Lisovy void __iomem *INTCSR_reg = info->mem[0].internal_addr + INTCSR;
6406849faaSRostislav Lisovy
6506849faaSRostislav Lisovy switch (source) {
6606849faaSRostislav Lisovy case ADC:
6706849faaSRostislav Lisovy iowrite32(ioread32(INTCSR_reg)
6806849faaSRostislav Lisovy | INTCSR_ADINT_ENABLE | INTCSR_PCIINT_ENABLE,
6906849faaSRostislav Lisovy INTCSR_reg);
7006849faaSRostislav Lisovy break;
7106849faaSRostislav Lisovy
7206849faaSRostislav Lisovy case CTR4:
7306849faaSRostislav Lisovy iowrite32(ioread32(INTCSR_reg)
7406849faaSRostislav Lisovy | INTCSR_CTR4INT_ENABLE | INTCSR_PCIINT_ENABLE,
7506849faaSRostislav Lisovy INTCSR_reg);
7606849faaSRostislav Lisovy break;
7706849faaSRostislav Lisovy
7806849faaSRostislav Lisovy case ALL:
7906849faaSRostislav Lisovy default:
8006849faaSRostislav Lisovy iowrite32(ioread32(INTCSR_reg)
8106849faaSRostislav Lisovy | INTCSR_ADINT_ENABLE | INTCSR_CTR4INT_ENABLE
8206849faaSRostislav Lisovy | INTCSR_PCIINT_ENABLE,
8306849faaSRostislav Lisovy INTCSR_reg);
8406849faaSRostislav Lisovy break;
8506849faaSRostislav Lisovy }
8606849faaSRostislav Lisovy }
8706849faaSRostislav Lisovy
mf624_irq_handler(int irq,struct uio_info * info)8806849faaSRostislav Lisovy static irqreturn_t mf624_irq_handler(int irq, struct uio_info *info)
8906849faaSRostislav Lisovy {
9006849faaSRostislav Lisovy void __iomem *INTCSR_reg = info->mem[0].internal_addr + INTCSR;
9106849faaSRostislav Lisovy
9206849faaSRostislav Lisovy if ((ioread32(INTCSR_reg) & INTCSR_ADINT_ENABLE)
9306849faaSRostislav Lisovy && (ioread32(INTCSR_reg) & INTCSR_ADINT_STATUS)) {
9406849faaSRostislav Lisovy mf624_disable_interrupt(ADC, info);
9506849faaSRostislav Lisovy return IRQ_HANDLED;
9606849faaSRostislav Lisovy }
9706849faaSRostislav Lisovy
9806849faaSRostislav Lisovy if ((ioread32(INTCSR_reg) & INTCSR_CTR4INT_ENABLE)
9906849faaSRostislav Lisovy && (ioread32(INTCSR_reg) & INTCSR_CTR4INT_STATUS)) {
10006849faaSRostislav Lisovy mf624_disable_interrupt(CTR4, info);
10106849faaSRostislav Lisovy return IRQ_HANDLED;
10206849faaSRostislav Lisovy }
10306849faaSRostislav Lisovy
10406849faaSRostislav Lisovy return IRQ_NONE;
10506849faaSRostislav Lisovy }
10606849faaSRostislav Lisovy
mf624_irqcontrol(struct uio_info * info,s32 irq_on)10706849faaSRostislav Lisovy static int mf624_irqcontrol(struct uio_info *info, s32 irq_on)
10806849faaSRostislav Lisovy {
10906849faaSRostislav Lisovy if (irq_on == 0)
11006849faaSRostislav Lisovy mf624_disable_interrupt(ALL, info);
11106849faaSRostislav Lisovy else if (irq_on == 1)
11206849faaSRostislav Lisovy mf624_enable_interrupt(ALL, info);
11306849faaSRostislav Lisovy
11406849faaSRostislav Lisovy return 0;
11506849faaSRostislav Lisovy }
11606849faaSRostislav Lisovy
mf624_setup_mem(struct pci_dev * dev,int bar,struct uio_mem * mem,const char * name)117a15d2ddbSMichal Sojka static int mf624_setup_mem(struct pci_dev *dev, int bar, struct uio_mem *mem, const char *name)
118a15d2ddbSMichal Sojka {
119270579d9SMichal Sojka resource_size_t start = pci_resource_start(dev, bar);
120270579d9SMichal Sojka resource_size_t len = pci_resource_len(dev, bar);
121270579d9SMichal Sojka
122a15d2ddbSMichal Sojka mem->name = name;
123270579d9SMichal Sojka mem->addr = start & PAGE_MASK;
124270579d9SMichal Sojka mem->offs = start & ~PAGE_MASK;
125a15d2ddbSMichal Sojka if (!mem->addr)
126a15d2ddbSMichal Sojka return -ENODEV;
127270579d9SMichal Sojka mem->size = ((start & ~PAGE_MASK) + len + PAGE_SIZE - 1) & PAGE_MASK;
128a15d2ddbSMichal Sojka mem->memtype = UIO_MEM_PHYS;
129a15d2ddbSMichal Sojka mem->internal_addr = pci_ioremap_bar(dev, bar);
130a15d2ddbSMichal Sojka if (!mem->internal_addr)
131a15d2ddbSMichal Sojka return -ENODEV;
132a15d2ddbSMichal Sojka return 0;
133a15d2ddbSMichal Sojka }
134a15d2ddbSMichal Sojka
mf624_pci_probe(struct pci_dev * dev,const struct pci_device_id * id)13506849faaSRostislav Lisovy static int mf624_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
13606849faaSRostislav Lisovy {
13706849faaSRostislav Lisovy struct uio_info *info;
13806849faaSRostislav Lisovy
139*6b76c98bSAlexandru Ardelean info = devm_kzalloc(&dev->dev, sizeof(struct uio_info), GFP_KERNEL);
14006849faaSRostislav Lisovy if (!info)
14106849faaSRostislav Lisovy return -ENOMEM;
14206849faaSRostislav Lisovy
14306849faaSRostislav Lisovy if (pci_enable_device(dev))
144*6b76c98bSAlexandru Ardelean return -ENODEV;
14506849faaSRostislav Lisovy
14606849faaSRostislav Lisovy if (pci_request_regions(dev, "mf624"))
14706849faaSRostislav Lisovy goto out_disable;
14806849faaSRostislav Lisovy
14906849faaSRostislav Lisovy info->name = "mf624";
15006849faaSRostislav Lisovy info->version = "0.0.1";
15106849faaSRostislav Lisovy
15206849faaSRostislav Lisovy /* Note: Datasheet says device uses BAR0, BAR1, BAR2 -- do not trust it */
15306849faaSRostislav Lisovy
15406849faaSRostislav Lisovy /* BAR0 */
155a15d2ddbSMichal Sojka if (mf624_setup_mem(dev, 0, &info->mem[0], "PCI chipset, interrupts, status "
156a15d2ddbSMichal Sojka "bits, special functions"))
15706849faaSRostislav Lisovy goto out_release;
15806849faaSRostislav Lisovy /* BAR2 */
159a15d2ddbSMichal Sojka if (mf624_setup_mem(dev, 2, &info->mem[1], "ADC, DAC, DIO"))
16006849faaSRostislav Lisovy goto out_unmap0;
16106849faaSRostislav Lisovy
16206849faaSRostislav Lisovy /* BAR4 */
163a15d2ddbSMichal Sojka if (mf624_setup_mem(dev, 4, &info->mem[2], "Counter/timer chip"))
16406849faaSRostislav Lisovy goto out_unmap1;
16506849faaSRostislav Lisovy
16606849faaSRostislav Lisovy info->irq = dev->irq;
16706849faaSRostislav Lisovy info->irq_flags = IRQF_SHARED;
16806849faaSRostislav Lisovy info->handler = mf624_irq_handler;
16906849faaSRostislav Lisovy
17006849faaSRostislav Lisovy info->irqcontrol = mf624_irqcontrol;
17106849faaSRostislav Lisovy
17206849faaSRostislav Lisovy if (uio_register_device(&dev->dev, info))
17306849faaSRostislav Lisovy goto out_unmap2;
17406849faaSRostislav Lisovy
17506849faaSRostislav Lisovy pci_set_drvdata(dev, info);
17606849faaSRostislav Lisovy
17706849faaSRostislav Lisovy return 0;
17806849faaSRostislav Lisovy
17906849faaSRostislav Lisovy out_unmap2:
18006849faaSRostislav Lisovy iounmap(info->mem[2].internal_addr);
18106849faaSRostislav Lisovy out_unmap1:
18206849faaSRostislav Lisovy iounmap(info->mem[1].internal_addr);
18306849faaSRostislav Lisovy out_unmap0:
18406849faaSRostislav Lisovy iounmap(info->mem[0].internal_addr);
18506849faaSRostislav Lisovy
18606849faaSRostislav Lisovy out_release:
18706849faaSRostislav Lisovy pci_release_regions(dev);
18806849faaSRostislav Lisovy
18906849faaSRostislav Lisovy out_disable:
19006849faaSRostislav Lisovy pci_disable_device(dev);
19106849faaSRostislav Lisovy
19206849faaSRostislav Lisovy return -ENODEV;
19306849faaSRostislav Lisovy }
19406849faaSRostislav Lisovy
mf624_pci_remove(struct pci_dev * dev)19506849faaSRostislav Lisovy static void mf624_pci_remove(struct pci_dev *dev)
19606849faaSRostislav Lisovy {
19706849faaSRostislav Lisovy struct uio_info *info = pci_get_drvdata(dev);
19806849faaSRostislav Lisovy
19906849faaSRostislav Lisovy mf624_disable_interrupt(ALL, info);
20006849faaSRostislav Lisovy
20106849faaSRostislav Lisovy uio_unregister_device(info);
20206849faaSRostislav Lisovy pci_release_regions(dev);
20306849faaSRostislav Lisovy pci_disable_device(dev);
20406849faaSRostislav Lisovy
20506849faaSRostislav Lisovy iounmap(info->mem[0].internal_addr);
20606849faaSRostislav Lisovy iounmap(info->mem[1].internal_addr);
20706849faaSRostislav Lisovy iounmap(info->mem[2].internal_addr);
20806849faaSRostislav Lisovy }
20906849faaSRostislav Lisovy
2100f4054dfSJingoo Han static const struct pci_device_id mf624_pci_id[] = {
21106849faaSRostislav Lisovy { PCI_DEVICE(PCI_VENDOR_ID_HUMUSOFT, PCI_DEVICE_ID_MF624) },
21206849faaSRostislav Lisovy { 0, }
21306849faaSRostislav Lisovy };
21406849faaSRostislav Lisovy
21506849faaSRostislav Lisovy static struct pci_driver mf624_pci_driver = {
21606849faaSRostislav Lisovy .name = "mf624",
21706849faaSRostislav Lisovy .id_table = mf624_pci_id,
21806849faaSRostislav Lisovy .probe = mf624_pci_probe,
21906849faaSRostislav Lisovy .remove = mf624_pci_remove,
22006849faaSRostislav Lisovy };
22106849faaSRostislav Lisovy MODULE_DEVICE_TABLE(pci, mf624_pci_id);
22206849faaSRostislav Lisovy
22306849faaSRostislav Lisovy module_pci_driver(mf624_pci_driver);
22406849faaSRostislav Lisovy MODULE_LICENSE("GPL v2");
22506849faaSRostislav Lisovy MODULE_AUTHOR("Rostislav Lisovy <lisovy@gmail.com>");
226