1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * UIO driver fo Humusoft MF624 DAQ card.
4  * Copyright (C) 2011 Rostislav Lisovy <lisovy@gmail.com>,
5  *                    Czech Technical University in Prague
6  */
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 
30 enum mf624_interrupt_source {ADC, CTR4, ALL};
31 
mf624_disable_interrupt(enum mf624_interrupt_source source,struct uio_info * info)32 static void mf624_disable_interrupt(enum mf624_interrupt_source source,
33 			     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 
mf624_enable_interrupt(enum mf624_interrupt_source source,struct uio_info * info)60 static void mf624_enable_interrupt(enum mf624_interrupt_source source,
61 			    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 
mf624_irq_handler(int irq,struct uio_info * info)88 static 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 
mf624_irqcontrol(struct uio_info * info,s32 irq_on)107 static 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 
mf624_setup_mem(struct pci_dev * dev,int bar,struct uio_mem * mem,const char * name)117 static int mf624_setup_mem(struct pci_dev *dev, int bar, struct uio_mem *mem, const char *name)
118 {
119 	resource_size_t start = pci_resource_start(dev, bar);
120 	resource_size_t len = pci_resource_len(dev, bar);
121 
122 	mem->name = name;
123 	mem->addr = start & PAGE_MASK;
124 	mem->offs = start & ~PAGE_MASK;
125 	if (!mem->addr)
126 		return -ENODEV;
127 	mem->size = ((start & ~PAGE_MASK) + len + PAGE_SIZE - 1) & PAGE_MASK;
128 	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 
mf624_pci_probe(struct pci_dev * dev,const struct pci_device_id * id)135 static int mf624_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
136 {
137 	struct uio_info *info;
138 
139 	info = devm_kzalloc(&dev->dev, sizeof(struct uio_info), GFP_KERNEL);
140 	if (!info)
141 		return -ENOMEM;
142 
143 	if (pci_enable_device(dev))
144 		return -ENODEV;
145 
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 */
155 	if (mf624_setup_mem(dev, 0, &info->mem[0], "PCI chipset, interrupts, status "
156 			    "bits, special functions"))
157 		goto out_release;
158 	/* BAR2 */
159 	if (mf624_setup_mem(dev, 2, &info->mem[1], "ADC, DAC, DIO"))
160 		goto out_unmap0;
161 
162 	/* BAR4 */
163 	if (mf624_setup_mem(dev, 4, &info->mem[2], "Counter/timer chip"))
164 		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 
179 out_unmap2:
180 	iounmap(info->mem[2].internal_addr);
181 out_unmap1:
182 	iounmap(info->mem[1].internal_addr);
183 out_unmap0:
184 	iounmap(info->mem[0].internal_addr);
185 
186 out_release:
187 	pci_release_regions(dev);
188 
189 out_disable:
190 	pci_disable_device(dev);
191 
192 	return -ENODEV;
193 }
194 
mf624_pci_remove(struct pci_dev * dev)195 static 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);
204 
205 	iounmap(info->mem[0].internal_addr);
206 	iounmap(info->mem[1].internal_addr);
207 	iounmap(info->mem[2].internal_addr);
208 }
209 
210 static const struct pci_device_id mf624_pci_id[] = {
211 	{ PCI_DEVICE(PCI_VENDOR_ID_HUMUSOFT, PCI_DEVICE_ID_MF624) },
212 	{ 0, }
213 };
214 
215 static 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 };
221 MODULE_DEVICE_TABLE(pci, mf624_pci_id);
222 
223 module_pci_driver(mf624_pci_driver);
224 MODULE_LICENSE("GPL v2");
225 MODULE_AUTHOR("Rostislav Lisovy <lisovy@gmail.com>");
226