blob: 790412f8dfd5eba622dfd152e79070bb165b5eb8 [file] [log] [blame]
Thomas Gleixner74ba9202019-05-20 09:19:02 +02001// SPDX-License-Identifier: GPL-2.0-or-later
Rostislav Lisovy06849fa2013-08-30 14:58:02 +02002/*
Jeff Johnson63921942024-05-23 18:30:35 -07003 * UIO driver for Humusoft MF624 DAQ card.
Rostislav Lisovy06849fa2013-08-30 14:58:02 +02004 * Copyright (C) 2011 Rostislav Lisovy <lisovy@gmail.com>,
5 * Czech Technical University in Prague
Rostislav Lisovy06849fa2013-08-30 14:58:02 +02006 */
7
8#include <linux/init.h>
9#include <linux/module.h>
10#include <linux/device.h>
11#include <linux/pci.h>
12#include <linux/slab.h>
13#include <linux/io.h>
14#include <linux/kernel.h>
15#include <linux/uio_driver.h>
16
17#define PCI_VENDOR_ID_HUMUSOFT 0x186c
18#define PCI_DEVICE_ID_MF624 0x0624
19#define PCI_SUBVENDOR_ID_HUMUSOFT 0x186c
20#define PCI_SUBDEVICE_DEVICE 0x0624
21
22/* BAR0 Interrupt control/status register */
23#define INTCSR 0x4C
24#define INTCSR_ADINT_ENABLE (1 << 0)
25#define INTCSR_CTR4INT_ENABLE (1 << 3)
26#define INTCSR_PCIINT_ENABLE (1 << 6)
27#define INTCSR_ADINT_STATUS (1 << 2)
28#define INTCSR_CTR4INT_STATUS (1 << 5)
29
30enum mf624_interrupt_source {ADC, CTR4, ALL};
31
Fengguang Wu497b46d2013-09-02 10:38:33 +020032static void mf624_disable_interrupt(enum mf624_interrupt_source source,
Rostislav Lisovy06849fa2013-08-30 14:58:02 +020033 struct uio_info *info)
34{
35 void __iomem *INTCSR_reg = info->mem[0].internal_addr + INTCSR;
36
37 switch (source) {
38 case ADC:
39 iowrite32(ioread32(INTCSR_reg)
40 & ~(INTCSR_ADINT_ENABLE | INTCSR_PCIINT_ENABLE),
41 INTCSR_reg);
42 break;
43
44 case CTR4:
45 iowrite32(ioread32(INTCSR_reg)
46 & ~(INTCSR_CTR4INT_ENABLE | INTCSR_PCIINT_ENABLE),
47 INTCSR_reg);
48 break;
49
50 case ALL:
51 default:
52 iowrite32(ioread32(INTCSR_reg)
53 & ~(INTCSR_ADINT_ENABLE | INTCSR_CTR4INT_ENABLE
54 | INTCSR_PCIINT_ENABLE),
55 INTCSR_reg);
56 break;
57 }
58}
59
Fengguang Wu497b46d2013-09-02 10:38:33 +020060static void mf624_enable_interrupt(enum mf624_interrupt_source source,
Rostislav Lisovy06849fa2013-08-30 14:58:02 +020061 struct uio_info *info)
62{
63 void __iomem *INTCSR_reg = info->mem[0].internal_addr + INTCSR;
64
65 switch (source) {
66 case ADC:
67 iowrite32(ioread32(INTCSR_reg)
68 | INTCSR_ADINT_ENABLE | INTCSR_PCIINT_ENABLE,
69 INTCSR_reg);
70 break;
71
72 case CTR4:
73 iowrite32(ioread32(INTCSR_reg)
74 | INTCSR_CTR4INT_ENABLE | INTCSR_PCIINT_ENABLE,
75 INTCSR_reg);
76 break;
77
78 case ALL:
79 default:
80 iowrite32(ioread32(INTCSR_reg)
81 | INTCSR_ADINT_ENABLE | INTCSR_CTR4INT_ENABLE
82 | INTCSR_PCIINT_ENABLE,
83 INTCSR_reg);
84 break;
85 }
86}
87
88static irqreturn_t mf624_irq_handler(int irq, struct uio_info *info)
89{
90 void __iomem *INTCSR_reg = info->mem[0].internal_addr + INTCSR;
91
92 if ((ioread32(INTCSR_reg) & INTCSR_ADINT_ENABLE)
93 && (ioread32(INTCSR_reg) & INTCSR_ADINT_STATUS)) {
94 mf624_disable_interrupt(ADC, info);
95 return IRQ_HANDLED;
96 }
97
98 if ((ioread32(INTCSR_reg) & INTCSR_CTR4INT_ENABLE)
99 && (ioread32(INTCSR_reg) & INTCSR_CTR4INT_STATUS)) {
100 mf624_disable_interrupt(CTR4, info);
101 return IRQ_HANDLED;
102 }
103
104 return IRQ_NONE;
105}
106
107static int mf624_irqcontrol(struct uio_info *info, s32 irq_on)
108{
109 if (irq_on == 0)
110 mf624_disable_interrupt(ALL, info);
111 else if (irq_on == 1)
112 mf624_enable_interrupt(ALL, info);
113
114 return 0;
115}
116
Michal Sojkaa15d2dd2017-03-16 14:50:09 +0100117static int mf624_setup_mem(struct pci_dev *dev, int bar, struct uio_mem *mem, const char *name)
118{
Michal Sojka270579d2017-03-16 14:50:10 +0100119 resource_size_t start = pci_resource_start(dev, bar);
120 resource_size_t len = pci_resource_len(dev, bar);
121
Michal Sojkaa15d2dd2017-03-16 14:50:09 +0100122 mem->name = name;
Michal Sojka270579d2017-03-16 14:50:10 +0100123 mem->addr = start & PAGE_MASK;
124 mem->offs = start & ~PAGE_MASK;
Michal Sojkaa15d2dd2017-03-16 14:50:09 +0100125 if (!mem->addr)
126 return -ENODEV;
Michal Sojka270579d2017-03-16 14:50:10 +0100127 mem->size = ((start & ~PAGE_MASK) + len + PAGE_SIZE - 1) & PAGE_MASK;
Michal Sojkaa15d2dd2017-03-16 14:50:09 +0100128 mem->memtype = UIO_MEM_PHYS;
129 mem->internal_addr = pci_ioremap_bar(dev, bar);
130 if (!mem->internal_addr)
131 return -ENODEV;
132 return 0;
133}
134
Rostislav Lisovy06849fa2013-08-30 14:58:02 +0200135static int mf624_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
136{
137 struct uio_info *info;
138
Alexandru Ardelean6b76c982020-11-20 10:42:06 +0200139 info = devm_kzalloc(&dev->dev, sizeof(struct uio_info), GFP_KERNEL);
Rostislav Lisovy06849fa2013-08-30 14:58:02 +0200140 if (!info)
141 return -ENOMEM;
142
143 if (pci_enable_device(dev))
Alexandru Ardelean6b76c982020-11-20 10:42:06 +0200144 return -ENODEV;
Rostislav Lisovy06849fa2013-08-30 14:58:02 +0200145
146 if (pci_request_regions(dev, "mf624"))
147 goto out_disable;
148
149 info->name = "mf624";
150 info->version = "0.0.1";
151
152 /* Note: Datasheet says device uses BAR0, BAR1, BAR2 -- do not trust it */
153
154 /* BAR0 */
Michal Sojkaa15d2dd2017-03-16 14:50:09 +0100155 if (mf624_setup_mem(dev, 0, &info->mem[0], "PCI chipset, interrupts, status "
156 "bits, special functions"))
Rostislav Lisovy06849fa2013-08-30 14:58:02 +0200157 goto out_release;
Rostislav Lisovy06849fa2013-08-30 14:58:02 +0200158 /* BAR2 */
Michal Sojkaa15d2dd2017-03-16 14:50:09 +0100159 if (mf624_setup_mem(dev, 2, &info->mem[1], "ADC, DAC, DIO"))
Rostislav Lisovy06849fa2013-08-30 14:58:02 +0200160 goto out_unmap0;
161
162 /* BAR4 */
Michal Sojkaa15d2dd2017-03-16 14:50:09 +0100163 if (mf624_setup_mem(dev, 4, &info->mem[2], "Counter/timer chip"))
Rostislav Lisovy06849fa2013-08-30 14:58:02 +0200164 goto out_unmap1;
165
166 info->irq = dev->irq;
167 info->irq_flags = IRQF_SHARED;
168 info->handler = mf624_irq_handler;
169
170 info->irqcontrol = mf624_irqcontrol;
171
172 if (uio_register_device(&dev->dev, info))
173 goto out_unmap2;
174
175 pci_set_drvdata(dev, info);
176
177 return 0;
178
179out_unmap2:
180 iounmap(info->mem[2].internal_addr);
181out_unmap1:
182 iounmap(info->mem[1].internal_addr);
183out_unmap0:
184 iounmap(info->mem[0].internal_addr);
185
186out_release:
187 pci_release_regions(dev);
188
189out_disable:
190 pci_disable_device(dev);
191
Rostislav Lisovy06849fa2013-08-30 14:58:02 +0200192 return -ENODEV;
193}
194
195static void mf624_pci_remove(struct pci_dev *dev)
196{
197 struct uio_info *info = pci_get_drvdata(dev);
198
199 mf624_disable_interrupt(ALL, info);
200
201 uio_unregister_device(info);
202 pci_release_regions(dev);
203 pci_disable_device(dev);
Rostislav Lisovy06849fa2013-08-30 14:58:02 +0200204
205 iounmap(info->mem[0].internal_addr);
206 iounmap(info->mem[1].internal_addr);
207 iounmap(info->mem[2].internal_addr);
Rostislav Lisovy06849fa2013-08-30 14:58:02 +0200208}
209
Jingoo Han0f4054d2013-12-03 08:27:13 +0900210static const struct pci_device_id mf624_pci_id[] = {
Rostislav Lisovy06849fa2013-08-30 14:58:02 +0200211 { PCI_DEVICE(PCI_VENDOR_ID_HUMUSOFT, PCI_DEVICE_ID_MF624) },
212 { 0, }
213};
214
215static struct pci_driver mf624_pci_driver = {
216 .name = "mf624",
217 .id_table = mf624_pci_id,
218 .probe = mf624_pci_probe,
219 .remove = mf624_pci_remove,
220};
221MODULE_DEVICE_TABLE(pci, mf624_pci_id);
222
223module_pci_driver(mf624_pci_driver);
Jeff Johnson63921942024-05-23 18:30:35 -0700224MODULE_DESCRIPTION("UIO driver for Humusoft MF624 DAQ card");
Rostislav Lisovy06849fa2013-08-30 14:58:02 +0200225MODULE_LICENSE("GPL v2");
226MODULE_AUTHOR("Rostislav Lisovy <lisovy@gmail.com>");