Linux Audio

Check our new training course

Loading...
v4.17
  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
 43enum mf624_interrupt_source {ADC, CTR4, ALL};
 44
 45static 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
 73static 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
101static 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
120static 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
130static int mf624_setup_mem(struct pci_dev *dev, int bar, struct uio_mem *mem, const char *name)
131{
132	resource_size_t start = pci_resource_start(dev, bar);
133	resource_size_t len = pci_resource_len(dev, bar);
134
135	mem->name = name;
136	mem->addr = start & PAGE_MASK;
137	mem->offs = start & ~PAGE_MASK;
138	if (!mem->addr)
139		return -ENODEV;
140	mem->size = ((start & ~PAGE_MASK) + len + PAGE_SIZE - 1) & PAGE_MASK;
141	mem->memtype = UIO_MEM_PHYS;
142	mem->internal_addr = pci_ioremap_bar(dev, bar);
143	if (!mem->internal_addr)
144		return -ENODEV;
145	return 0;
146}
147
148static int mf624_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
149{
150	struct uio_info *info;
151
152	info = kzalloc(sizeof(struct uio_info), GFP_KERNEL);
153	if (!info)
154		return -ENOMEM;
155
156	if (pci_enable_device(dev))
157		goto out_free;
158
159	if (pci_request_regions(dev, "mf624"))
160		goto out_disable;
161
162	info->name = "mf624";
163	info->version = "0.0.1";
164
165	/* Note: Datasheet says device uses BAR0, BAR1, BAR2 -- do not trust it */
166
167	/* BAR0 */
168	if (mf624_setup_mem(dev, 0, &info->mem[0], "PCI chipset, interrupts, status "
169			    "bits, special functions"))
 
 
170		goto out_release;
 
 
 
 
 
 
171	/* BAR2 */
172	if (mf624_setup_mem(dev, 2, &info->mem[1], "ADC, DAC, DIO"))
 
 
 
 
 
 
 
173		goto out_unmap0;
174
175	/* BAR4 */
176	if (mf624_setup_mem(dev, 4, &info->mem[2], "Counter/timer chip"))
 
 
 
 
 
 
 
177		goto out_unmap1;
178
179	info->irq = dev->irq;
180	info->irq_flags = IRQF_SHARED;
181	info->handler = mf624_irq_handler;
182
183	info->irqcontrol = mf624_irqcontrol;
184
185	if (uio_register_device(&dev->dev, info))
186		goto out_unmap2;
187
188	pci_set_drvdata(dev, info);
189
190	return 0;
191
192out_unmap2:
193	iounmap(info->mem[2].internal_addr);
194out_unmap1:
195	iounmap(info->mem[1].internal_addr);
196out_unmap0:
197	iounmap(info->mem[0].internal_addr);
198
199out_release:
200	pci_release_regions(dev);
201
202out_disable:
203	pci_disable_device(dev);
204
205out_free:
206	kfree(info);
207	return -ENODEV;
208}
209
210static void mf624_pci_remove(struct pci_dev *dev)
211{
212	struct uio_info *info = pci_get_drvdata(dev);
213
214	mf624_disable_interrupt(ALL, info);
215
216	uio_unregister_device(info);
217	pci_release_regions(dev);
218	pci_disable_device(dev);
219
220	iounmap(info->mem[0].internal_addr);
221	iounmap(info->mem[1].internal_addr);
222	iounmap(info->mem[2].internal_addr);
223
224	kfree(info);
225}
226
227static const struct pci_device_id mf624_pci_id[] = {
228	{ PCI_DEVICE(PCI_VENDOR_ID_HUMUSOFT, PCI_DEVICE_ID_MF624) },
229	{ 0, }
230};
231
232static struct pci_driver mf624_pci_driver = {
233	.name = "mf624",
234	.id_table = mf624_pci_id,
235	.probe = mf624_pci_probe,
236	.remove = mf624_pci_remove,
237};
238MODULE_DEVICE_TABLE(pci, mf624_pci_id);
239
240module_pci_driver(mf624_pci_driver);
241MODULE_LICENSE("GPL v2");
242MODULE_AUTHOR("Rostislav Lisovy <lisovy@gmail.com>");
v4.10.11
  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
 43enum mf624_interrupt_source {ADC, CTR4, ALL};
 44
 45static 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
 73static 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
101static 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
120static 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
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
130static 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
196out_unmap2:
197	iounmap(info->mem[2].internal_addr);
198out_unmap1:
199	iounmap(info->mem[1].internal_addr);
200out_unmap0:
201	iounmap(info->mem[0].internal_addr);
202
203out_release:
204	pci_release_regions(dev);
205
206out_disable:
207	pci_disable_device(dev);
208
209out_free:
210	kfree(info);
211	return -ENODEV;
212}
213
214static 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
224	iounmap(info->mem[0].internal_addr);
225	iounmap(info->mem[1].internal_addr);
226	iounmap(info->mem[2].internal_addr);
227
228	kfree(info);
229}
230
231static const struct pci_device_id mf624_pci_id[] = {
232	{ PCI_DEVICE(PCI_VENDOR_ID_HUMUSOFT, PCI_DEVICE_ID_MF624) },
233	{ 0, }
234};
235
236static struct pci_driver mf624_pci_driver = {
237	.name = "mf624",
238	.id_table = mf624_pci_id,
239	.probe = mf624_pci_probe,
240	.remove = mf624_pci_remove,
241};
242MODULE_DEVICE_TABLE(pci, mf624_pci_id);
243
244module_pci_driver(mf624_pci_driver);
245MODULE_LICENSE("GPL v2");
246MODULE_AUTHOR("Rostislav Lisovy <lisovy@gmail.com>");