Blame view

drivers/uio/uio_mf624.c 5.25 KB
74ba9207e   Thomas Gleixner   treewide: Replace...
1
  // SPDX-License-Identifier: GPL-2.0-or-later
06849faab   Rostislav Lisovy   drivers: uio: Add...
2
3
4
5
  /*
   * UIO driver fo Humusoft MF624 DAQ card.
   * Copyright (C) 2011 Rostislav Lisovy <lisovy@gmail.com>,
   *                    Czech Technical University in Prague
06849faab   Rostislav Lisovy   drivers: uio: Add...
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
   */
  
  #include <linux/init.h>
  #include <linux/module.h>
  #include <linux/device.h>
  #include <linux/pci.h>
  #include <linux/slab.h>
  #include <linux/io.h>
  #include <linux/kernel.h>
  #include <linux/uio_driver.h>
  
  #define PCI_VENDOR_ID_HUMUSOFT		0x186c
  #define PCI_DEVICE_ID_MF624		0x0624
  #define PCI_SUBVENDOR_ID_HUMUSOFT	0x186c
  #define PCI_SUBDEVICE_DEVICE		0x0624
  
  /* BAR0 Interrupt control/status register */
  #define INTCSR				0x4C
  #define INTCSR_ADINT_ENABLE		(1 << 0)
  #define INTCSR_CTR4INT_ENABLE		(1 << 3)
  #define INTCSR_PCIINT_ENABLE		(1 << 6)
  #define INTCSR_ADINT_STATUS		(1 << 2)
  #define INTCSR_CTR4INT_STATUS		(1 << 5)
  
  enum mf624_interrupt_source {ADC, CTR4, ALL};
497b46dbc   Fengguang Wu   drivers: uio: mf6...
31
  static void mf624_disable_interrupt(enum mf624_interrupt_source source,
06849faab   Rostislav Lisovy   drivers: uio: Add...
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
  			     struct uio_info *info)
  {
  	void __iomem *INTCSR_reg = info->mem[0].internal_addr + INTCSR;
  
  	switch (source) {
  	case ADC:
  		iowrite32(ioread32(INTCSR_reg)
  			& ~(INTCSR_ADINT_ENABLE | INTCSR_PCIINT_ENABLE),
  			INTCSR_reg);
  		break;
  
  	case CTR4:
  		iowrite32(ioread32(INTCSR_reg)
  			& ~(INTCSR_CTR4INT_ENABLE | INTCSR_PCIINT_ENABLE),
  			INTCSR_reg);
  		break;
  
  	case ALL:
  	default:
  		iowrite32(ioread32(INTCSR_reg)
  			& ~(INTCSR_ADINT_ENABLE | INTCSR_CTR4INT_ENABLE
  			    | INTCSR_PCIINT_ENABLE),
  			INTCSR_reg);
  		break;
  	}
  }
497b46dbc   Fengguang Wu   drivers: uio: mf6...
58
  static void mf624_enable_interrupt(enum mf624_interrupt_source source,
06849faab   Rostislav Lisovy   drivers: uio: Add...
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
  			    struct uio_info *info)
  {
  	void __iomem *INTCSR_reg = info->mem[0].internal_addr + INTCSR;
  
  	switch (source) {
  	case ADC:
  		iowrite32(ioread32(INTCSR_reg)
  			| INTCSR_ADINT_ENABLE | INTCSR_PCIINT_ENABLE,
  			INTCSR_reg);
  		break;
  
  	case CTR4:
  		iowrite32(ioread32(INTCSR_reg)
  			| INTCSR_CTR4INT_ENABLE | INTCSR_PCIINT_ENABLE,
  			INTCSR_reg);
  		break;
  
  	case ALL:
  	default:
  		iowrite32(ioread32(INTCSR_reg)
  			| INTCSR_ADINT_ENABLE | INTCSR_CTR4INT_ENABLE
  			| INTCSR_PCIINT_ENABLE,
  			INTCSR_reg);
  		break;
  	}
  }
  
  static irqreturn_t mf624_irq_handler(int irq, struct uio_info *info)
  {
  	void __iomem *INTCSR_reg = info->mem[0].internal_addr + INTCSR;
  
  	if ((ioread32(INTCSR_reg) & INTCSR_ADINT_ENABLE)
  	    && (ioread32(INTCSR_reg) & INTCSR_ADINT_STATUS)) {
  		mf624_disable_interrupt(ADC, info);
  		return IRQ_HANDLED;
  	}
  
  	if ((ioread32(INTCSR_reg) & INTCSR_CTR4INT_ENABLE)
  	    && (ioread32(INTCSR_reg) & INTCSR_CTR4INT_STATUS)) {
  		mf624_disable_interrupt(CTR4, info);
  		return IRQ_HANDLED;
  	}
  
  	return IRQ_NONE;
  }
  
  static int mf624_irqcontrol(struct uio_info *info, s32 irq_on)
  {
  	if (irq_on == 0)
  		mf624_disable_interrupt(ALL, info);
  	else if (irq_on == 1)
  		mf624_enable_interrupt(ALL, info);
  
  	return 0;
  }
