diff options
author | Rostislav Lisovy <lisovy@gmail.com> | 2013-08-30 08:58:02 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2013-08-30 15:12:49 -0400 |
commit | 06849faab58fc7ff9f4eae2532380c2a746a6f47 (patch) | |
tree | ef168536533cdbf4ea074a0d821ed0503465a674 /drivers/uio | |
parent | 08cb2e2148f7633e05ad211cff9234acca60a194 (diff) |
drivers: uio: Add driver for Humusoft MF624 DAQ PCI card
Signed-off-by: Rostislav Lisovy <lisovy@gmail.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/uio')
-rw-r--r-- | drivers/uio/Kconfig | 13 | ||||
-rw-r--r-- | drivers/uio/Makefile | 1 | ||||
-rw-r--r-- | drivers/uio/uio_mf624.c | 247 |
3 files changed, 261 insertions, 0 deletions
diff --git a/drivers/uio/Kconfig b/drivers/uio/Kconfig index a81d16389a58..86d98c86e147 100644 --- a/drivers/uio/Kconfig +++ b/drivers/uio/Kconfig | |||
@@ -121,4 +121,17 @@ config UIO_PRUSS | |||
121 | To compile this driver as a module, choose M here: the module | 121 | To compile this driver as a module, choose M here: the module |
122 | will be called uio_pruss. | 122 | will be called uio_pruss. |
123 | 123 | ||
124 | config UIO_MF624 | ||
125 | tristate "Humusoft MF624 DAQ PCI card driver" | ||
126 | depends on PCI | ||
127 | help | ||
128 | Userspace I/O interface for the Humusoft MF624 PCI card. | ||
129 | A sample userspace application using this driver is available | ||
130 | (among other MF624 related information and software components) | ||
131 | for download in a git repository: | ||
132 | |||
133 | git clone git://rtime.felk.cvut.cz/mf6xx.git | ||
134 | |||
135 | If you compile this as a module, it will be called uio_mf624. | ||
136 | |||
124 | endif | 137 | endif |
diff --git a/drivers/uio/Makefile b/drivers/uio/Makefile index ea015a290127..d3218bde3aeb 100644 --- a/drivers/uio/Makefile +++ b/drivers/uio/Makefile | |||
@@ -7,3 +7,4 @@ obj-$(CONFIG_UIO_SERCOS3) += uio_sercos3.o | |||
7 | obj-$(CONFIG_UIO_PCI_GENERIC) += uio_pci_generic.o | 7 | obj-$(CONFIG_UIO_PCI_GENERIC) += uio_pci_generic.o |
8 | obj-$(CONFIG_UIO_NETX) += uio_netx.o | 8 | obj-$(CONFIG_UIO_NETX) += uio_netx.o |
9 | obj-$(CONFIG_UIO_PRUSS) += uio_pruss.o | 9 | obj-$(CONFIG_UIO_PRUSS) += uio_pruss.o |
10 | obj-$(CONFIG_UIO_MF624) += uio_mf624.o | ||
diff --git a/drivers/uio/uio_mf624.c b/drivers/uio/uio_mf624.c new file mode 100644 index 000000000000..a1768b2f4493 --- /dev/null +++ b/drivers/uio/uio_mf624.c | |||
@@ -0,0 +1,247 @@ | |||
1 | /* | ||
2 | * UIO driver fo Humusoft MF624 DAQ card. | ||
3 | * Copyright (C) 2011 Rostislav Lisovy <lisovy@gmail.com>, | ||
4 | * Czech Technical University in Prague | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
19 | */ | ||
20 | |||
21 | #include <linux/init.h> | ||
22 | #include <linux/module.h> | ||
23 | #include <linux/device.h> | ||
24 | #include <linux/pci.h> | ||
25 | #include <linux/slab.h> | ||
26 | #include <linux/io.h> | ||
27 | #include <linux/kernel.h> | ||
28 | #include <linux/uio_driver.h> | ||
29 | |||
30 | #define PCI_VENDOR_ID_HUMUSOFT 0x186c | ||
31 | #define PCI_DEVICE_ID_MF624 0x0624 | ||
32 | #define PCI_SUBVENDOR_ID_HUMUSOFT 0x186c | ||
33 | #define PCI_SUBDEVICE_DEVICE 0x0624 | ||
34 | |||
35 | /* BAR0 Interrupt control/status register */ | ||
36 | #define INTCSR 0x4C | ||
37 | #define INTCSR_ADINT_ENABLE (1 << 0) | ||
38 | #define INTCSR_CTR4INT_ENABLE (1 << 3) | ||
39 | #define INTCSR_PCIINT_ENABLE (1 << 6) | ||
40 | #define INTCSR_ADINT_STATUS (1 << 2) | ||
41 | #define INTCSR_CTR4INT_STATUS (1 << 5) | ||
42 | |||
43 | enum mf624_interrupt_source {ADC, CTR4, ALL}; | ||
44 | |||
45 | void mf624_disable_interrupt(enum mf624_interrupt_source source, | ||
46 | struct uio_info *info) | ||
47 | { | ||
48 | void __iomem *INTCSR_reg = info->mem[0].internal_addr + INTCSR; | ||
49 | |||
50 | switch (source) { | ||
51 | case ADC: | ||
52 | iowrite32(ioread32(INTCSR_reg) | ||
53 | & ~(INTCSR_ADINT_ENABLE | INTCSR_PCIINT_ENABLE), | ||
54 | INTCSR_reg); | ||
55 | break; | ||
56 | |||
57 | case CTR4: | ||
58 | iowrite32(ioread32(INTCSR_reg) | ||
59 | & ~(INTCSR_CTR4INT_ENABLE | INTCSR_PCIINT_ENABLE), | ||
60 | INTCSR_reg); | ||
61 | break; | ||
62 | |||
63 | case ALL: | ||
64 | default: | ||
65 | iowrite32(ioread32(INTCSR_reg) | ||
66 | & ~(INTCSR_ADINT_ENABLE | INTCSR_CTR4INT_ENABLE | ||
67 | | INTCSR_PCIINT_ENABLE), | ||
68 | INTCSR_reg); | ||
69 | break; | ||
70 | } | ||
71 | } | ||
72 | |||
73 | void mf624_enable_interrupt(enum mf624_interrupt_source source, | ||
74 | struct uio_info *info) | ||
75 | { | ||
76 | void __iomem *INTCSR_reg = info->mem[0].internal_addr + INTCSR; | ||
77 | |||
78 | switch (source) { | ||
79 | case ADC: | ||
80 | iowrite32(ioread32(INTCSR_reg) | ||
81 | | INTCSR_ADINT_ENABLE | INTCSR_PCIINT_ENABLE, | ||
82 | INTCSR_reg); | ||
83 | break; | ||
84 | |||
85 | case CTR4: | ||
86 | iowrite32(ioread32(INTCSR_reg) | ||
87 | | INTCSR_CTR4INT_ENABLE | INTCSR_PCIINT_ENABLE, | ||
88 | INTCSR_reg); | ||
89 | break; | ||
90 | |||
91 | case ALL: | ||
92 | default: | ||
93 | iowrite32(ioread32(INTCSR_reg) | ||
94 | | INTCSR_ADINT_ENABLE | INTCSR_CTR4INT_ENABLE | ||
95 | | INTCSR_PCIINT_ENABLE, | ||
96 | INTCSR_reg); | ||
97 | break; | ||
98 | } | ||
99 | } | ||
100 | |||
101 | static irqreturn_t mf624_irq_handler(int irq, struct uio_info *info) | ||
102 | { | ||
103 | void __iomem *INTCSR_reg = info->mem[0].internal_addr + INTCSR; | ||
104 | |||
105 | if ((ioread32(INTCSR_reg) & INTCSR_ADINT_ENABLE) | ||
106 | && (ioread32(INTCSR_reg) & INTCSR_ADINT_STATUS)) { | ||
107 | mf624_disable_interrupt(ADC, info); | ||
108 | return IRQ_HANDLED; | ||
109 | } | ||
110 | |||
111 | if ((ioread32(INTCSR_reg) & INTCSR_CTR4INT_ENABLE) | ||
112 | && (ioread32(INTCSR_reg) & INTCSR_CTR4INT_STATUS)) { | ||
113 | mf624_disable_interrupt(CTR4, info); | ||
114 | return IRQ_HANDLED; | ||
115 | } | ||
116 | |||
117 | return IRQ_NONE; | ||
118 | } | ||
119 | |||
120 | static int mf624_irqcontrol(struct uio_info *info, s32 irq_on) | ||
121 | { | ||
122 | if (irq_on == 0) | ||
123 | mf624_disable_interrupt(ALL, info); | ||
124 | else if (irq_on == 1) | ||
125 | mf624_enable_interrupt(ALL, info); | ||
126 | |||
127 | return 0; | ||
128 | } | ||
129 | |||
130 | static int mf624_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) | ||
131 | { | ||
132 | struct uio_info *info; | ||
133 | |||
134 | info = kzalloc(sizeof(struct uio_info), GFP_KERNEL); | ||
135 | if (!info) | ||
136 | return -ENOMEM; | ||
137 | |||
138 | if (pci_enable_device(dev)) | ||
139 | goto out_free; | ||
140 | |||
141 | if (pci_request_regions(dev, "mf624")) | ||
142 | goto out_disable; | ||
143 | |||
144 | info->name = "mf624"; | ||
145 | info->version = "0.0.1"; | ||
146 | |||
147 | /* Note: Datasheet says device uses BAR0, BAR1, BAR2 -- do not trust it */ | ||
148 | |||
149 | /* BAR0 */ | ||
150 | info->mem[0].name = "PCI chipset, interrupts, status " | ||
151 | "bits, special functions"; | ||
152 | info->mem[0].addr = pci_resource_start(dev, 0); | ||
153 | if (!info->mem[0].addr) | ||
154 | goto out_release; | ||
155 | info->mem[0].size = pci_resource_len(dev, 0); | ||
156 | info->mem[0].memtype = UIO_MEM_PHYS; | ||
157 | info->mem[0].internal_addr = pci_ioremap_bar(dev, 0); | ||
158 | if (!info->mem[0].internal_addr) | ||
159 | goto out_release; | ||
160 | |||
161 | /* BAR2 */ | ||
162 | info->mem[1].name = "ADC, DAC, DIO"; | ||
163 | info->mem[1].addr = pci_resource_start(dev, 2); | ||
164 | if (!info->mem[1].addr) | ||
165 | goto out_unmap0; | ||
166 | info->mem[1].size = pci_resource_len(dev, 2); | ||
167 | info->mem[1].memtype = UIO_MEM_PHYS; | ||
168 | info->mem[1].internal_addr = pci_ioremap_bar(dev, 2); | ||
169 | if (!info->mem[1].internal_addr) | ||
170 | goto out_unmap0; | ||
171 | |||
172 | /* BAR4 */ | ||
173 | info->mem[2].name = "Counter/timer chip"; | ||
174 | info->mem[2].addr = pci_resource_start(dev, 4); | ||
175 | if (!info->mem[2].addr) | ||
176 | goto out_unmap1; | ||
177 | info->mem[2].size = pci_resource_len(dev, 4); | ||
178 | info->mem[2].memtype = UIO_MEM_PHYS; | ||
179 | info->mem[2].internal_addr = pci_ioremap_bar(dev, 4); | ||
180 | if (!info->mem[2].internal_addr) | ||
181 | goto out_unmap1; | ||
182 | |||
183 | info->irq = dev->irq; | ||
184 | info->irq_flags = IRQF_SHARED; | ||
185 | info->handler = mf624_irq_handler; | ||
186 | |||
187 | info->irqcontrol = mf624_irqcontrol; | ||
188 | |||
189 | if (uio_register_device(&dev->dev, info)) | ||
190 | goto out_unmap2; | ||
191 | |||
192 | pci_set_drvdata(dev, info); | ||
193 | |||
194 | return 0; | ||
195 | |||
196 | out_unmap2: | ||
197 | iounmap(info->mem[2].internal_addr); | ||
198 | out_unmap1: | ||
199 | iounmap(info->mem[1].internal_addr); | ||
200 | out_unmap0: | ||
201 | iounmap(info->mem[0].internal_addr); | ||
202 | |||
203 | out_release: | ||
204 | pci_release_regions(dev); | ||
205 | |||
206 | out_disable: | ||
207 | pci_disable_device(dev); | ||
208 | |||
209 | out_free: | ||
210 | kfree(info); | ||
211 | return -ENODEV; | ||
212 | } | ||
213 | |||
214 | static void mf624_pci_remove(struct pci_dev *dev) | ||
215 | { | ||
216 | struct uio_info *info = pci_get_drvdata(dev); | ||
217 | |||
218 | mf624_disable_interrupt(ALL, info); | ||
219 | |||
220 | uio_unregister_device(info); | ||
221 | pci_release_regions(dev); | ||
222 | pci_disable_device(dev); | ||
223 | pci_set_drvdata(dev, NULL); | ||
224 | |||
225 | iounmap(info->mem[0].internal_addr); | ||
226 | iounmap(info->mem[1].internal_addr); | ||
227 | iounmap(info->mem[2].internal_addr); | ||
228 | |||
229 | kfree(info); | ||
230 | } | ||
231 | |||
232 | static DEFINE_PCI_DEVICE_TABLE(mf624_pci_id) = { | ||
233 | { PCI_DEVICE(PCI_VENDOR_ID_HUMUSOFT, PCI_DEVICE_ID_MF624) }, | ||
234 | { 0, } | ||
235 | }; | ||
236 | |||
237 | static struct pci_driver mf624_pci_driver = { | ||
238 | .name = "mf624", | ||
239 | .id_table = mf624_pci_id, | ||
240 | .probe = mf624_pci_probe, | ||
241 | .remove = mf624_pci_remove, | ||
242 | }; | ||
243 | MODULE_DEVICE_TABLE(pci, mf624_pci_id); | ||
244 | |||
245 | module_pci_driver(mf624_pci_driver); | ||
246 | MODULE_LICENSE("GPL v2"); | ||
247 | MODULE_AUTHOR("Rostislav Lisovy <lisovy@gmail.com>"); | ||