staging: comedi: me4000: use comedi_range_is_bipolar() in ai (*insn_read)
[deliverable/linux.git] / drivers / staging / comedi / drivers / me4000.c
CommitLineData
e55c95a3
GG
1/*
2 comedi/drivers/me4000.c
3 Source code for the Meilhaus ME-4000 board family.
4
5 COMEDI - Linux Control and Measurement Device Interface
6 Copyright (C) 2000 David A. Schleef <ds@schleef.org>
7
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
12
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
e55c95a3
GG
17 */
18/*
19Driver: me4000
20Description: Meilhaus ME-4000 series boards
21Devices: [Meilhaus] ME-4650 (me4000), ME-4670i, ME-4680, ME-4680i, ME-4680is
22Author: gg (Guenter Gebhardt <g.gebhardt@meilhaus.com>)
23Updated: Mon, 18 Mar 2002 15:34:01 -0800
24Status: broken (no support for loading firmware)
25
26Supports:
27
28 - Analog Input
29 - Analog Output
30 - Digital I/O
31 - Counter
32
5f8f8d43 33Configuration Options: not applicable, uses PCI auto config
e55c95a3
GG
34
35The firmware required by these boards is available in the
36comedi_nonfree_firmware tarball available from
37http://www.comedi.org. However, the driver's support for
38loading the firmware through comedi_config is currently
39broken.
40
41 */
42
ce157f80 43#include <linux/module.h>
e55c95a3 44#include <linux/delay.h>
33782dd5 45#include <linux/interrupt.h>
e55c95a3 46
f2e8e285 47#include "../comedi_pci.h"
33782dd5 48
d92d39d9 49#include "comedi_8254.h"
58af6b92 50#include "plx9052.h"
81dd1811 51
ac584af5 52#define ME4000_FIRMWARE "me4000_firmware.bin"
e55c95a3 53
81dd1811
HS
54/*
55 * ME4000 Register map and bit defines
56 */
57#define ME4000_AO_CHAN(x) ((x) * 0x18)
58
59#define ME4000_AO_CTRL_REG(x) (0x00 + ME4000_AO_CHAN(x))
60#define ME4000_AO_CTRL_BIT_MODE_0 (1 << 0)
61#define ME4000_AO_CTRL_BIT_MODE_1 (1 << 1)
62#define ME4000_AO_CTRL_MASK_MODE (3 << 0)
63#define ME4000_AO_CTRL_BIT_STOP (1 << 2)
64#define ME4000_AO_CTRL_BIT_ENABLE_FIFO (1 << 3)
65#define ME4000_AO_CTRL_BIT_ENABLE_EX_TRIG (1 << 4)
66#define ME4000_AO_CTRL_BIT_EX_TRIG_EDGE (1 << 5)
67#define ME4000_AO_CTRL_BIT_IMMEDIATE_STOP (1 << 7)
68#define ME4000_AO_CTRL_BIT_ENABLE_DO (1 << 8)
69#define ME4000_AO_CTRL_BIT_ENABLE_IRQ (1 << 9)
70#define ME4000_AO_CTRL_BIT_RESET_IRQ (1 << 10)
71#define ME4000_AO_STATUS_REG(x) (0x04 + ME4000_AO_CHAN(x))
72#define ME4000_AO_STATUS_BIT_FSM (1 << 0)
73#define ME4000_AO_STATUS_BIT_FF (1 << 1)
74#define ME4000_AO_STATUS_BIT_HF (1 << 2)
75#define ME4000_AO_STATUS_BIT_EF (1 << 3)
76#define ME4000_AO_FIFO_REG(x) (0x08 + ME4000_AO_CHAN(x))
77#define ME4000_AO_SINGLE_REG(x) (0x0c + ME4000_AO_CHAN(x))
78#define ME4000_AO_TIMER_REG(x) (0x10 + ME4000_AO_CHAN(x))
79#define ME4000_AI_CTRL_REG 0x74
80#define ME4000_AI_STATUS_REG 0x74
81#define ME4000_AI_CTRL_BIT_MODE_0 (1 << 0)
82#define ME4000_AI_CTRL_BIT_MODE_1 (1 << 1)
83#define ME4000_AI_CTRL_BIT_MODE_2 (1 << 2)
84#define ME4000_AI_CTRL_BIT_SAMPLE_HOLD (1 << 3)
85#define ME4000_AI_CTRL_BIT_IMMEDIATE_STOP (1 << 4)
86#define ME4000_AI_CTRL_BIT_STOP (1 << 5)
87#define ME4000_AI_CTRL_BIT_CHANNEL_FIFO (1 << 6)
88#define ME4000_AI_CTRL_BIT_DATA_FIFO (1 << 7)
89#define ME4000_AI_CTRL_BIT_FULLSCALE (1 << 8)
90#define ME4000_AI_CTRL_BIT_OFFSET (1 << 9)
91#define ME4000_AI_CTRL_BIT_EX_TRIG_ANALOG (1 << 10)
92#define ME4000_AI_CTRL_BIT_EX_TRIG (1 << 11)
93#define ME4000_AI_CTRL_BIT_EX_TRIG_FALLING (1 << 12)
94#define ME4000_AI_CTRL_BIT_EX_IRQ (1 << 13)
95#define ME4000_AI_CTRL_BIT_EX_IRQ_RESET (1 << 14)
96#define ME4000_AI_CTRL_BIT_LE_IRQ (1 << 15)
97#define ME4000_AI_CTRL_BIT_LE_IRQ_RESET (1 << 16)
98#define ME4000_AI_CTRL_BIT_HF_IRQ (1 << 17)
99#define ME4000_AI_CTRL_BIT_HF_IRQ_RESET (1 << 18)
100#define ME4000_AI_CTRL_BIT_SC_IRQ (1 << 19)
101#define ME4000_AI_CTRL_BIT_SC_IRQ_RESET (1 << 20)
102#define ME4000_AI_CTRL_BIT_SC_RELOAD (1 << 21)
103#define ME4000_AI_STATUS_BIT_EF_CHANNEL (1 << 22)
104#define ME4000_AI_STATUS_BIT_HF_CHANNEL (1 << 23)
105#define ME4000_AI_STATUS_BIT_FF_CHANNEL (1 << 24)
106#define ME4000_AI_STATUS_BIT_EF_DATA (1 << 25)
107#define ME4000_AI_STATUS_BIT_HF_DATA (1 << 26)
108#define ME4000_AI_STATUS_BIT_FF_DATA (1 << 27)
109#define ME4000_AI_STATUS_BIT_LE (1 << 28)
110#define ME4000_AI_STATUS_BIT_FSM (1 << 29)
111#define ME4000_AI_CTRL_BIT_EX_TRIG_BOTH (1 << 31)
112#define ME4000_AI_CHANNEL_LIST_REG 0x78
a0861f87 113#define ME4000_AI_LIST_INPUT_DIFFERENTIAL BIT(5)
245bd462 114#define ME4000_AI_LIST_RANGE(x) ((3 - ((x) & 3)) << 6)
a0861f87 115#define ME4000_AI_LIST_LAST_ENTRY BIT(8)
81dd1811
HS
116#define ME4000_AI_DATA_REG 0x7c
117#define ME4000_AI_CHAN_TIMER_REG 0x80
118#define ME4000_AI_CHAN_PRE_TIMER_REG 0x84
119#define ME4000_AI_SCAN_TIMER_LOW_REG 0x88
120#define ME4000_AI_SCAN_TIMER_HIGH_REG 0x8c
121#define ME4000_AI_SCAN_PRE_TIMER_LOW_REG 0x90
122#define ME4000_AI_SCAN_PRE_TIMER_HIGH_REG 0x94
123#define ME4000_AI_START_REG 0x98
124#define ME4000_IRQ_STATUS_REG 0x9c
125#define ME4000_IRQ_STATUS_BIT_EX (1 << 0)
126#define ME4000_IRQ_STATUS_BIT_LE (1 << 1)
127#define ME4000_IRQ_STATUS_BIT_AI_HF (1 << 2)
128#define ME4000_IRQ_STATUS_BIT_AO_0_HF (1 << 3)
129#define ME4000_IRQ_STATUS_BIT_AO_1_HF (1 << 4)
130#define ME4000_IRQ_STATUS_BIT_AO_2_HF (1 << 5)
131#define ME4000_IRQ_STATUS_BIT_AO_3_HF (1 << 6)
132#define ME4000_IRQ_STATUS_BIT_SC (1 << 7)
133#define ME4000_DIO_PORT_0_REG 0xa0
134#define ME4000_DIO_PORT_1_REG 0xa4
135#define ME4000_DIO_PORT_2_REG 0xa8
136#define ME4000_DIO_PORT_3_REG 0xac
137#define ME4000_DIO_DIR_REG 0xb0
138#define ME4000_AO_LOADSETREG_XX 0xb4
139#define ME4000_DIO_CTRL_REG 0xb8
140#define ME4000_DIO_CTRL_BIT_MODE_0 (1 << 0)
141#define ME4000_DIO_CTRL_BIT_MODE_1 (1 << 1)
142#define ME4000_DIO_CTRL_BIT_MODE_2 (1 << 2)
143#define ME4000_DIO_CTRL_BIT_MODE_3 (1 << 3)
144#define ME4000_DIO_CTRL_BIT_MODE_4 (1 << 4)
145#define ME4000_DIO_CTRL_BIT_MODE_5 (1 << 5)
146#define ME4000_DIO_CTRL_BIT_MODE_6 (1 << 6)
147#define ME4000_DIO_CTRL_BIT_MODE_7 (1 << 7)
148#define ME4000_DIO_CTRL_BIT_FUNCTION_0 (1 << 8)
149#define ME4000_DIO_CTRL_BIT_FUNCTION_1 (1 << 9)
150#define ME4000_DIO_CTRL_BIT_FIFO_HIGH_0 (1 << 10)
151#define ME4000_DIO_CTRL_BIT_FIFO_HIGH_1 (1 << 11)
152#define ME4000_DIO_CTRL_BIT_FIFO_HIGH_2 (1 << 12)
153#define ME4000_DIO_CTRL_BIT_FIFO_HIGH_3 (1 << 13)
154#define ME4000_AO_DEMUX_ADJUST_REG 0xbc
155#define ME4000_AO_DEMUX_ADJUST_VALUE 0x4c
156#define ME4000_AI_SAMPLE_COUNTER_REG 0xc0
157
81dd1811
HS
158#define ME4000_AI_FIFO_COUNT 2048
159
160#define ME4000_AI_MIN_TICKS 66
161#define ME4000_AI_MIN_SAMPLE_TIME 2000
81dd1811
HS
162
163#define ME4000_AI_CHANNEL_LIST_COUNT 1024
164
3674a87e 165struct me4000_private {
cc6f3336 166 unsigned long plx_regbase;
cc6f3336
HS
167};
168
8c355509
HS
169enum me4000_boardid {
170 BOARD_ME4650,
171 BOARD_ME4660,
172 BOARD_ME4660I,
173 BOARD_ME4660S,
174 BOARD_ME4660IS,
175 BOARD_ME4670,
176 BOARD_ME4670I,
177 BOARD_ME4670S,
178 BOARD_ME4670IS,
179 BOARD_ME4680,
180 BOARD_ME4680I,
181 BOARD_ME4680S,
182 BOARD_ME4680IS,
183};
184
06b60981
HS
185struct me4000_board {
186 const char *name;
06b60981 187 int ai_nchan;
56f71de6 188 unsigned int can_do_diff_ai:1;
e5f66350 189 unsigned int can_do_sh_ai:1; /* sample & hold (8 channels) */
13a463ae 190 unsigned int ex_trig_analog:1;
aed9b663 191 unsigned int has_ao:1;
77714d31 192 unsigned int has_ao_fifo:1;
13a463ae 193 unsigned int has_counter:1;
06b60981
HS
194};
195
27f4caaa 196static const struct me4000_board me4000_boards[] = {
8c355509 197 [BOARD_ME4650] = {
035d432a 198 .name = "ME-4650",
6ba8dfef 199 .ai_nchan = 16,
8c355509
HS
200 },
201 [BOARD_ME4660] = {
035d432a 202 .name = "ME-4660",
6ba8dfef 203 .ai_nchan = 32,
56f71de6 204 .can_do_diff_ai = 1,
eedf4299 205 .has_counter = 1,
8c355509
HS
206 },
207 [BOARD_ME4660I] = {
035d432a 208 .name = "ME-4660i",
6ba8dfef 209 .ai_nchan = 32,
56f71de6 210 .can_do_diff_ai = 1,
eedf4299 211 .has_counter = 1,
8c355509
HS
212 },
213 [BOARD_ME4660S] = {
035d432a 214 .name = "ME-4660s",
6ba8dfef 215 .ai_nchan = 32,
56f71de6 216 .can_do_diff_ai = 1,
e5f66350 217 .can_do_sh_ai = 1,
eedf4299 218 .has_counter = 1,
8c355509
HS
219 },
220 [BOARD_ME4660IS] = {
035d432a 221 .name = "ME-4660is",
6ba8dfef 222 .ai_nchan = 32,
56f71de6 223 .can_do_diff_ai = 1,
e5f66350 224 .can_do_sh_ai = 1,
eedf4299 225 .has_counter = 1,
8c355509
HS
226 },
227 [BOARD_ME4670] = {
035d432a 228 .name = "ME-4670",
6ba8dfef 229 .ai_nchan = 32,
56f71de6 230 .can_do_diff_ai = 1,
6ba8dfef 231 .ex_trig_analog = 1,
aed9b663 232 .has_ao = 1,
eedf4299 233 .has_counter = 1,
8c355509
HS
234 },
235 [BOARD_ME4670I] = {
035d432a 236 .name = "ME-4670i",
6ba8dfef 237 .ai_nchan = 32,
56f71de6 238 .can_do_diff_ai = 1,
6ba8dfef 239 .ex_trig_analog = 1,
aed9b663 240 .has_ao = 1,
eedf4299 241 .has_counter = 1,
8c355509
HS
242 },
243 [BOARD_ME4670S] = {
035d432a 244 .name = "ME-4670s",
6ba8dfef 245 .ai_nchan = 32,
56f71de6 246 .can_do_diff_ai = 1,
e5f66350 247 .can_do_sh_ai = 1,
6ba8dfef 248 .ex_trig_analog = 1,
aed9b663 249 .has_ao = 1,
eedf4299 250 .has_counter = 1,
8c355509
HS
251 },
252 [BOARD_ME4670IS] = {
035d432a 253 .name = "ME-4670is",
6ba8dfef 254 .ai_nchan = 32,
56f71de6 255 .can_do_diff_ai = 1,
e5f66350 256 .can_do_sh_ai = 1,
6ba8dfef 257 .ex_trig_analog = 1,
aed9b663 258 .has_ao = 1,
eedf4299 259 .has_counter = 1,
8c355509
HS
260 },
261 [BOARD_ME4680] = {
035d432a 262 .name = "ME-4680",
6ba8dfef 263 .ai_nchan = 32,
56f71de6 264 .can_do_diff_ai = 1,
6ba8dfef 265 .ex_trig_analog = 1,
aed9b663 266 .has_ao = 1,
77714d31 267 .has_ao_fifo = 1,
eedf4299 268 .has_counter = 1,
8c355509
HS
269 },
270 [BOARD_ME4680I] = {
035d432a 271 .name = "ME-4680i",
6ba8dfef 272 .ai_nchan = 32,
56f71de6 273 .can_do_diff_ai = 1,
6ba8dfef 274 .ex_trig_analog = 1,
aed9b663 275 .has_ao = 1,
77714d31 276 .has_ao_fifo = 1,
eedf4299 277 .has_counter = 1,
8c355509
HS
278 },
279 [BOARD_ME4680S] = {
035d432a 280 .name = "ME-4680s",
6ba8dfef 281 .ai_nchan = 32,
56f71de6 282 .can_do_diff_ai = 1,
e5f66350 283 .can_do_sh_ai = 1,
6ba8dfef 284 .ex_trig_analog = 1,
aed9b663 285 .has_ao = 1,
77714d31 286 .has_ao_fifo = 1,
eedf4299 287 .has_counter = 1,
8c355509
HS
288 },
289 [BOARD_ME4680IS] = {
035d432a 290 .name = "ME-4680is",
6ba8dfef 291 .ai_nchan = 32,
56f71de6 292 .can_do_diff_ai = 1,
e5f66350 293 .can_do_sh_ai = 1,
6ba8dfef 294 .ex_trig_analog = 1,
aed9b663 295 .has_ao = 1,
77714d31 296 .has_ao_fifo = 1,
eedf4299 297 .has_counter = 1,
035d432a 298 },
e55c95a3
GG
299};
300
245bd462
HS
301/*
302 * NOTE: the ranges here are inverted compared to the values
303 * written to the ME4000_AI_CHANNEL_LIST_REG,
304 *
305 * The ME4000_AI_LIST_RANGE() macro handles the inversion.
306 */
9ced1de6 307static const struct comedi_lrange me4000_ai_range = {
93626a45
HS
308 4, {
309 UNI_RANGE(2.5),
310 UNI_RANGE(10),
311 BIP_RANGE(2.5),
312 BIP_RANGE(10)
313 }
e55c95a3
GG
314};
315
ac584af5
HS
316static int me4000_xilinx_download(struct comedi_device *dev,
317 const u8 *data, size_t size,
318 unsigned long context)
e55c95a3 319{
fe531d12 320 struct pci_dev *pcidev = comedi_to_pci_dev(dev);
3674a87e 321 struct me4000_private *devpriv = dev->private;
fe531d12 322 unsigned long xilinx_iobase = pci_resource_start(pcidev, 5);
ac584af5
HS
323 unsigned int file_length;
324 unsigned int val;
325 unsigned int i;
e55c95a3 326
fe531d12
HS
327 if (!xilinx_iobase)
328 return -ENODEV;
329
e55c95a3
GG
330 /*
331 * Set PLX local interrupt 2 polarity to high.
332 * Interrupt is thrown by init pin of xilinx.
333 */
3674a87e 334 outl(PLX9052_INTCSR_LI2POL, devpriv->plx_regbase + PLX9052_INTCSR);
e55c95a3
GG
335
336 /* Set /CS and /WRITE of the Xilinx */
3674a87e 337 val = inl(devpriv->plx_regbase + PLX9052_CNTRL);
ac584af5 338 val |= PLX9052_CNTRL_UIO2_DATA;
3674a87e 339 outl(val, devpriv->plx_regbase + PLX9052_CNTRL);
e55c95a3
GG
340
341 /* Init Xilinx with CS1 */
fe531d12 342 inb(xilinx_iobase + 0xC8);
e55c95a3
GG
343
344 /* Wait until /INIT pin is set */
345 udelay(20);
3674a87e 346 val = inl(devpriv->plx_regbase + PLX9052_INTCSR);
ac584af5 347 if (!(val & PLX9052_INTCSR_LI2STAT)) {
5da80ee8 348 dev_err(dev->class_dev, "Can't init Xilinx\n");
e55c95a3
GG
349 return -EIO;
350 }
351
352 /* Reset /CS and /WRITE of the Xilinx */
3674a87e 353 val = inl(devpriv->plx_regbase + PLX9052_CNTRL);
ac584af5 354 val &= ~PLX9052_CNTRL_UIO2_DATA;
3674a87e 355 outl(val, devpriv->plx_regbase + PLX9052_CNTRL);
e55c95a3 356
ac584af5
HS
357 /* Download Xilinx firmware */
358 file_length = (((unsigned int)data[0] & 0xff) << 24) +
359 (((unsigned int)data[1] & 0xff) << 16) +
360 (((unsigned int)data[2] & 0xff) << 8) +
361 ((unsigned int)data[3] & 0xff);
362 udelay(10);
e55c95a3 363
ac584af5
HS
364 for (i = 0; i < file_length; i++) {
365 outb(data[16 + i], xilinx_iobase);
366 udelay(10);
367
368 /* Check if BUSY flag is low */
3674a87e 369 val = inl(devpriv->plx_regbase + PLX9052_CNTRL);
ac584af5
HS
370 if (val & PLX9052_CNTRL_UIO1_DATA) {
371 dev_err(dev->class_dev,
372 "Xilinx is still busy (i = %d)\n", i);
373 return -EIO;
e55c95a3
GG
374 }
375 }
376
377 /* If done flag is high download was successful */
3674a87e 378 val = inl(devpriv->plx_regbase + PLX9052_CNTRL);
ac584af5 379 if (!(val & PLX9052_CNTRL_UIO0_DATA)) {
5da80ee8
HS
380 dev_err(dev->class_dev, "DONE flag is not set\n");
381 dev_err(dev->class_dev, "Download not successful\n");
e55c95a3
GG
382 return -EIO;
383 }
384
385 /* Set /CS and /WRITE */
3674a87e 386 val = inl(devpriv->plx_regbase + PLX9052_CNTRL);
ac584af5 387 val |= PLX9052_CNTRL_UIO2_DATA;
3674a87e 388 outl(val, devpriv->plx_regbase + PLX9052_CNTRL);
e55c95a3
GG
389
390 return 0;
391}
392
2f348ecd 393static void me4000_reset(struct comedi_device *dev)
e55c95a3 394{
3674a87e 395 struct me4000_private *devpriv = dev->private;
ac2832f8 396 unsigned int val;
e1d7ccb7 397 int chan;
e55c95a3 398
e55c95a3 399 /* Make a hardware reset */
3674a87e 400 val = inl(devpriv->plx_regbase + PLX9052_CNTRL);
4564cfd0 401 val |= PLX9052_CNTRL_PCI_RESET;
3674a87e 402 outl(val, devpriv->plx_regbase + PLX9052_CNTRL);
4564cfd0 403 val &= ~PLX9052_CNTRL_PCI_RESET;
3674a87e 404 outl(val, devpriv->plx_regbase + PLX9052_CNTRL);
e55c95a3
GG
405
406 /* 0x8000 to the DACs means an output voltage of 0V */
e1d7ccb7
HS
407 for (chan = 0; chan < 4; chan++)
408 outl(0x8000, dev->iobase + ME4000_AO_SINGLE_REG(chan));
e55c95a3
GG
409
410 /* Set both stop bits in the analog input control register */
d6cbe537 411 outl(ME4000_AI_CTRL_BIT_IMMEDIATE_STOP | ME4000_AI_CTRL_BIT_STOP,
6c7d2c8b 412 dev->iobase + ME4000_AI_CTRL_REG);
e55c95a3
GG
413
414 /* Set both stop bits in the analog output control register */
e1d7ccb7
HS
415 val = ME4000_AO_CTRL_BIT_IMMEDIATE_STOP | ME4000_AO_CTRL_BIT_STOP;
416 for (chan = 0; chan < 4; chan++)
417 outl(val, dev->iobase + ME4000_AO_CTRL_REG(chan));
e55c95a3
GG
418
419 /* Enable interrupts on the PLX */
58af6b92
HS
420 outl(PLX9052_INTCSR_LI1ENAB |
421 PLX9052_INTCSR_LI1POL |
3674a87e 422 PLX9052_INTCSR_PCIENAB, devpriv->plx_regbase + PLX9052_INTCSR);
e55c95a3
GG
423
424 /* Set the adustment register for AO demux */
d6cbe537 425 outl(ME4000_AO_DEMUX_ADJUST_VALUE,
6c7d2c8b 426 dev->iobase + ME4000_AO_DEMUX_ADJUST_REG);
e55c95a3 427
b6241fda
GS
428 /*
429 * Set digital I/O direction for port 0
430 * to output on isolated versions
431 */
362bcbde
HS
432 if (!(inl(dev->iobase + ME4000_DIO_DIR_REG) & 0x1))
433 outl(0x1, dev->iobase + ME4000_DIO_CTRL_REG);
e55c95a3
GG
434}
435
023c129f
HS
436static int me4000_ai_eoc(struct comedi_device *dev,
437 struct comedi_subdevice *s,
438 struct comedi_insn *insn,
439 unsigned long context)
440{
441 unsigned int status;
442
443 status = inl(dev->iobase + ME4000_AI_STATUS_REG);
444 if (status & ME4000_AI_STATUS_BIT_EF_DATA)
445 return 0;
446 return -EBUSY;
447}
e55c95a3 448
71b5f4f1 449static int me4000_ai_insn_read(struct comedi_device *dev,
1a023870
HS
450 struct comedi_subdevice *s,
451 struct comedi_insn *insn,
452 unsigned int *data)
e55c95a3 453{
959717a3 454 unsigned int chan = CR_CHAN(insn->chanspec);
e9784261 455 unsigned int range = CR_RANGE(insn->chanspec);
959717a3 456 unsigned int aref = CR_AREF(insn->chanspec);
e9784261 457 unsigned int entry;
ac2832f8 458 unsigned int tmp;
023c129f 459 int ret;
fb7891e4 460 int i;
e55c95a3 461
e9784261 462 entry = chan | ME4000_AI_LIST_RANGE(range);
271f5aa0 463 if (aref == AREF_DIFF) {
1a023870
HS
464 if (!(s->subdev_flags && SDF_DIFF)) {
465 dev_err(dev->class_dev,
466 "Differential inputs are not available\n");
467 return -EINVAL;
468 }
469
e9784261 470 if (!comedi_range_is_bipolar(s, range)) {
5da80ee8
HS
471 dev_err(dev->class_dev,
472 "Range must be bipolar when aref = diff\n");
e55c95a3
GG
473 return -EINVAL;
474 }
475
1a023870 476 if (chan >= (s->n_chan / 2)) {
5da80ee8
HS
477 dev_err(dev->class_dev,
478 "Analog input is not available\n");
e55c95a3
GG
479 return -EINVAL;
480 }
271f5aa0 481 entry |= ME4000_AI_LIST_INPUT_DIFFERENTIAL;
e55c95a3
GG
482 }
483
484 entry |= ME4000_AI_LIST_LAST_ENTRY;
485
486 /* Clear channel list, data fifo and both stop bits */
b08bfa38 487 tmp = inl(dev->iobase + ME4000_AI_CTRL_REG);
e55c95a3 488 tmp &= ~(ME4000_AI_CTRL_BIT_CHANNEL_FIFO |
0a85b6f0
MT
489 ME4000_AI_CTRL_BIT_DATA_FIFO |
490 ME4000_AI_CTRL_BIT_STOP | ME4000_AI_CTRL_BIT_IMMEDIATE_STOP);
b08bfa38 491 outl(tmp, dev->iobase + ME4000_AI_CTRL_REG);
e55c95a3
GG
492
493 /* Set the acquisition mode to single */
494 tmp &= ~(ME4000_AI_CTRL_BIT_MODE_0 | ME4000_AI_CTRL_BIT_MODE_1 |
0a85b6f0 495 ME4000_AI_CTRL_BIT_MODE_2);
b08bfa38 496 outl(tmp, dev->iobase + ME4000_AI_CTRL_REG);
e55c95a3
GG
497
498 /* Enable channel list and data fifo */
499 tmp |= ME4000_AI_CTRL_BIT_CHANNEL_FIFO | ME4000_AI_CTRL_BIT_DATA_FIFO;
b08bfa38 500 outl(tmp, dev->iobase + ME4000_AI_CTRL_REG);
e55c95a3
GG
501
502 /* Generate channel list entry */
b08bfa38 503 outl(entry, dev->iobase + ME4000_AI_CHANNEL_LIST_REG);
e55c95a3
GG
504
505 /* Set the timer to maximum sample rate */
b08bfa38
HS
506 outl(ME4000_AI_MIN_TICKS, dev->iobase + ME4000_AI_CHAN_TIMER_REG);
507 outl(ME4000_AI_MIN_TICKS, dev->iobase + ME4000_AI_CHAN_PRE_TIMER_REG);
e55c95a3 508
fb7891e4
HS
509 for (i = 0; i < insn->n; i++) {
510 unsigned int val;
e55c95a3 511
fb7891e4
HS
512 /* start conversion by dummy read */
513 inl(dev->iobase + ME4000_AI_START_REG);
e55c95a3 514
fb7891e4
HS
515 ret = comedi_timeout(dev, s, insn, me4000_ai_eoc, 0);
516 if (ret)
517 return ret;
518
519 /* read two's complement value and munge to offset binary */
520 val = inl(dev->iobase + ME4000_AI_DATA_REG);
521 data[i] = comedi_offset_munge(s, val);
522 }
e55c95a3 523
fb7891e4 524 return insn->n;
e55c95a3
GG
525}
526
0a85b6f0
MT
527static int me4000_ai_cancel(struct comedi_device *dev,
528 struct comedi_subdevice *s)
e55c95a3 529{
ac2832f8 530 unsigned int tmp;
e55c95a3 531
e55c95a3 532 /* Stop any running conversion */
b08bfa38 533 tmp = inl(dev->iobase + ME4000_AI_CTRL_REG);
e55c95a3 534 tmp &= ~(ME4000_AI_CTRL_BIT_STOP | ME4000_AI_CTRL_BIT_IMMEDIATE_STOP);
b08bfa38 535 outl(tmp, dev->iobase + ME4000_AI_CTRL_REG);
e55c95a3
GG
536
537 /* Clear the control register */
b08bfa38 538 outl(0x0, dev->iobase + ME4000_AI_CTRL_REG);
e55c95a3
GG
539
540 return 0;
541}
542
926e5073
HS
543static int me4000_ai_check_chanlist(struct comedi_device *dev,
544 struct comedi_subdevice *s,
545 struct comedi_cmd *cmd)
e55c95a3 546{
926e5073 547 unsigned int aref0 = CR_AREF(cmd->chanlist[0]);
e55c95a3
GG
548 int i;
549
e55c95a3 550 for (i = 0; i < cmd->chanlist_len; i++) {
926e5073
HS
551 unsigned int chan = CR_CHAN(cmd->chanlist[i]);
552 unsigned int range = CR_RANGE(cmd->chanlist[i]);
553 unsigned int aref = CR_AREF(cmd->chanlist[i]);
554
555 if (aref != aref0) {
556 dev_dbg(dev->class_dev,
5da80ee8 557 "Mode is not equal for all entries\n");
e55c95a3
GG
558 return -EINVAL;
559 }
e55c95a3 560
a7dab198 561 if (aref == AREF_DIFF) {
4ec85dad
HS
562 if (!(s->subdev_flags && SDF_DIFF)) {
563 dev_err(dev->class_dev,
564 "Differential inputs are not available\n");
565 return -EINVAL;
566 }
567
568 if (chan >= (s->n_chan / 2)) {
926e5073 569 dev_dbg(dev->class_dev,
5da80ee8 570 "Channel number to high\n");
e55c95a3
GG
571 return -EINVAL;
572 }
e55c95a3 573
926e5073
HS
574 if (!comedi_range_is_bipolar(s, range)) {
575 dev_dbg(dev->class_dev,
6c7d2c8b 576 "Bipolar is not selected in differential mode\n");
e55c95a3
GG
577 return -EINVAL;
578 }
579 }
580 }
581
582 return 0;
583}
584
71b5f4f1 585static int ai_round_cmd_args(struct comedi_device *dev,
0a85b6f0
MT
586 struct comedi_subdevice *s,
587 struct comedi_cmd *cmd,
588 unsigned int *init_ticks,
589 unsigned int *scan_ticks, unsigned int *chan_ticks)
e55c95a3 590{
e55c95a3
GG
591 int rest;
592
e55c95a3
GG
593 *init_ticks = 0;
594 *scan_ticks = 0;
595 *chan_ticks = 0;
596
e55c95a3
GG
597 if (cmd->start_arg) {
598 *init_ticks = (cmd->start_arg * 33) / 1000;
599 rest = (cmd->start_arg * 33) % 1000;
600
1e00dedc 601 if ((cmd->flags & CMDF_ROUND_MASK) == CMDF_ROUND_NEAREST) {
82675f35 602 if (rest > 33)
e55c95a3 603 (*init_ticks)++;
1e00dedc 604 } else if ((cmd->flags & CMDF_ROUND_MASK) == CMDF_ROUND_UP) {
e55c95a3
GG
605 if (rest)
606 (*init_ticks)++;
607 }
608 }
609
610 if (cmd->scan_begin_arg) {
611 *scan_ticks = (cmd->scan_begin_arg * 33) / 1000;
612 rest = (cmd->scan_begin_arg * 33) % 1000;
613
1e00dedc 614 if ((cmd->flags & CMDF_ROUND_MASK) == CMDF_ROUND_NEAREST) {
82675f35 615 if (rest > 33)
e55c95a3 616 (*scan_ticks)++;
1e00dedc 617 } else if ((cmd->flags & CMDF_ROUND_MASK) == CMDF_ROUND_UP) {
e55c95a3
GG
618 if (rest)
619 (*scan_ticks)++;
620 }
621 }
622
623 if (cmd->convert_arg) {
624 *chan_ticks = (cmd->convert_arg * 33) / 1000;
625 rest = (cmd->convert_arg * 33) % 1000;
626
1e00dedc 627 if ((cmd->flags & CMDF_ROUND_MASK) == CMDF_ROUND_NEAREST) {
82675f35 628 if (rest > 33)
e55c95a3 629 (*chan_ticks)++;
1e00dedc 630 } else if ((cmd->flags & CMDF_ROUND_MASK) == CMDF_ROUND_UP) {
e55c95a3
GG
631 if (rest)
632 (*chan_ticks)++;
633 }
634 }
635
e55c95a3
GG
636 return 0;
637}
638
71b5f4f1 639static void ai_write_timer(struct comedi_device *dev,
0a85b6f0
MT
640 unsigned int init_ticks,
641 unsigned int scan_ticks, unsigned int chan_ticks)
e55c95a3 642{
b08bfa38
HS
643 outl(init_ticks - 1, dev->iobase + ME4000_AI_SCAN_PRE_TIMER_LOW_REG);
644 outl(0x0, dev->iobase + ME4000_AI_SCAN_PRE_TIMER_HIGH_REG);
e55c95a3
GG
645
646 if (scan_ticks) {
b08bfa38
HS
647 outl(scan_ticks - 1, dev->iobase + ME4000_AI_SCAN_TIMER_LOW_REG);
648 outl(0x0, dev->iobase + ME4000_AI_SCAN_TIMER_HIGH_REG);
e55c95a3
GG
649 }
650
b08bfa38
HS
651 outl(chan_ticks - 1, dev->iobase + ME4000_AI_CHAN_PRE_TIMER_REG);
652 outl(chan_ticks - 1, dev->iobase + ME4000_AI_CHAN_TIMER_REG);
e55c95a3
GG
653}
654
518c5b64
HS
655static int me4000_ai_write_chanlist(struct comedi_device *dev,
656 struct comedi_subdevice *s,
657 struct comedi_cmd *cmd)
4b2f15f1 658{
4b2f15f1
HS
659 int i;
660
661 for (i = 0; i < cmd->chanlist_len; i++) {
518c5b64
HS
662 unsigned int chan = CR_CHAN(cmd->chanlist[i]);
663 unsigned int range = CR_RANGE(cmd->chanlist[i]);
664 unsigned int aref = CR_AREF(cmd->chanlist[i]);
665 unsigned int entry;
4b2f15f1 666
518c5b64 667 entry = chan | ME4000_AI_LIST_RANGE(range);
4b2f15f1 668
8d44945d 669 if (aref == AREF_DIFF)
4b2f15f1 670 entry |= ME4000_AI_LIST_INPUT_DIFFERENTIAL;
4b2f15f1 671
518c5b64
HS
672 if (i == (cmd->chanlist_len - 1))
673 entry |= ME4000_AI_LIST_LAST_ENTRY;
674
4b2f15f1
HS
675 outl(entry, dev->iobase + ME4000_AI_CHANNEL_LIST_REG);
676 }
677
678 return 0;
679}
680
71b5f4f1 681static int ai_prepare(struct comedi_device *dev,
0a85b6f0
MT
682 struct comedi_subdevice *s,
683 struct comedi_cmd *cmd,
684 unsigned int init_ticks,
685 unsigned int scan_ticks, unsigned int chan_ticks)
e55c95a3 686{
ac2832f8 687 unsigned int tmp = 0;
e55c95a3 688
e55c95a3
GG
689 /* Write timer arguments */
690 ai_write_timer(dev, init_ticks, scan_ticks, chan_ticks);
691
692 /* Reset control register */
b08bfa38 693 outl(tmp, dev->iobase + ME4000_AI_CTRL_REG);
e55c95a3
GG
694
695 /* Start sources */
696 if ((cmd->start_src == TRIG_EXT &&
0a85b6f0
MT
697 cmd->scan_begin_src == TRIG_TIMER &&
698 cmd->convert_src == TRIG_TIMER) ||
699 (cmd->start_src == TRIG_EXT &&
700 cmd->scan_begin_src == TRIG_FOLLOW &&
701 cmd->convert_src == TRIG_TIMER)) {
e55c95a3 702 tmp = ME4000_AI_CTRL_BIT_MODE_1 |
0a85b6f0
MT
703 ME4000_AI_CTRL_BIT_CHANNEL_FIFO |
704 ME4000_AI_CTRL_BIT_DATA_FIFO;
e55c95a3 705 } else if (cmd->start_src == TRIG_EXT &&
0a85b6f0
MT
706 cmd->scan_begin_src == TRIG_EXT &&
707 cmd->convert_src == TRIG_TIMER) {
e55c95a3 708 tmp = ME4000_AI_CTRL_BIT_MODE_2 |
0a85b6f0
MT
709 ME4000_AI_CTRL_BIT_CHANNEL_FIFO |
710 ME4000_AI_CTRL_BIT_DATA_FIFO;
e55c95a3 711 } else if (cmd->start_src == TRIG_EXT &&
0a85b6f0
MT
712 cmd->scan_begin_src == TRIG_EXT &&
713 cmd->convert_src == TRIG_EXT) {
e55c95a3 714 tmp = ME4000_AI_CTRL_BIT_MODE_0 |
0a85b6f0
MT
715 ME4000_AI_CTRL_BIT_MODE_1 |
716 ME4000_AI_CTRL_BIT_CHANNEL_FIFO |
717 ME4000_AI_CTRL_BIT_DATA_FIFO;
e55c95a3
GG
718 } else {
719 tmp = ME4000_AI_CTRL_BIT_MODE_0 |
0a85b6f0
MT
720 ME4000_AI_CTRL_BIT_CHANNEL_FIFO |
721 ME4000_AI_CTRL_BIT_DATA_FIFO;
e55c95a3
GG
722 }
723
724 /* Stop triggers */
725 if (cmd->stop_src == TRIG_COUNT) {
d6cbe537 726 outl(cmd->chanlist_len * cmd->stop_arg,
6c7d2c8b 727 dev->iobase + ME4000_AI_SAMPLE_COUNTER_REG);
e55c95a3
GG
728 tmp |= ME4000_AI_CTRL_BIT_HF_IRQ | ME4000_AI_CTRL_BIT_SC_IRQ;
729 } else if (cmd->stop_src == TRIG_NONE &&
0a85b6f0 730 cmd->scan_end_src == TRIG_COUNT) {
d6cbe537 731 outl(cmd->scan_end_arg,
6c7d2c8b 732 dev->iobase + ME4000_AI_SAMPLE_COUNTER_REG);
e55c95a3
GG
733 tmp |= ME4000_AI_CTRL_BIT_HF_IRQ | ME4000_AI_CTRL_BIT_SC_IRQ;
734 } else {
735 tmp |= ME4000_AI_CTRL_BIT_HF_IRQ;
736 }
737
738 /* Write the setup to the control register */
b08bfa38 739 outl(tmp, dev->iobase + ME4000_AI_CTRL_REG);
e55c95a3
GG
740
741 /* Write the channel list */
518c5b64 742 me4000_ai_write_chanlist(dev, s, cmd);
e55c95a3
GG
743
744 return 0;
745}
746
0a85b6f0
MT
747static int me4000_ai_do_cmd(struct comedi_device *dev,
748 struct comedi_subdevice *s)
e55c95a3
GG
749{
750 int err;
751 unsigned int init_ticks = 0;
752 unsigned int scan_ticks = 0;
753 unsigned int chan_ticks = 0;
ea6d0d4c 754 struct comedi_cmd *cmd = &s->async->cmd;
e55c95a3 755
e55c95a3
GG
756 /* Reset the analog input */
757 err = me4000_ai_cancel(dev, s);
758 if (err)
759 return err;
760
761 /* Round the timer arguments */
762 err = ai_round_cmd_args(dev,
0a85b6f0 763 s, cmd, &init_ticks, &scan_ticks, &chan_ticks);
e55c95a3
GG
764 if (err)
765 return err;
766
767 /* Prepare the AI for acquisition */
768 err = ai_prepare(dev, s, cmd, init_ticks, scan_ticks, chan_ticks);
769 if (err)
770 return err;
771
772 /* Start acquistion by dummy read */
b08bfa38 773 inl(dev->iobase + ME4000_AI_START_REG);
e55c95a3
GG
774
775 return 0;
776}
777
71b5f4f1 778static int me4000_ai_do_cmd_test(struct comedi_device *dev,
0a85b6f0
MT
779 struct comedi_subdevice *s,
780 struct comedi_cmd *cmd)
e55c95a3 781{
e55c95a3
GG
782 unsigned int init_ticks;
783 unsigned int chan_ticks;
784 unsigned int scan_ticks;
785 int err = 0;
786
e55c95a3
GG
787 /* Round the timer arguments */
788 ai_round_cmd_args(dev, s, cmd, &init_ticks, &scan_ticks, &chan_ticks);
789
27020ffe
HS
790 /* Step 1 : check if triggers are trivially valid */
791
51ec1db9
IA
792 err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW | TRIG_EXT);
793 err |= comedi_check_trigger_src(&cmd->scan_begin_src,
27020ffe 794 TRIG_FOLLOW | TRIG_TIMER | TRIG_EXT);
51ec1db9
IA
795 err |= comedi_check_trigger_src(&cmd->convert_src,
796 TRIG_TIMER | TRIG_EXT);
797 err |= comedi_check_trigger_src(&cmd->scan_end_src,
27020ffe 798 TRIG_NONE | TRIG_COUNT);
51ec1db9 799 err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_NONE | TRIG_COUNT);
27020ffe 800
82675f35 801 if (err)
e55c95a3 802 return 1;
e55c95a3 803
27020ffe
HS
804 /* Step 2a : make sure trigger sources are unique */
805
51ec1db9
IA
806 err |= comedi_check_trigger_is_unique(cmd->start_src);
807 err |= comedi_check_trigger_is_unique(cmd->scan_begin_src);
808 err |= comedi_check_trigger_is_unique(cmd->convert_src);
809 err |= comedi_check_trigger_is_unique(cmd->scan_end_src);
810 err |= comedi_check_trigger_is_unique(cmd->stop_src);
27020ffe
HS
811
812 /* Step 2b : and mutually compatible */
813
e55c95a3 814 if (cmd->start_src == TRIG_NOW &&
0a85b6f0
MT
815 cmd->scan_begin_src == TRIG_TIMER &&
816 cmd->convert_src == TRIG_TIMER) {
e55c95a3 817 } else if (cmd->start_src == TRIG_NOW &&
0a85b6f0
MT
818 cmd->scan_begin_src == TRIG_FOLLOW &&
819 cmd->convert_src == TRIG_TIMER) {
e55c95a3 820 } else if (cmd->start_src == TRIG_EXT &&
0a85b6f0
MT
821 cmd->scan_begin_src == TRIG_TIMER &&
822 cmd->convert_src == TRIG_TIMER) {
e55c95a3 823 } else if (cmd->start_src == TRIG_EXT &&
0a85b6f0
MT
824 cmd->scan_begin_src == TRIG_FOLLOW &&
825 cmd->convert_src == TRIG_TIMER) {
e55c95a3 826 } else if (cmd->start_src == TRIG_EXT &&
0a85b6f0
MT
827 cmd->scan_begin_src == TRIG_EXT &&
828 cmd->convert_src == TRIG_TIMER) {
e55c95a3 829 } else if (cmd->start_src == TRIG_EXT &&
0a85b6f0
MT
830 cmd->scan_begin_src == TRIG_EXT &&
831 cmd->convert_src == TRIG_EXT) {
e55c95a3 832 } else {
27020ffe 833 err |= -EINVAL;
e55c95a3
GG
834 }
835
82675f35 836 if (err)
e55c95a3 837 return 2;
e55c95a3 838
8c6c5a69
HS
839 /* Step 3: check if arguments are trivially valid */
840
51ec1db9 841 err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
025b9187 842
e55c95a3 843 if (cmd->chanlist_len < 1) {
e55c95a3 844 cmd->chanlist_len = 1;
8c6c5a69 845 err |= -EINVAL;
e55c95a3
GG
846 }
847 if (init_ticks < 66) {
e55c95a3 848 cmd->start_arg = 2000;
8c6c5a69 849 err |= -EINVAL;
e55c95a3
GG
850 }
851 if (scan_ticks && scan_ticks < 67) {
e55c95a3 852 cmd->scan_begin_arg = 2031;
8c6c5a69 853 err |= -EINVAL;
e55c95a3
GG
854 }
855 if (chan_ticks < 66) {
e55c95a3 856 cmd->convert_arg = 2000;
8c6c5a69 857 err |= -EINVAL;
e55c95a3 858 }
82675f35 859
76af50dd 860 if (cmd->stop_src == TRIG_COUNT)
51ec1db9 861 err |= comedi_check_trigger_arg_min(&cmd->stop_arg, 1);
76af50dd 862 else /* TRIG_NONE */
51ec1db9 863 err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
76af50dd 864
82675f35 865 if (err)
e55c95a3 866 return 3;
e55c95a3
GG
867
868 /*
869 * Stage 4. Check for argument conflicts.
870 */
871 if (cmd->start_src == TRIG_NOW &&
0a85b6f0
MT
872 cmd->scan_begin_src == TRIG_TIMER &&
873 cmd->convert_src == TRIG_TIMER) {
e55c95a3
GG
874 /* Check timer arguments */
875 if (init_ticks < ME4000_AI_MIN_TICKS) {
5da80ee8 876 dev_err(dev->class_dev, "Invalid start arg\n");
b6c77757 877 cmd->start_arg = 2000; /* 66 ticks at least */
e55c95a3
GG
878 err++;
879 }
880 if (chan_ticks < ME4000_AI_MIN_TICKS) {
5da80ee8 881 dev_err(dev->class_dev, "Invalid convert arg\n");
b6c77757 882 cmd->convert_arg = 2000; /* 66 ticks at least */
e55c95a3
GG
883 err++;
884 }
885 if (scan_ticks <= cmd->chanlist_len * chan_ticks) {
5da80ee8 886 dev_err(dev->class_dev, "Invalid scan end arg\n");
b6241fda
GS
887
888 /* At least one tick more */
889 cmd->scan_end_arg = 2000 * cmd->chanlist_len + 31;
e55c95a3
GG
890 err++;
891 }
892 } else if (cmd->start_src == TRIG_NOW &&
0a85b6f0
MT
893 cmd->scan_begin_src == TRIG_FOLLOW &&
894 cmd->convert_src == TRIG_TIMER) {
e55c95a3
GG
895 /* Check timer arguments */
896 if (init_ticks < ME4000_AI_MIN_TICKS) {
5da80ee8 897 dev_err(dev->class_dev, "Invalid start arg\n");
b6c77757 898 cmd->start_arg = 2000; /* 66 ticks at least */
e55c95a3
GG
899 err++;
900 }
901 if (chan_ticks < ME4000_AI_MIN_TICKS) {
5da80ee8 902 dev_err(dev->class_dev, "Invalid convert arg\n");
b6c77757 903 cmd->convert_arg = 2000; /* 66 ticks at least */
e55c95a3
GG
904 err++;
905 }
906 } else if (cmd->start_src == TRIG_EXT &&
0a85b6f0
MT
907 cmd->scan_begin_src == TRIG_TIMER &&
908 cmd->convert_src == TRIG_TIMER) {
e55c95a3
GG
909 /* Check timer arguments */
910 if (init_ticks < ME4000_AI_MIN_TICKS) {
5da80ee8 911 dev_err(dev->class_dev, "Invalid start arg\n");
b6c77757 912 cmd->start_arg = 2000; /* 66 ticks at least */
e55c95a3
GG
913 err++;
914 }
915 if (chan_ticks < ME4000_AI_MIN_TICKS) {
5da80ee8 916 dev_err(dev->class_dev, "Invalid convert arg\n");
b6c77757 917 cmd->convert_arg = 2000; /* 66 ticks at least */
e55c95a3
GG
918 err++;
919 }
920 if (scan_ticks <= cmd->chanlist_len * chan_ticks) {
5da80ee8 921 dev_err(dev->class_dev, "Invalid scan end arg\n");
b6241fda
GS
922
923 /* At least one tick more */
924 cmd->scan_end_arg = 2000 * cmd->chanlist_len + 31;
e55c95a3
GG
925 err++;
926 }
927 } else if (cmd->start_src == TRIG_EXT &&
0a85b6f0
MT
928 cmd->scan_begin_src == TRIG_FOLLOW &&
929 cmd->convert_src == TRIG_TIMER) {
e55c95a3
GG
930 /* Check timer arguments */
931 if (init_ticks < ME4000_AI_MIN_TICKS) {
5da80ee8 932 dev_err(dev->class_dev, "Invalid start arg\n");
b6c77757 933 cmd->start_arg = 2000; /* 66 ticks at least */
e55c95a3
GG
934 err++;
935 }
936 if (chan_ticks < ME4000_AI_MIN_TICKS) {
5da80ee8 937 dev_err(dev->class_dev, "Invalid convert arg\n");
b6c77757 938 cmd->convert_arg = 2000; /* 66 ticks at least */
e55c95a3
GG
939 err++;
940 }
941 } else if (cmd->start_src == TRIG_EXT &&
0a85b6f0
MT
942 cmd->scan_begin_src == TRIG_EXT &&
943 cmd->convert_src == TRIG_TIMER) {
e55c95a3
GG
944 /* Check timer arguments */
945 if (init_ticks < ME4000_AI_MIN_TICKS) {
5da80ee8 946 dev_err(dev->class_dev, "Invalid start arg\n");
b6c77757 947 cmd->start_arg = 2000; /* 66 ticks at least */
e55c95a3
GG
948 err++;
949 }
950 if (chan_ticks < ME4000_AI_MIN_TICKS) {
5da80ee8 951 dev_err(dev->class_dev, "Invalid convert arg\n");
b6c77757 952 cmd->convert_arg = 2000; /* 66 ticks at least */
e55c95a3
GG
953 err++;
954 }
955 } else if (cmd->start_src == TRIG_EXT &&
0a85b6f0
MT
956 cmd->scan_begin_src == TRIG_EXT &&
957 cmd->convert_src == TRIG_EXT) {
e55c95a3
GG
958 /* Check timer arguments */
959 if (init_ticks < ME4000_AI_MIN_TICKS) {
5da80ee8 960 dev_err(dev->class_dev, "Invalid start arg\n");
b6c77757 961 cmd->start_arg = 2000; /* 66 ticks at least */
e55c95a3
GG
962 err++;
963 }
964 }
e55c95a3
GG
965 if (cmd->scan_end_src == TRIG_COUNT) {
966 if (cmd->scan_end_arg == 0) {
5da80ee8 967 dev_err(dev->class_dev, "Invalid scan end arg\n");
e55c95a3
GG
968 cmd->scan_end_arg = 1;
969 err++;
970 }
971 }
82675f35
BP
972
973 if (err)
e55c95a3 974 return 4;
e55c95a3 975
926e5073
HS
976 /* Step 5: check channel list if it exists */
977 if (cmd->chanlist && cmd->chanlist_len > 0)
978 err |= me4000_ai_check_chanlist(dev, s, cmd);
979
980 if (err)
e55c95a3
GG
981 return 5;
982
983 return 0;
984}
985
70265d24 986static irqreturn_t me4000_ai_isr(int irq, void *dev_id)
e55c95a3
GG
987{
988 unsigned int tmp;
71b5f4f1 989 struct comedi_device *dev = dev_id;
b3403f2e 990 struct comedi_subdevice *s = dev->read_subdev;
e55c95a3
GG
991 int i;
992 int c = 0;
ac2832f8 993 unsigned int lval;
e55c95a3 994
ef5bbfcb 995 if (!dev->attached)
e55c95a3 996 return IRQ_NONE;
e55c95a3 997
b08bfa38 998 if (inl(dev->iobase + ME4000_IRQ_STATUS_REG) &
0a85b6f0 999 ME4000_IRQ_STATUS_BIT_AI_HF) {
e55c95a3 1000 /* Read status register to find out what happened */
b08bfa38 1001 tmp = inl(dev->iobase + ME4000_AI_CTRL_REG);
e55c95a3
GG
1002
1003 if (!(tmp & ME4000_AI_STATUS_BIT_FF_DATA) &&
0a85b6f0
MT
1004 !(tmp & ME4000_AI_STATUS_BIT_HF_DATA) &&
1005 (tmp & ME4000_AI_STATUS_BIT_EF_DATA)) {
e55c95a3
GG
1006 c = ME4000_AI_FIFO_COUNT;
1007
b6241fda
GS
1008 /*
1009 * FIFO overflow, so stop conversion
1010 * and disable all interrupts
1011 */
e55c95a3
GG
1012 tmp |= ME4000_AI_CTRL_BIT_IMMEDIATE_STOP;
1013 tmp &= ~(ME4000_AI_CTRL_BIT_HF_IRQ |
0a85b6f0 1014 ME4000_AI_CTRL_BIT_SC_IRQ);
b08bfa38 1015 outl(tmp, dev->iobase + ME4000_AI_CTRL_REG);
e55c95a3 1016
3e6cb74f 1017 s->async->events |= COMEDI_CB_ERROR;
e55c95a3 1018
5da80ee8 1019 dev_err(dev->class_dev, "FIFO overflow\n");
e55c95a3 1020 } else if ((tmp & ME4000_AI_STATUS_BIT_FF_DATA)
0a85b6f0
MT
1021 && !(tmp & ME4000_AI_STATUS_BIT_HF_DATA)
1022 && (tmp & ME4000_AI_STATUS_BIT_EF_DATA)) {
e55c95a3
GG
1023 c = ME4000_AI_FIFO_COUNT / 2;
1024 } else {
5da80ee8
HS
1025 dev_err(dev->class_dev,
1026 "Can't determine state of fifo\n");
e55c95a3
GG
1027 c = 0;
1028
b6241fda
GS
1029 /*
1030 * Undefined state, so stop conversion
1031 * and disable all interrupts
1032 */
e55c95a3
GG
1033 tmp |= ME4000_AI_CTRL_BIT_IMMEDIATE_STOP;
1034 tmp &= ~(ME4000_AI_CTRL_BIT_HF_IRQ |
0a85b6f0 1035 ME4000_AI_CTRL_BIT_SC_IRQ);
b08bfa38 1036 outl(tmp, dev->iobase + ME4000_AI_CTRL_REG);
e55c95a3 1037
3e6cb74f 1038 s->async->events |= COMEDI_CB_ERROR;
e55c95a3 1039
5da80ee8 1040 dev_err(dev->class_dev, "Undefined FIFO state\n");
e55c95a3
GG
1041 }
1042
e55c95a3
GG
1043 for (i = 0; i < c; i++) {
1044 /* Read value from data fifo */
b08bfa38 1045 lval = inl(dev->iobase + ME4000_AI_DATA_REG) & 0xFFFF;
e55c95a3
GG
1046 lval ^= 0x8000;
1047
de88924f 1048 if (!comedi_buf_write_samples(s, &lval, 1)) {
b6241fda
GS
1049 /*
1050 * Buffer overflow, so stop conversion
1051 * and disable all interrupts
1052 */
e55c95a3
GG
1053 tmp |= ME4000_AI_CTRL_BIT_IMMEDIATE_STOP;
1054 tmp &= ~(ME4000_AI_CTRL_BIT_HF_IRQ |
0a85b6f0 1055 ME4000_AI_CTRL_BIT_SC_IRQ);
b08bfa38 1056 outl(tmp, dev->iobase + ME4000_AI_CTRL_REG);
e55c95a3
GG
1057 break;
1058 }
1059 }
1060
1061 /* Work is done, so reset the interrupt */
e55c95a3 1062 tmp |= ME4000_AI_CTRL_BIT_HF_IRQ_RESET;
b08bfa38 1063 outl(tmp, dev->iobase + ME4000_AI_CTRL_REG);
e55c95a3 1064 tmp &= ~ME4000_AI_CTRL_BIT_HF_IRQ_RESET;
b08bfa38 1065 outl(tmp, dev->iobase + ME4000_AI_CTRL_REG);
e55c95a3
GG
1066 }
1067
b08bfa38
HS
1068 if (inl(dev->iobase + ME4000_IRQ_STATUS_REG) &
1069 ME4000_IRQ_STATUS_BIT_SC) {
de88924f 1070 s->async->events |= COMEDI_CB_EOA;
e55c95a3 1071
b6241fda
GS
1072 /*
1073 * Acquisition is complete, so stop
1074 * conversion and disable all interrupts
1075 */
b08bfa38 1076 tmp = inl(dev->iobase + ME4000_AI_CTRL_REG);
e55c95a3
GG
1077 tmp |= ME4000_AI_CTRL_BIT_IMMEDIATE_STOP;
1078 tmp &= ~(ME4000_AI_CTRL_BIT_HF_IRQ | ME4000_AI_CTRL_BIT_SC_IRQ);
b08bfa38 1079 outl(tmp, dev->iobase + ME4000_AI_CTRL_REG);
e55c95a3
GG
1080
1081 /* Poll data until fifo empty */
b08bfa38
HS
1082 while (inl(dev->iobase + ME4000_AI_CTRL_REG) &
1083 ME4000_AI_STATUS_BIT_EF_DATA) {
e55c95a3 1084 /* Read value from data fifo */
b08bfa38 1085 lval = inl(dev->iobase + ME4000_AI_DATA_REG) & 0xFFFF;
e55c95a3
GG
1086 lval ^= 0x8000;
1087
de88924f 1088 if (!comedi_buf_write_samples(s, &lval, 1))
e55c95a3 1089 break;
e55c95a3
GG
1090 }
1091
1092 /* Work is done, so reset the interrupt */
e55c95a3 1093 tmp |= ME4000_AI_CTRL_BIT_SC_IRQ_RESET;
b08bfa38 1094 outl(tmp, dev->iobase + ME4000_AI_CTRL_REG);
e55c95a3 1095 tmp &= ~ME4000_AI_CTRL_BIT_SC_IRQ_RESET;
b08bfa38 1096 outl(tmp, dev->iobase + ME4000_AI_CTRL_REG);
e55c95a3
GG
1097 }
1098
3fa1eb64 1099 comedi_handle_events(dev, s);
e55c95a3
GG
1100
1101 return IRQ_HANDLED;
1102}
1103
71b5f4f1 1104static int me4000_ao_insn_write(struct comedi_device *dev,
0a85b6f0 1105 struct comedi_subdevice *s,
97e658d1
HS
1106 struct comedi_insn *insn,
1107 unsigned int *data)
e55c95a3 1108{
959717a3 1109 unsigned int chan = CR_CHAN(insn->chanspec);
ac2832f8 1110 unsigned int tmp;
e55c95a3 1111
e55c95a3 1112 /* Stop any running conversion */
e1d7ccb7 1113 tmp = inl(dev->iobase + ME4000_AO_CTRL_REG(chan));
e55c95a3 1114 tmp |= ME4000_AO_CTRL_BIT_IMMEDIATE_STOP;
e1d7ccb7 1115 outl(tmp, dev->iobase + ME4000_AO_CTRL_REG(chan));
e55c95a3
GG
1116
1117 /* Clear control register and set to single mode */
e1d7ccb7 1118 outl(0x0, dev->iobase + ME4000_AO_CTRL_REG(chan));
e55c95a3
GG
1119
1120 /* Write data value */
e1d7ccb7 1121 outl(data[0], dev->iobase + ME4000_AO_SINGLE_REG(chan));
e55c95a3
GG
1122
1123 /* Store in the mirror */
081b6ee6 1124 s->readback[chan] = data[0];
e55c95a3
GG
1125
1126 return 1;
1127}
1128
71b5f4f1 1129static int me4000_dio_insn_bits(struct comedi_device *dev,
0a85b6f0 1130 struct comedi_subdevice *s,
b523c2b2
HS
1131 struct comedi_insn *insn,
1132 unsigned int *data)
e55c95a3 1133{
b523c2b2 1134 if (comedi_dio_update_state(s, data)) {
d6cbe537 1135 outl((s->state >> 0) & 0xFF,
6c7d2c8b 1136 dev->iobase + ME4000_DIO_PORT_0_REG);
d6cbe537 1137 outl((s->state >> 8) & 0xFF,
6c7d2c8b 1138 dev->iobase + ME4000_DIO_PORT_1_REG);
d6cbe537 1139 outl((s->state >> 16) & 0xFF,
6c7d2c8b 1140 dev->iobase + ME4000_DIO_PORT_2_REG);
d6cbe537 1141 outl((s->state >> 24) & 0xFF,
6c7d2c8b 1142 dev->iobase + ME4000_DIO_PORT_3_REG);
e55c95a3
GG
1143 }
1144
da755d15
HS
1145 data[1] = ((inl(dev->iobase + ME4000_DIO_PORT_0_REG) & 0xFF) << 0) |
1146 ((inl(dev->iobase + ME4000_DIO_PORT_1_REG) & 0xFF) << 8) |
1147 ((inl(dev->iobase + ME4000_DIO_PORT_2_REG) & 0xFF) << 16) |
1148 ((inl(dev->iobase + ME4000_DIO_PORT_3_REG) & 0xFF) << 24);
e55c95a3 1149
a2714e3e 1150 return insn->n;
e55c95a3
GG
1151}
1152
71b5f4f1 1153static int me4000_dio_insn_config(struct comedi_device *dev,
0a85b6f0 1154 struct comedi_subdevice *s,
5dacadcc
HS
1155 struct comedi_insn *insn,
1156 unsigned int *data)
e55c95a3 1157{
5dacadcc
HS
1158 unsigned int chan = CR_CHAN(insn->chanspec);
1159 unsigned int mask;
1160 unsigned int tmp;
1161 int ret;
e55c95a3 1162
5dacadcc
HS
1163 if (chan < 8)
1164 mask = 0x000000ff;
1165 else if (chan < 16)
1166 mask = 0x0000ff00;
1167 else if (chan < 24)
1168 mask = 0x00ff0000;
1169 else
1170 mask = 0xff000000;
e55c95a3 1171
5dacadcc
HS
1172 ret = comedi_dio_insn_config(dev, s, insn, data, mask);
1173 if (ret)
1174 return ret;
e55c95a3 1175
da755d15 1176 tmp = inl(dev->iobase + ME4000_DIO_CTRL_REG);
5dacadcc
HS
1177 tmp &= ~(ME4000_DIO_CTRL_BIT_MODE_0 | ME4000_DIO_CTRL_BIT_MODE_1 |
1178 ME4000_DIO_CTRL_BIT_MODE_2 | ME4000_DIO_CTRL_BIT_MODE_3 |
1179 ME4000_DIO_CTRL_BIT_MODE_4 | ME4000_DIO_CTRL_BIT_MODE_5 |
1180 ME4000_DIO_CTRL_BIT_MODE_6 | ME4000_DIO_CTRL_BIT_MODE_7);
1181 if (s->io_bits & 0x000000ff)
1182 tmp |= ME4000_DIO_CTRL_BIT_MODE_0;
1183 if (s->io_bits & 0x0000ff00)
1184 tmp |= ME4000_DIO_CTRL_BIT_MODE_2;
1185 if (s->io_bits & 0x00ff0000)
1186 tmp |= ME4000_DIO_CTRL_BIT_MODE_4;
1187 if (s->io_bits & 0xff000000)
1188 tmp |= ME4000_DIO_CTRL_BIT_MODE_6;
e55c95a3 1189
5dacadcc
HS
1190 /*
1191 * Check for optoisolated ME-4000 version.
1192 * If one the first port is a fixed output
1193 * port and the second is a fixed input port.
1194 */
1195 if (inl(dev->iobase + ME4000_DIO_DIR_REG)) {
1196 s->io_bits |= 0x000000ff;
1197 s->io_bits &= ~0x0000ff00;
1198 tmp |= ME4000_DIO_CTRL_BIT_MODE_0;
1199 tmp &= ~(ME4000_DIO_CTRL_BIT_MODE_2 |
1200 ME4000_DIO_CTRL_BIT_MODE_3);
e55c95a3
GG
1201 }
1202
da755d15 1203 outl(tmp, dev->iobase + ME4000_DIO_CTRL_REG);
e55c95a3 1204
5dacadcc 1205 return insn->n;
e55c95a3
GG
1206}
1207
a690b7e5 1208static int me4000_auto_attach(struct comedi_device *dev,
8c355509 1209 unsigned long context)
ba5cb4ba 1210{
750af5e5 1211 struct pci_dev *pcidev = comedi_to_pci_dev(dev);
49ef9c85 1212 const struct me4000_board *board = NULL;
3674a87e 1213 struct me4000_private *devpriv;
ba5cb4ba
HS
1214 struct comedi_subdevice *s;
1215 int result;
4b2f15f1 1216
8c355509 1217 if (context < ARRAY_SIZE(me4000_boards))
49ef9c85
HS
1218 board = &me4000_boards[context];
1219 if (!board)
5f8f8d43 1220 return -ENODEV;
49ef9c85
HS
1221 dev->board_ptr = board;
1222 dev->board_name = board->name;
4b2f15f1 1223
3674a87e
HS
1224 devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
1225 if (!devpriv)
c34fa261 1226 return -ENOMEM;
4b2f15f1 1227
818f569f 1228 result = comedi_pci_enable(dev);
ba5cb4ba
HS
1229 if (result)
1230 return result;
1231
3674a87e 1232 devpriv->plx_regbase = pci_resource_start(pcidev, 1);
ba5cb4ba 1233 dev->iobase = pci_resource_start(pcidev, 2);
3674a87e 1234 if (!devpriv->plx_regbase || !dev->iobase)
4b2f15f1
HS
1235 return -ENODEV;
1236
ac584af5
HS
1237 result = comedi_load_firmware(dev, &pcidev->dev, ME4000_FIRMWARE,
1238 me4000_xilinx_download, 0);
1239 if (result < 0)
4b2f15f1
HS
1240 return result;
1241
2f348ecd 1242 me4000_reset(dev);
4b2f15f1 1243
a9b7ff93
HS
1244 if (pcidev->irq > 0) {
1245 result = request_irq(pcidev->irq, me4000_ai_isr, IRQF_SHARED,
6c7d2c8b 1246 dev->board_name, dev);
a9b7ff93
HS
1247 if (result == 0)
1248 dev->irq = pcidev->irq;
1249 }
1250
8b6c5694
HS
1251 result = comedi_alloc_subdevices(dev, 4);
1252 if (result)
1253 return result;
3af09830 1254
14aa4789 1255 /* Analog Input subdevice */
8aaf2717 1256 s = &dev->subdevices[0];
14aa4789 1257 s->type = COMEDI_SUBD_AI;
31bebc03 1258 s->subdev_flags = SDF_READABLE | SDF_COMMON | SDF_GROUND;
56f71de6 1259 if (board->can_do_diff_ai)
31bebc03 1260 s->subdev_flags |= SDF_DIFF;
14aa4789
HS
1261 s->n_chan = board->ai_nchan;
1262 s->maxdata = 0xffff;
1263 s->len_chanlist = ME4000_AI_CHANNEL_LIST_COUNT;
1264 s->range_table = &me4000_ai_range;
1265 s->insn_read = me4000_ai_insn_read;
1266
1267 if (dev->irq) {
1268 dev->read_subdev = s;
1269 s->subdev_flags |= SDF_CMD_READ;
1270 s->cancel = me4000_ai_cancel;
1271 s->do_cmdtest = me4000_ai_do_cmd_test;
1272 s->do_cmd = me4000_ai_do_cmd;
3af09830
HS
1273 }
1274
1275 /*=========================================================================
1276 Analog output subdevice
1277 ========================================================================*/
1278
8aaf2717 1279 s = &dev->subdevices[1];
3af09830 1280
aed9b663 1281 if (board->has_ao) {
3af09830 1282 s->type = COMEDI_SUBD_AO;
ef49d832 1283 s->subdev_flags = SDF_WRITABLE | SDF_COMMON | SDF_GROUND;
aed9b663 1284 s->n_chan = 4;
3af09830 1285 s->maxdata = 0xFFFF; /* 16 bit DAC */
4683f9f8 1286 s->range_table = &range_bipolar10;
3af09830 1287 s->insn_write = me4000_ao_insn_write;
081b6ee6
HS
1288
1289 result = comedi_alloc_subdev_readback(s);
1290 if (result)
1291 return result;
3af09830
HS
1292 } else {
1293 s->type = COMEDI_SUBD_UNUSED;
1294 }
1295
d8553701 1296 /* Digital I/O subdevice */
8aaf2717 1297 s = &dev->subdevices[2];
d8553701
HS
1298 s->type = COMEDI_SUBD_DIO;
1299 s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
1300 s->n_chan = 32;
1301 s->maxdata = 1;
1302 s->range_table = &range_digital;
1303 s->insn_bits = me4000_dio_insn_bits;
1304 s->insn_config = me4000_dio_insn_config;
3af09830
HS
1305
1306 /*
1307 * Check for optoisolated ME-4000 version. If one the first
1308 * port is a fixed output port and the second is a fixed input port.
1309 */
da755d15 1310 if (!inl(dev->iobase + ME4000_DIO_DIR_REG)) {
3af09830 1311 s->io_bits |= 0xFF;
da755d15 1312 outl(ME4000_DIO_CTRL_BIT_MODE_0,
6c7d2c8b 1313 dev->iobase + ME4000_DIO_DIR_REG);
3af09830
HS
1314 }
1315
d92d39d9 1316 /* Counter subdevice (8254) */
8aaf2717 1317 s = &dev->subdevices[3];
49ef9c85 1318 if (board->has_counter) {
d92d39d9
HS
1319 unsigned long timer_base = pci_resource_start(pcidev, 3);
1320
1321 if (!timer_base)
1322 return -ENODEV;
1323
1324 dev->pacer = comedi_8254_init(timer_base, 0, I8254_IO8, 0);
1325 if (!dev->pacer)
1326 return -ENOMEM;
1327
1328 comedi_8254_subdevice_init(s, dev->pacer);
3af09830
HS
1329 } else {
1330 s->type = COMEDI_SUBD_UNUSED;
1331 }
1332
1333 return 0;
1334}
1335
484ecc95 1336static void me4000_detach(struct comedi_device *dev)
3af09830 1337{
7f072f54
HS
1338 if (dev->iobase)
1339 me4000_reset(dev);
aac307f9 1340 comedi_pci_detach(dev);
3af09830
HS
1341}
1342
75e6301b 1343static struct comedi_driver me4000_driver = {
3af09830
HS
1344 .driver_name = "me4000",
1345 .module = THIS_MODULE,
750af5e5 1346 .auto_attach = me4000_auto_attach,
3af09830
HS
1347 .detach = me4000_detach,
1348};
1349
a690b7e5 1350static int me4000_pci_probe(struct pci_dev *dev,
b8f4ac23 1351 const struct pci_device_id *id)
727b286b 1352{
b8f4ac23 1353 return comedi_pci_auto_config(dev, &me4000_driver, id->driver_data);
727b286b
AT
1354}
1355
41e043fc 1356static const struct pci_device_id me4000_pci_table[] = {
8c355509
HS
1357 { PCI_VDEVICE(MEILHAUS, 0x4650), BOARD_ME4650 },
1358 { PCI_VDEVICE(MEILHAUS, 0x4660), BOARD_ME4660 },
1359 { PCI_VDEVICE(MEILHAUS, 0x4661), BOARD_ME4660I },
1360 { PCI_VDEVICE(MEILHAUS, 0x4662), BOARD_ME4660S },
1361 { PCI_VDEVICE(MEILHAUS, 0x4663), BOARD_ME4660IS },
1362 { PCI_VDEVICE(MEILHAUS, 0x4670), BOARD_ME4670 },
1363 { PCI_VDEVICE(MEILHAUS, 0x4671), BOARD_ME4670I },
1364 { PCI_VDEVICE(MEILHAUS, 0x4672), BOARD_ME4670S },
1365 { PCI_VDEVICE(MEILHAUS, 0x4673), BOARD_ME4670IS },
1366 { PCI_VDEVICE(MEILHAUS, 0x4680), BOARD_ME4680 },
1367 { PCI_VDEVICE(MEILHAUS, 0x4681), BOARD_ME4680I },
1368 { PCI_VDEVICE(MEILHAUS, 0x4682), BOARD_ME4680S },
1369 { PCI_VDEVICE(MEILHAUS, 0x4683), BOARD_ME4680IS },
1370 { 0 }
3af09830
HS
1371};
1372MODULE_DEVICE_TABLE(pci, me4000_pci_table);
1373
75e6301b
HS
1374static struct pci_driver me4000_pci_driver = {
1375 .name = "me4000",
3af09830 1376 .id_table = me4000_pci_table,
75e6301b 1377 .probe = me4000_pci_probe,
9901a4d7 1378 .remove = comedi_pci_auto_unconfig,
727b286b 1379};
75e6301b 1380module_comedi_pci_driver(me4000_driver, me4000_pci_driver);
90f703d3
AT
1381
1382MODULE_AUTHOR("Comedi http://www.comedi.org");
1383MODULE_DESCRIPTION("Comedi low-level driver");
1384MODULE_LICENSE("GPL");
ac584af5 1385MODULE_FIRMWARE(ME4000_FIRMWARE);
This page took 1.071996 seconds and 5 git commands to generate.