a15d2ddba   Michal Sojka   uio_mf624: Refact...
114
115
  static int mf624_setup_mem(struct pci_dev *dev, int bar, struct uio_mem *mem, const char *name)
  {
270579d95   Michal Sojka   uio_mf624: Align ...
116
117
  	resource_size_t start = pci_resource_start(dev, bar);
  	resource_size_t len = pci_resource_len(dev, bar);
a15d2ddba   Michal Sojka   uio_mf624: Refact...
118
  	mem->name = name;
270579d95   Michal Sojka   uio_mf624: Align ...
119
120
  	mem->addr = start & PAGE_MASK;
  	mem->offs = start & ~PAGE_MASK;
a15d2ddba   Michal Sojka   uio_mf624: Refact...
121
122
  	if (!mem->addr)
  		return -ENODEV;
270579d95   Michal Sojka   uio_mf624: Align ...
123
  	mem->size = ((start & ~PAGE_MASK) + len + PAGE_SIZE - 1) & PAGE_MASK;
a15d2ddba   Michal Sojka   uio_mf624: Refact...
124
125
126
127
128
129
  	mem->memtype = UIO_MEM_PHYS;
  	mem->internal_addr = pci_ioremap_bar(dev, bar);
  	if (!mem->internal_addr)
  		return -ENODEV;
  	return 0;
  }
06849faab   Rostislav Lisovy   drivers: uio: Add...
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
  static int mf624_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
  {
  	struct uio_info *info;
  
  	info = kzalloc(sizeof(struct uio_info), GFP_KERNEL);
  	if (!info)
  		return -ENOMEM;
  
  	if (pci_enable_device(dev))
  		goto out_free;
  
  	if (pci_request_regions(dev, "mf624"))
  		goto out_disable;
  
  	info->name = "mf624";
  	info->version = "0.0.1";
  
  	/* Note: Datasheet says device uses BAR0, BAR1, BAR2 -- do not trust it */
  
  	/* BAR0 */
a15d2ddba   Michal Sojka   uio_mf624: Refact...
150
151
  	if (mf624_setup_mem(dev, 0, &info->mem[0], "PCI chipset, interrupts, status "
  			    "bits, special functions"))
06849faab   Rostislav Lisovy   drivers: uio: Add...
152
  		goto out_release;
06849faab   Rostislav Lisovy   drivers: uio: Add...
153
  	/* BAR2 */
a15d2ddba   Michal Sojka   uio_mf624: Refact...
154
  	if (mf624_setup_mem(dev, 2, &info->mem[1], "ADC, DAC, DIO"))
06849faab   Rostislav Lisovy   drivers: uio: Add...
155
156
157
  		goto out_unmap0;
  
  	/* BAR4 */
a15d2ddba   Michal Sojka   uio_mf624: Refact...
158
  	if (mf624_setup_mem(dev, 4, &info->mem[2], "Counter/timer chip"))
06849faab   Rostislav Lisovy   drivers: uio: Add...
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
  		goto out_unmap1;
  
  	info->irq = dev->irq;
  	info->irq_flags = IRQF_SHARED;
  	info->handler = mf624_irq_handler;
  
  	info->irqcontrol = mf624_irqcontrol;
  
  	if (uio_register_device(&dev->dev, info))
  		goto out_unmap2;
  
  	pci_set_drvdata(dev, info);
  
  	return 0;
  
  out_unmap2:
  	iounmap(info->mem[2].internal_addr);
  out_unmap1:
  	iounmap(info->mem[1].internal_addr);
  out_unmap0:
  	iounmap(info->mem[0].internal_addr);
  
  out_release:
  	pci_release_regions(dev);
  
  out_disable:
  	pci_disable_device(dev);
  
  out_free:
  	kfree(info);
  	return -ENODEV;
  }
  
  static void mf624_pci_remove(struct pci_dev *dev)
  {
  	struct uio_info *info = pci_get_drvdata(dev);
  
  	mf624_disable_interrupt(ALL, info);
  
  	uio_unregister_device(info);
  	pci_release_regions(dev);
  	pci_disable_device(dev);
06849faab   Rostislav Lisovy   drivers: uio: Add...
201
202
203
204
205
206
207
  
  	iounmap(info->mem[0].internal_addr);
  	iounmap(info->mem[1].internal_addr);
  	iounmap(info->mem[2].internal_addr);
  
  	kfree(info);
  }
0f4054df0   Jingoo Han   uio: uio_mf624: r...
208
  static const struct pci_device_id mf624_pci_id[] = {
06849faab   Rostislav Lisovy   drivers: uio: Add...
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
  	{ PCI_DEVICE(PCI_VENDOR_ID_HUMUSOFT, PCI_DEVICE_ID_MF624) },
  	{ 0, }
  };
  
  static struct pci_driver mf624_pci_driver = {
  	.name = "mf624",
  	.id_table = mf624_pci_id,
  	.probe = mf624_pci_probe,
  	.remove = mf624_pci_remove,
  };
  MODULE_DEVICE_TABLE(pci, mf624_pci_id);
  
  module_pci_driver(mf624_pci_driver);
  MODULE_LICENSE("GPL v2");
  MODULE_AUTHOR("Rostislav Lisovy <lisovy@gmail.com>");