1
2
3
4
5
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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55#include <linux/module.h>
56#include "../comedidev.h"
57
58#include <linux/delay.h>
59
60
61#define MPC624_MASTER_CONTROL 0
62#define MPC624_GNMUXCH 1
63#define MPC624_ADC 2
64#define MPC624_EE 3
65#define MPC624_LEDS 4
66#define MPC624_DIO 5
67#define MPC624_IRQ_MASK 6
68
69
70#define MPC624_ADBUSY BIT(5)
71#define MPC624_ADSDO BIT(4)
72#define MPC624_ADFO BIT(3)
73#define MPC624_ADCS BIT(2)
74#define MPC624_ADSCK BIT(1)
75#define MPC624_ADSDI BIT(0)
76
77
78#define MPC624_EOC_BIT BIT(31)
79#define MPC624_DMY_BIT BIT(30)
80#define MPC624_SGN_BIT BIT(29)
81
82
83#define MPC624_OSR(x) (((x) & 0x1f) << 27)
84#define MPC624_SPEED_3_52_KHZ MPC624_OSR(0x11)
85#define MPC624_SPEED_1_76_KHZ MPC624_OSR(0x12)
86#define MPC624_SPEED_880_HZ MPC624_OSR(0x13)
87#define MPC624_SPEED_440_HZ MPC624_OSR(0x14)
88#define MPC624_SPEED_220_HZ MPC624_OSR(0x15)
89#define MPC624_SPEED_110_HZ MPC624_OSR(0x16)
90#define MPC624_SPEED_55_HZ MPC624_OSR(0x17)
91#define MPC624_SPEED_27_5_HZ MPC624_OSR(0x18)
92#define MPC624_SPEED_13_75_HZ MPC624_OSR(0x19)
93#define MPC624_SPEED_6_875_HZ MPC624_OSR(0x1f)
94
95struct mpc624_private {
96 unsigned int ai_speed;
97};
98
99
100static const struct comedi_lrange range_mpc624_bipolar1 = {
101 1,
102 {
103
104
105 BIP_RANGE(2.02)
106 }
107};
108
109static const struct comedi_lrange range_mpc624_bipolar10 = {
110 1,
111 {
112
113
114 BIP_RANGE(20.2)
115 }
116};
117
118static unsigned int mpc624_ai_get_sample(struct comedi_device *dev,
119 struct comedi_subdevice *s)
120{
121 struct mpc624_private *devpriv = dev->private;
122 unsigned int data_out = devpriv->ai_speed;
123 unsigned int data_in = 0;
124 unsigned int bit;
125 int i;
126
127
128 udelay(1);
129 for (i = 0; i < 32; i++) {
130
131 outb(0, dev->iobase + MPC624_ADC);
132 udelay(1);
133
134
135 bit = (data_out & BIT(31)) ? MPC624_ADSDI : 0;
136 outb(bit, dev->iobase + MPC624_ADC);
137 udelay(1);
138
139
140 outb(MPC624_ADSCK | bit, dev->iobase + MPC624_ADC);
141 udelay(1);
142
143
144 data_in <<= 1;
145 data_in |= (inb(dev->iobase + MPC624_ADC) & MPC624_ADSDO) >> 4;
146 udelay(1);
147
148 data_out <<= 1;
149 }
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167 if (data_in & MPC624_EOC_BIT)
168 dev_dbg(dev->class_dev, "EOC bit is set!");
169 if (data_in & MPC624_DMY_BIT)
170 dev_dbg(dev->class_dev, "DMY bit is set!");
171
172 if (data_in & MPC624_SGN_BIT) {
173
174
175
176
177
178
179 data_in &= 0x3fffffff;
180 } else {
181
182
183
184
185
186
187 data_in |= MPC624_SGN_BIT;
188 data_in = ~data_in;
189 data_in += 1;
190
191 data_in &= ~(MPC624_EOC_BIT | MPC624_DMY_BIT);
192 data_in = 0x20000000 - data_in;
193 }
194 return data_in;
195}
196
197static int mpc624_ai_eoc(struct comedi_device *dev,
198 struct comedi_subdevice *s,
199 struct comedi_insn *insn,
200 unsigned long context)
201{
202 unsigned char status;
203
204 status = inb(dev->iobase + MPC624_ADC);
205 if ((status & MPC624_ADBUSY) == 0)
206 return 0;
207 return -EBUSY;
208}
209
210static int mpc624_ai_insn_read(struct comedi_device *dev,
211 struct comedi_subdevice *s,
212 struct comedi_insn *insn,
213 unsigned int *data)
214{
215 int ret;
216 int i;
217
218
219
220
221
222 outb(insn->chanspec, dev->iobase + MPC624_GNMUXCH);
223
224 for (i = 0; i < insn->n; i++) {
225
226 outb(MPC624_ADSCK, dev->iobase + MPC624_ADC);
227 udelay(1);
228 outb(MPC624_ADCS | MPC624_ADSCK, dev->iobase + MPC624_ADC);
229 udelay(1);
230 outb(0, dev->iobase + MPC624_ADC);
231 udelay(1);
232
233
234 ret = comedi_timeout(dev, s, insn, mpc624_ai_eoc, 0);
235 if (ret)
236 return ret;
237
238 data[i] = mpc624_ai_get_sample(dev, s);
239 }
240
241 return insn->n;
242}
243
244static int mpc624_attach(struct comedi_device *dev, struct comedi_devconfig *it)
245{
246 struct mpc624_private *devpriv;
247 struct comedi_subdevice *s;
248 int ret;
249
250 ret = comedi_request_region(dev, it->options[0], 0x10);
251 if (ret)
252 return ret;
253
254 devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
255 if (!devpriv)
256 return -ENOMEM;
257
258 switch (it->options[1]) {
259 case 0:
260 devpriv->ai_speed = MPC624_SPEED_3_52_KHZ;
261 break;
262 case 1:
263 devpriv->ai_speed = MPC624_SPEED_1_76_KHZ;
264 break;
265 case 2:
266 devpriv->ai_speed = MPC624_SPEED_880_HZ;
267 break;
268 case 3:
269 devpriv->ai_speed = MPC624_SPEED_440_HZ;
270 break;
271 case 4:
272 devpriv->ai_speed = MPC624_SPEED_220_HZ;
273 break;
274 case 5:
275 devpriv->ai_speed = MPC624_SPEED_110_HZ;
276 break;
277 case 6:
278 devpriv->ai_speed = MPC624_SPEED_55_HZ;
279 break;
280 case 7:
281 devpriv->ai_speed = MPC624_SPEED_27_5_HZ;
282 break;
283 case 8:
284 devpriv->ai_speed = MPC624_SPEED_13_75_HZ;
285 break;
286 case 9:
287 devpriv->ai_speed = MPC624_SPEED_6_875_HZ;
288 break;
289 default:
290 devpriv->ai_speed = MPC624_SPEED_3_52_KHZ;
291 }
292
293 ret = comedi_alloc_subdevices(dev, 1);
294 if (ret)
295 return ret;
296
297
298 s = &dev->subdevices[0];
299 s->type = COMEDI_SUBD_AI;
300 s->subdev_flags = SDF_READABLE | SDF_DIFF;
301 s->n_chan = 4;
302 s->maxdata = 0x3fffffff;
303 s->range_table = (it->options[1] == 0) ? &range_mpc624_bipolar1
304 : &range_mpc624_bipolar10;
305 s->insn_read = mpc624_ai_insn_read;
306
307 return 0;
308}
309
310static struct comedi_driver mpc624_driver = {
311 .driver_name = "mpc624",
312 .module = THIS_MODULE,
313 .attach = mpc624_attach,
314 .detach = comedi_legacy_detach,
315};
316module_comedi_driver(mpc624_driver);
317
318MODULE_AUTHOR("Comedi http://www.comedi.org");
319MODULE_DESCRIPTION("Comedi driver for Micro/sys MPC-624 PC/104 board");
320MODULE_LICENSE("GPL");
321