adv_pci_dio.c revision 90035c0886b256d75bced13b3b3cea5234aff136
1/*
2 * comedi/drivers/adv_pci_dio.c
3 *
4 * Author: Michal Dobes <dobes@tesnet.cz>
5 *
6 *  Hardware driver for Advantech PCI DIO cards.
7*/
8/*
9Driver: adv_pci_dio
10Description: Advantech PCI-1730, PCI-1733, PCI-1734, PCI-1736UP,
11             PCI-1750, PCI-1751, PCI-1752, PCI-1753/E, PCI-1754,
12             PCI-1756, PCI-1762
13Author: Michal Dobes <dobes@tesnet.cz>
14Devices: [Advantech] PCI-1730 (adv_pci_dio), PCI-1733,
15  PCI-1734, PCI-1736UP, PCI-1750,
16  PCI-1751, PCI-1752, PCI-1753,
17  PCI-1753+PCI-1753E, PCI-1754, PCI-1756,
18  PCI-1760, PCI-1762
19Status: untested
20Updated: Mon, 14 Apr 2008 10:43:08 +0100
21
22This driver supports now only insn interface for DI/DO/DIO.
23
24Configuration options:
25  [0] - PCI bus of device (optional)
26  [1] - PCI slot of device (optional)
27          If bus/slot is not specified, the first available PCI
28          device will be used.
29
30*/
31
32#include "../comedidev.h"
33
34#include <linux/delay.h>
35
36#include "comedi_pci.h"
37#include "8255.h"
38
39#undef PCI_DIO_EXTDEBUG		/* if defined, enable extensive debug logging */
40
41#undef DPRINTK
42#ifdef PCI_DIO_EXTDEBUG
43#define DPRINTK(fmt, args...) rt_printk(fmt, ## args)
44#else
45#define DPRINTK(fmt, args...)
46#endif
47
48// hardware types of the cards
49typedef enum {
50	TYPE_PCI1730, TYPE_PCI1733, TYPE_PCI1734, TYPE_PCI1736,
51	TYPE_PCI1750,
52	TYPE_PCI1751,
53	TYPE_PCI1752,
54	TYPE_PCI1753, TYPE_PCI1753E,
55	TYPE_PCI1754, TYPE_PCI1756,
56	TYPE_PCI1760,
57	TYPE_PCI1762
58} hw_cards_id;
59
60// which I/O instructions to use
61typedef enum {
62	IO_8b, IO_16b
63} hw_io_access;
64
65#define MAX_DI_SUBDEVS	2	/* max number of DI subdevices per card */
66#define MAX_DO_SUBDEVS	2	/* max number of DO subdevices per card */
67#define MAX_DIO_SUBDEVG	2	/* max number of DIO subdevices group per card */
68
69#define SIZE_8255	   4	/* 8255 IO space length */
70
71#define PCIDIO_MAINREG	   2	/* main I/O region for all Advantech cards? */
72
73/* Register offset definitions */
74// Advantech PCI-1730/3/4
75#define PCI1730_IDI	   0	/* R:   Isolated digital input  0-15 */
76#define PCI1730_IDO	   0	/* W:   Isolated digital output 0-15 */
77#define PCI1730_DI	   2	/* R:   Digital input  0-15 */
78#define PCI1730_DO	   2	/* W:   Digital output 0-15 */
79#define PCI1733_IDI	   0	/* R:   Isolated digital input  0-31 */
80#define	PCI1730_3_INT_EN	0x08	/* R/W: enable/disable interrupts */
81#define	PCI1730_3_INT_RF	0x0c	/* R/W: set falling/raising edge for interrupts */
82#define	PCI1730_3_INT_CLR	0x10	/* R/W: clear interrupts */
83#define PCI1734_IDO	   0	/* W:   Isolated digital output 0-31 */
84#define PCI173x_BOARDID	   4	/* R:   Board I/D switch for 1730/3/4 */
85
86// Advantech PCI-1736UP
87#define PCI1736_IDI        0    /* R:   Isolated digital input  0-15 */
88#define PCI1736_IDO        0    /* W:   Isolated digital output 0-15 */
89#define PCI1736_3_INT_EN        0x08    /* R/W: enable/disable interrupts */
90#define PCI1736_3_INT_RF        0x0c    /* R/W: set falling/raising edge for interrupts */
91#define PCI1736_3_INT_CLR       0x10    /* R/W: clear interrupts */
92#define PCI1736_BOARDID    4            /* R:   Board I/D switch for 1736UP */
93#define PCI1736_MAINREG    0            /* Normal register (2) doesn't work */
94
95// Advantech PCI-1750
96#define PCI1750_IDI	   0	/* R:   Isolated digital input  0-15 */
97#define PCI1750_IDO	   0	/* W:   Isolated digital output 0-15 */
98#define PCI1750_ICR	  32	/* W:   Interrupt control register */
99#define PCI1750_ISR	  32	/* R:   Interrupt status register */
100
101// Advantech PCI-1751/3/3E
102#define PCI1751_DIO	   0	/* R/W: begin of 8255 registers block */
103#define PCI1751_ICR	  32	/* W:   Interrupt control register */
104#define PCI1751_ISR	  32	/* R:   Interrupt status register */
105#define PCI1753_DIO	   0	/* R/W: begin of 8255 registers block */
106#define PCI1753_ICR0	  16	/* R/W: Interrupt control register group 0 */
107#define PCI1753_ICR1	  17	/* R/W: Interrupt control register group 1 */
108#define PCI1753_ICR2	  18	/* R/W: Interrupt control register group 2 */
109#define PCI1753_ICR3	  19	/* R/W: Interrupt control register group 3 */
110#define PCI1753E_DIO	  32	/* R/W: begin of 8255 registers block */
111#define PCI1753E_ICR0	  48	/* R/W: Interrupt control register group 0 */
112#define PCI1753E_ICR1	  49	/* R/W: Interrupt control register group 1 */
113#define PCI1753E_ICR2	  50	/* R/W: Interrupt control register group 2 */
114#define PCI1753E_ICR3	  51	/* R/W: Interrupt control register group 3 */
115
116// Advantech PCI-1752/4/6
117#define PCI1752_IDO	   0	/* R/W: Digital output  0-31 */
118#define PCI1752_IDO2	   4	/* R/W: Digital output 32-63 */
119#define PCI1754_IDI	   0	/* R:   Digital input   0-31 */
120#define PCI1754_IDI2	   4	/* R:   Digital input  32-64 */
121#define PCI1756_IDI	   0	/* R:   Digital input   0-31 */
122#define PCI1756_IDO	   4	/* R/W: Digital output  0-31 */
123#define PCI1754_6_ICR0	0x08	/* R/W: Interrupt control register group 0 */
124#define PCI1754_6_ICR1	0x0a	/* R/W: Interrupt control register group 1 */
125#define PCI1754_ICR2	0x0c	/* R/W: Interrupt control register group 2 */
126#define PCI1754_ICR3	0x0e	/* R/W: Interrupt control register group 3 */
127#define PCI1752_6_CFC	0x12	/* R/W: set/read channel freeze function */
128#define PCI175x_BOARDID	0x10	/* R:   Board I/D switch for 1752/4/6 */
129
130// Advantech PCI-1762 registers
131#define PCI1762_RO	   0	/* R/W: Relays status/output */
132#define PCI1762_IDI	   2	/* R:   Isolated input status */
133#define PCI1762_BOARDID	   4	/* R:   Board I/D switch */
134#define PCI1762_ICR	   6	/* W:   Interrupt control register */
135#define PCI1762_ISR	   6	/* R:   Interrupt status register */
136
137// Advantech PCI-1760 registers
138#define OMB0		0x0c	/* W:   Mailbox outgoing registers */
139#define OMB1		0x0d
140#define OMB2		0x0e
141#define OMB3		0x0f
142#define IMB0		0x1c	/* R:   Mailbox incoming registers */
143#define IMB1		0x1d
144#define IMB2		0x1e
145#define IMB3		0x1f
146#define INTCSR0		0x38	/* R/W: Interrupt control registers */
147#define INTCSR1		0x39
148#define INTCSR2		0x3a
149#define INTCSR3		0x3b
150
151// PCI-1760 mailbox commands
152#define CMD_ClearIMB2		0x00	/* Clear IMB2 status and return actaul DI status in IMB3 */
153#define CMD_SetRelaysOutput	0x01	/* Set relay output from OMB0 */
154#define CMD_GetRelaysStatus	0x02	/* Get relay status to IMB0 */
155#define CMD_ReadCurrentStatus	0x07	/* Read the current status of the register in OMB0, result in IMB0 */
156#define CMD_ReadFirmwareVersion	0x0e	/* Read the firmware ver., result in IMB1.IMB0 */
157#define CMD_ReadHardwareVersion	0x0f	/* Read the hardware ver., result in IMB1.IMB0 */
158#define CMD_EnableIDIFilters	0x20	/* Enable IDI filters based on bits in OMB0 */
159#define CMD_EnableIDIPatternMatch 0x21	/* Enable IDI pattern match based on bits in OMB0 */
160#define CMD_SetIDIPatternMatch	0x22	/* Enable IDI pattern match based on bits in OMB0 */
161#define CMD_EnableIDICounters	0x28	/* Enable IDI counters based on bits in OMB0 */
162#define CMD_ResetIDICounters	0x29	/* Reset IDI counters based on bits in OMB0 to its reset values */
163#define CMD_OverflowIDICounters	0x2a	/* Enable IDI counters overflow interrupts  based on bits in OMB0 */
164#define CMD_MatchIntIDICounters	0x2b	/* Enable IDI counters match value interrupts  based on bits in OMB0 */
165#define CMD_EdgeIDICounters	0x2c	/* Set IDI up counters count edge (bit=0 - rising, =1 - falling) */
166#define CMD_GetIDICntCurValue	0x2f	/* Read IDI{OMB0} up counter current value */
167#define CMD_SetIDI0CntResetValue 0x40	/* Set IDI0 Counter Reset Value 256*OMB1+OMB0 */
168#define CMD_SetIDI1CntResetValue 0x41	/* Set IDI1 Counter Reset Value 256*OMB1+OMB0 */
169#define CMD_SetIDI2CntResetValue 0x42	/* Set IDI2 Counter Reset Value 256*OMB1+OMB0 */
170#define CMD_SetIDI3CntResetValue 0x43	/* Set IDI3 Counter Reset Value 256*OMB1+OMB0 */
171#define CMD_SetIDI4CntResetValue 0x44	/* Set IDI4 Counter Reset Value 256*OMB1+OMB0 */
172#define CMD_SetIDI5CntResetValue 0x45	/* Set IDI5 Counter Reset Value 256*OMB1+OMB0 */
173#define CMD_SetIDI6CntResetValue 0x46	/* Set IDI6 Counter Reset Value 256*OMB1+OMB0 */
174#define CMD_SetIDI7CntResetValue 0x47	/* Set IDI7 Counter Reset Value 256*OMB1+OMB0 */
175#define CMD_SetIDI0CntMatchValue 0x48	/* Set IDI0 Counter Match Value 256*OMB1+OMB0 */
176#define CMD_SetIDI1CntMatchValue 0x49	/* Set IDI1 Counter Match Value 256*OMB1+OMB0 */
177#define CMD_SetIDI2CntMatchValue 0x4a	/* Set IDI2 Counter Match Value 256*OMB1+OMB0 */
178#define CMD_SetIDI3CntMatchValue 0x4b	/* Set IDI3 Counter Match Value 256*OMB1+OMB0 */
179#define CMD_SetIDI4CntMatchValue 0x4c	/* Set IDI4 Counter Match Value 256*OMB1+OMB0 */
180#define CMD_SetIDI5CntMatchValue 0x4d	/* Set IDI5 Counter Match Value 256*OMB1+OMB0 */
181#define CMD_SetIDI6CntMatchValue 0x4e	/* Set IDI6 Counter Match Value 256*OMB1+OMB0 */
182#define CMD_SetIDI7CntMatchValue 0x4f	/* Set IDI7 Counter Match Value 256*OMB1+OMB0 */
183
184#define OMBCMD_RETRY	0x03	/* 3 times try request before error */
185
186static int pci_dio_attach(struct comedi_device * dev, comedi_devconfig * it);
187static int pci_dio_detach(struct comedi_device * dev);
188
189typedef struct {
190	int chans;		// num of chans
191	int addr;		// PCI address ofset
192	int regs;		// number of registers to read or 8255 subdevices
193	unsigned int specflags;	// addon subdevice flags
194} diosubd_data;
195
196typedef struct {
197	const char *name;	// board name
198	int vendor_id;		// vendor/device PCI ID
199	int device_id;
200	int main_pci_region;	// main I/O PCI region
201	hw_cards_id cardtype;	// {enum hw_cards_id_enum}
202	diosubd_data sdi[MAX_DI_SUBDEVS];	// DI chans
203	diosubd_data sdo[MAX_DO_SUBDEVS];	// DO chans
204	diosubd_data sdio[MAX_DIO_SUBDEVG];	// DIO 8255 chans
205	diosubd_data boardid;	// card supports board ID switch
206	hw_io_access io_access;	// {enum hw_io_access_enum}
207} boardtype;
208
209static DEFINE_PCI_DEVICE_TABLE(pci_dio_pci_table) = {
210	{PCI_VENDOR_ID_ADVANTECH, 0x1730, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
211	{PCI_VENDOR_ID_ADVANTECH, 0x1733, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
212	{PCI_VENDOR_ID_ADVANTECH, 0x1734, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
213	{PCI_VENDOR_ID_ADVANTECH, 0x1736, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
214	{PCI_VENDOR_ID_ADVANTECH, 0x1750, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
215	{PCI_VENDOR_ID_ADVANTECH, 0x1751, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
216	{PCI_VENDOR_ID_ADVANTECH, 0x1752, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
217	{PCI_VENDOR_ID_ADVANTECH, 0x1753, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
218	{PCI_VENDOR_ID_ADVANTECH, 0x1754, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
219	{PCI_VENDOR_ID_ADVANTECH, 0x1756, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
220	{PCI_VENDOR_ID_ADVANTECH, 0x1760, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
221	{PCI_VENDOR_ID_ADVANTECH, 0x1762, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
222	{0}
223};
224
225MODULE_DEVICE_TABLE(pci, pci_dio_pci_table);
226
227static const boardtype boardtypes[] = {
228	{"pci1730", PCI_VENDOR_ID_ADVANTECH, 0x1730, PCIDIO_MAINREG,
229			TYPE_PCI1730,
230			{{16, PCI1730_DI, 2, 0}, {16, PCI1730_IDI, 2, 0}},
231			{{16, PCI1730_DO, 2, 0}, {16, PCI1730_IDO, 2, 0}},
232			{{0, 0, 0, 0}, {0, 0, 0, 0}},
233			{4, PCI173x_BOARDID, 1, SDF_INTERNAL},
234			IO_8b,
235		},
236	{"pci1733", PCI_VENDOR_ID_ADVANTECH, 0x1733, PCIDIO_MAINREG,
237			TYPE_PCI1733,
238			{{0, 0, 0, 0}, {32, PCI1733_IDI, 4, 0}},
239			{{0, 0, 0, 0}, {0, 0, 0, 0}},
240			{{0, 0, 0, 0}, {0, 0, 0, 0}},
241			{4, PCI173x_BOARDID, 1, SDF_INTERNAL},
242		IO_8b},
243	{"pci1734", PCI_VENDOR_ID_ADVANTECH, 0x1734, PCIDIO_MAINREG,
244			TYPE_PCI1734,
245			{{0, 0, 0, 0}, {0, 0, 0, 0}},
246			{{0, 0, 0, 0}, {32, PCI1734_IDO, 4, 0}},
247			{{0, 0, 0, 0}, {0, 0, 0, 0}},
248			{4, PCI173x_BOARDID, 1, SDF_INTERNAL},
249		IO_8b},
250	{"pci1736", PCI_VENDOR_ID_ADVANTECH, 0x1736, PCI1736_MAINREG,
251			TYPE_PCI1736,
252			{{0, 0, 0, 0}, {16, PCI1736_IDI, 2, 0}},
253			{{0, 0, 0, 0}, {16, PCI1736_IDO, 2, 0}},
254			{{ 0, 0, 0, 0}, { 0, 0, 0, 0}},
255			{ 4, PCI1736_BOARDID, 1, SDF_INTERNAL},
256			IO_8b,
257        },
258	{"pci1750", PCI_VENDOR_ID_ADVANTECH, 0x1750, PCIDIO_MAINREG,
259			TYPE_PCI1750,
260			{{0, 0, 0, 0}, {16, PCI1750_IDI, 2, 0}},
261			{{0, 0, 0, 0}, {16, PCI1750_IDO, 2, 0}},
262			{{0, 0, 0, 0}, {0, 0, 0, 0}},
263			{0, 0, 0, 0},
264		IO_8b},
265	{"pci1751", PCI_VENDOR_ID_ADVANTECH, 0x1751, PCIDIO_MAINREG,
266			TYPE_PCI1751,
267			{{0, 0, 0, 0}, {0, 0, 0, 0}},
268			{{0, 0, 0, 0}, {0, 0, 0, 0}},
269			{{48, PCI1751_DIO, 2, 0}, {0, 0, 0, 0}},
270			{0, 0, 0, 0},
271		IO_8b},
272	{"pci1752", PCI_VENDOR_ID_ADVANTECH, 0x1752, PCIDIO_MAINREG,
273			TYPE_PCI1752,
274			{{0, 0, 0, 0}, {0, 0, 0, 0}},
275			{{32, PCI1752_IDO, 2, 0}, {32, PCI1752_IDO2, 2, 0}},
276			{{0, 0, 0, 0}, {0, 0, 0, 0}},
277			{4, PCI175x_BOARDID, 1, SDF_INTERNAL},
278		IO_16b},
279	{"pci1753", PCI_VENDOR_ID_ADVANTECH, 0x1753, PCIDIO_MAINREG,
280			TYPE_PCI1753,
281			{{0, 0, 0, 0}, {0, 0, 0, 0}},
282			{{0, 0, 0, 0}, {0, 0, 0, 0}},
283			{{96, PCI1753_DIO, 4, 0}, {0, 0, 0, 0}},
284			{0, 0, 0, 0},
285		IO_8b},
286	{"pci1753e", PCI_VENDOR_ID_ADVANTECH, 0x1753, PCIDIO_MAINREG,
287			TYPE_PCI1753E,
288			{{0, 0, 0, 0}, {0, 0, 0, 0}},
289			{{0, 0, 0, 0}, {0, 0, 0, 0}},
290			{{96, PCI1753_DIO, 4, 0}, {96, PCI1753E_DIO, 4, 0}},
291			{0, 0, 0, 0},
292		IO_8b},
293	{"pci1754", PCI_VENDOR_ID_ADVANTECH, 0x1754, PCIDIO_MAINREG,
294			TYPE_PCI1754,
295			{{32, PCI1754_IDI, 2, 0}, {32, PCI1754_IDI2, 2, 0}},
296			{{0, 0, 0, 0}, {0, 0, 0, 0}},
297			{{0, 0, 0, 0}, {0, 0, 0, 0}},
298			{4, PCI175x_BOARDID, 1, SDF_INTERNAL},
299		IO_16b},
300	{"pci1756", PCI_VENDOR_ID_ADVANTECH, 0x1756, PCIDIO_MAINREG,
301			TYPE_PCI1756,
302			{{0, 0, 0, 0}, {32, PCI1756_IDI, 2, 0}},
303			{{0, 0, 0, 0}, {32, PCI1756_IDO, 2, 0}},
304			{{0, 0, 0, 0}, {0, 0, 0, 0}},
305			{4, PCI175x_BOARDID, 1, SDF_INTERNAL},
306		IO_16b},
307	{"pci1760", PCI_VENDOR_ID_ADVANTECH, 0x1760, 0,
308			TYPE_PCI1760,
309			{{0, 0, 0, 0}, {0, 0, 0, 0}},	// This card have own setup work
310			{{0, 0, 0, 0}, {0, 0, 0, 0}},
311			{{0, 0, 0, 0}, {0, 0, 0, 0}},
312			{0, 0, 0, 0},
313		IO_8b},
314	{"pci1762", PCI_VENDOR_ID_ADVANTECH, 0x1762, PCIDIO_MAINREG,
315			TYPE_PCI1762,
316			{{0, 0, 0, 0}, {16, PCI1762_IDI, 1, 0}},
317			{{0, 0, 0, 0}, {16, PCI1762_RO, 1, 0}},
318			{{0, 0, 0, 0}, {0, 0, 0, 0}},
319			{4, PCI1762_BOARDID, 1, SDF_INTERNAL},
320		IO_16b}
321};
322
323#define n_boardtypes (sizeof(boardtypes)/sizeof(boardtype))
324
325static struct comedi_driver driver_pci_dio = {
326      driver_name:"adv_pci_dio",
327      module:THIS_MODULE,
328      attach:pci_dio_attach,
329      detach:pci_dio_detach
330};
331typedef struct pci_dio_private_st pci_dio_private;
332struct pci_dio_private_st {
333	pci_dio_private *prev;	// previous private struct
334	pci_dio_private *next;	// next private struct
335	struct pci_dev *pcidev;	// pointer to board's pci_dev
336	char valid;		// card is usable
337	char GlobalIrqEnabled;	// 1= any IRQ source is enabled
338	// PCI-1760 specific data
339	unsigned char IDICntEnable;	// counter's counting enable status
340	unsigned char IDICntOverEnable;	// counter's overflow interrupts enable status
341	unsigned char IDICntMatchEnable;	// counter's match interrupts enable status
342	unsigned char IDICntEdge;	// counter's count edge value (bit=0 - rising, =1 - falling)
343	unsigned short CntResValue[8];	// counters' reset value
344	unsigned short CntMatchValue[8];	// counters' match interrupt value
345	unsigned char IDIFiltersEn;	// IDI's digital filters enable status
346	unsigned char IDIPatMatchEn;	// IDI's pattern match enable status
347	unsigned char IDIPatMatchValue;	// IDI's pattern match value
348	unsigned short IDIFiltrLow[8];	// IDI's filter value low signal
349	unsigned short IDIFiltrHigh[8];	// IDI's filter value high signal
350};
351
352static pci_dio_private *pci_priv = NULL;	/* list of allocated cards */
353
354#define devpriv ((pci_dio_private *)dev->private)
355#define this_board ((const boardtype *)dev->board_ptr)
356
357/*
358==============================================================================
359*/
360static int pci_dio_insn_bits_di_b(struct comedi_device * dev, struct comedi_subdevice * s,
361	struct comedi_insn * insn, unsigned int * data)
362{
363	const diosubd_data *d = (const diosubd_data *)s->private;
364	int i;
365
366	data[1] = 0;
367	for (i = 0; i < d->regs; i++) {
368		data[1] |= inb(dev->iobase + d->addr + i) << (8 * i);
369	}
370
371	return 2;
372}
373
374/*
375==============================================================================
376*/
377static int pci_dio_insn_bits_di_w(struct comedi_device * dev, struct comedi_subdevice * s,
378	struct comedi_insn * insn, unsigned int * data)
379{
380	const diosubd_data *d = (const diosubd_data *)s->private;
381	int i;
382
383	data[1] = 0;
384	for (i = 0; i < d->regs; i++)
385		data[1] |= inw(dev->iobase + d->addr + 2 * i) << (16 * i);
386
387	return 2;
388}
389
390/*
391==============================================================================
392*/
393static int pci_dio_insn_bits_do_b(struct comedi_device * dev, struct comedi_subdevice * s,
394	struct comedi_insn * insn, unsigned int * data)
395{
396	const diosubd_data *d = (const diosubd_data *)s->private;
397	int i;
398
399	if (data[0]) {
400		s->state &= ~data[0];
401		s->state |= (data[0] & data[1]);
402		for (i = 0; i < d->regs; i++)
403			outb((s->state >> (8 * i)) & 0xff,
404				dev->iobase + d->addr + i);
405	}
406	data[1] = s->state;
407
408	return 2;
409}
410
411/*
412==============================================================================
413*/
414static int pci_dio_insn_bits_do_w(struct comedi_device * dev, struct comedi_subdevice * s,
415	struct comedi_insn * insn, unsigned int * data)
416{
417	const diosubd_data *d = (const diosubd_data *)s->private;
418	int i;
419
420	if (data[0]) {
421		s->state &= ~data[0];
422		s->state |= (data[0] & data[1]);
423		for (i = 0; i < d->regs; i++)
424			outw((s->state >> (16 * i)) & 0xffff,
425				dev->iobase + d->addr + 2 * i);
426	}
427	data[1] = s->state;
428
429	return 2;
430}
431
432/*
433==============================================================================
434*/
435static int pci1760_unchecked_mbxrequest(struct comedi_device * dev,
436	unsigned char *omb, unsigned char *imb, int repeats)
437{
438	int cnt, tout, ok = 0;
439
440	for (cnt = 0; cnt < repeats; cnt++) {
441		outb(omb[0], dev->iobase + OMB0);
442		outb(omb[1], dev->iobase + OMB1);
443		outb(omb[2], dev->iobase + OMB2);
444		outb(omb[3], dev->iobase + OMB3);
445		for (tout = 0; tout < 251; tout++) {
446			if ((imb[2] = inb(dev->iobase + IMB2)) == omb[2]) {
447				imb[0] = inb(dev->iobase + IMB0);
448				imb[1] = inb(dev->iobase + IMB1);
449				imb[3] = inb(dev->iobase + IMB3);
450				ok = 1;
451				break;
452			}
453			comedi_udelay(1);
454		}
455		if (ok)
456			return 0;
457	}
458
459	comedi_error(dev, "PCI-1760 mailbox request timeout!");
460	return -ETIME;
461}
462
463static int pci1760_clear_imb2(struct comedi_device * dev)
464{
465	unsigned char omb[4] = { 0x0, 0x0, CMD_ClearIMB2, 0x0 };
466	unsigned char imb[4];
467	/* check if imb2 is already clear */
468	if (inb(dev->iobase + IMB2) == CMD_ClearIMB2)
469		return 0;
470	return pci1760_unchecked_mbxrequest(dev, omb, imb, OMBCMD_RETRY);
471}
472
473static int pci1760_mbxrequest(struct comedi_device * dev,
474	unsigned char *omb, unsigned char *imb)
475{
476	if (omb[2] == CMD_ClearIMB2) {
477		comedi_error(dev,
478			"bug! this function should not be used for CMD_ClearIMB2 command");
479		return -EINVAL;
480	}
481	if (inb(dev->iobase + IMB2) == omb[2]) {
482		int retval;
483		retval = pci1760_clear_imb2(dev);
484		if (retval < 0)
485			return retval;
486	}
487	return pci1760_unchecked_mbxrequest(dev, omb, imb, OMBCMD_RETRY);
488}
489
490/*
491==============================================================================
492*/
493static int pci1760_insn_bits_di(struct comedi_device * dev, struct comedi_subdevice * s,
494	struct comedi_insn * insn, unsigned int * data)
495{
496	data[1] = inb(dev->iobase + IMB3);
497
498	return 2;
499}
500
501/*
502==============================================================================
503*/
504static int pci1760_insn_bits_do(struct comedi_device * dev, struct comedi_subdevice * s,
505	struct comedi_insn * insn, unsigned int * data)
506{
507	int ret;
508	unsigned char omb[4] = {
509		0x00,
510		0x00,
511		CMD_SetRelaysOutput,
512		0x00
513	};
514	unsigned char imb[4];
515
516	if (data[0]) {
517		s->state &= ~data[0];
518		s->state |= (data[0] & data[1]);
519		omb[0] = s->state;
520		if (!(ret = pci1760_mbxrequest(dev, omb, imb)))
521			return ret;
522	}
523	data[1] = s->state;
524
525	return 2;
526}
527
528/*
529==============================================================================
530*/
531static int pci1760_insn_cnt_read(struct comedi_device * dev, struct comedi_subdevice * s,
532	struct comedi_insn * insn, unsigned int * data)
533{
534	int ret, n;
535	unsigned char omb[4] = {
536		CR_CHAN(insn->chanspec) & 0x07,
537		0x00,
538		CMD_GetIDICntCurValue,
539		0x00
540	};
541	unsigned char imb[4];
542
543	for (n = 0; n < insn->n; n++) {
544		if (!(ret = pci1760_mbxrequest(dev, omb, imb)))
545			return ret;
546		data[n] = (imb[1] << 8) + imb[0];
547	}
548
549	return n;
550}
551
552/*
553==============================================================================
554*/
555static int pci1760_insn_cnt_write(struct comedi_device * dev, struct comedi_subdevice * s,
556	struct comedi_insn * insn, unsigned int * data)
557{
558	int ret;
559	unsigned char chan = CR_CHAN(insn->chanspec) & 0x07;
560	unsigned char bitmask = 1 << chan;
561	unsigned char omb[4] = {
562		data[0] & 0xff,
563		(data[0] >> 8) & 0xff,
564		CMD_SetIDI0CntResetValue + chan,
565		0x00
566	};
567	unsigned char imb[4];
568
569	if (devpriv->CntResValue[chan] != (data[0] & 0xffff)) {	// Set reset value if different
570		if (!(ret = pci1760_mbxrequest(dev, omb, imb)))
571			return ret;
572		devpriv->CntResValue[chan] = data[0] & 0xffff;
573	}
574
575	omb[0] = bitmask;	// reset counter to it reset value
576	omb[2] = CMD_ResetIDICounters;
577	if (!(ret = pci1760_mbxrequest(dev, omb, imb)))
578		return ret;
579
580	if (!(bitmask & devpriv->IDICntEnable)) {	// start counter if it don't run
581		omb[0] = bitmask;
582		omb[2] = CMD_EnableIDICounters;
583		if (!(ret = pci1760_mbxrequest(dev, omb, imb)))
584			return ret;
585		devpriv->IDICntEnable |= bitmask;
586	}
587	return 1;
588}
589
590/*
591==============================================================================
592*/
593static int pci1760_reset(struct comedi_device * dev)
594{
595	int i;
596	unsigned char omb[4] = { 0x00, 0x00, 0x00, 0x00 };
597	unsigned char imb[4];
598
599	outb(0, dev->iobase + INTCSR0);	// disable IRQ
600	outb(0, dev->iobase + INTCSR1);
601	outb(0, dev->iobase + INTCSR2);
602	outb(0, dev->iobase + INTCSR3);
603	devpriv->GlobalIrqEnabled = 0;
604
605	omb[0] = 0x00;
606	omb[2] = CMD_SetRelaysOutput;	// reset relay outputs
607	pci1760_mbxrequest(dev, omb, imb);
608
609	omb[0] = 0x00;
610	omb[2] = CMD_EnableIDICounters;	// disable IDI up counters
611	pci1760_mbxrequest(dev, omb, imb);
612	devpriv->IDICntEnable = 0;
613
614	omb[0] = 0x00;
615	omb[2] = CMD_OverflowIDICounters;	// disable counters overflow interrupts
616	pci1760_mbxrequest(dev, omb, imb);
617	devpriv->IDICntOverEnable = 0;
618
619	omb[0] = 0x00;
620	omb[2] = CMD_MatchIntIDICounters;	// disable counters match value interrupts
621	pci1760_mbxrequest(dev, omb, imb);
622	devpriv->IDICntMatchEnable = 0;
623
624	omb[0] = 0x00;
625	omb[1] = 0x80;
626	for (i = 0; i < 8; i++) {	// set IDI up counters match value
627		omb[2] = CMD_SetIDI0CntMatchValue + i;
628		pci1760_mbxrequest(dev, omb, imb);
629		devpriv->CntMatchValue[i] = 0x8000;
630	}
631
632	omb[0] = 0x00;
633	omb[1] = 0x00;
634	for (i = 0; i < 8; i++) {	// set IDI up counters reset value
635		omb[2] = CMD_SetIDI0CntResetValue + i;
636		pci1760_mbxrequest(dev, omb, imb);
637		devpriv->CntResValue[i] = 0x0000;
638	}
639
640	omb[0] = 0xff;
641	omb[2] = CMD_ResetIDICounters;	// reset IDI up counters to reset values
642	pci1760_mbxrequest(dev, omb, imb);
643
644	omb[0] = 0x00;
645	omb[2] = CMD_EdgeIDICounters;	// set IDI up counters count edge
646	pci1760_mbxrequest(dev, omb, imb);
647	devpriv->IDICntEdge = 0x00;
648
649	omb[0] = 0x00;
650	omb[2] = CMD_EnableIDIFilters;	// disable all digital in filters
651	pci1760_mbxrequest(dev, omb, imb);
652	devpriv->IDIFiltersEn = 0x00;
653
654	omb[0] = 0x00;
655	omb[2] = CMD_EnableIDIPatternMatch;	// disable pattern matching
656	pci1760_mbxrequest(dev, omb, imb);
657	devpriv->IDIPatMatchEn = 0x00;
658
659	omb[0] = 0x00;
660	omb[2] = CMD_SetIDIPatternMatch;	// set pattern match value
661	pci1760_mbxrequest(dev, omb, imb);
662	devpriv->IDIPatMatchValue = 0x00;
663
664	return 0;
665}
666
667/*
668==============================================================================
669*/
670static int pci_dio_reset(struct comedi_device * dev)
671{
672	DPRINTK("adv_pci_dio EDBG: BGN: pci171x_reset(...)\n");
673
674	switch (this_board->cardtype) {
675	case TYPE_PCI1730:
676		outb(0, dev->iobase + PCI1730_DO);	// clear outputs
677		outb(0, dev->iobase + PCI1730_DO + 1);
678		outb(0, dev->iobase + PCI1730_IDO);
679		outb(0, dev->iobase + PCI1730_IDO + 1);
680		/* NO break there! */
681	case TYPE_PCI1733:
682		outb(0, dev->iobase + PCI1730_3_INT_EN);	// disable interrupts
683		outb(0x0f, dev->iobase + PCI1730_3_INT_CLR);	// clear interrupts
684		outb(0, dev->iobase + PCI1730_3_INT_RF);	// set rising edge trigger
685		break;
686	case TYPE_PCI1734:
687		outb(0, dev->iobase + PCI1734_IDO);	// clear outputs
688		outb(0, dev->iobase + PCI1734_IDO + 1);
689		outb(0, dev->iobase + PCI1734_IDO + 2);
690		outb(0, dev->iobase + PCI1734_IDO + 3);
691		break;
692
693	case TYPE_PCI1736:
694		outb(0, dev->iobase+PCI1736_IDO);
695		outb(0, dev->iobase+PCI1736_IDO+1);
696		outb(0, dev->iobase+PCI1736_3_INT_EN);  // disable interrupts
697		outb(0x0f, dev->iobase+PCI1736_3_INT_CLR);// clear interrupts
698		outb(0, dev->iobase+PCI1736_3_INT_RF);  // set rising edge trigger
699		break;
700
701	case TYPE_PCI1750:
702	case TYPE_PCI1751:
703		outb(0x88, dev->iobase + PCI1750_ICR);	// disable & clear interrupts
704		break;
705	case TYPE_PCI1752:
706		outw(0, dev->iobase + PCI1752_6_CFC);	// disable channel freeze function
707		outw(0, dev->iobase + PCI1752_IDO);	// clear outputs
708		outw(0, dev->iobase + PCI1752_IDO + 2);
709		outw(0, dev->iobase + PCI1752_IDO2);
710		outw(0, dev->iobase + PCI1752_IDO2 + 2);
711		break;
712	case TYPE_PCI1753E:
713		outb(0x88, dev->iobase + PCI1753E_ICR0);	// disable & clear interrupts
714		outb(0x80, dev->iobase + PCI1753E_ICR1);
715		outb(0x80, dev->iobase + PCI1753E_ICR2);
716		outb(0x80, dev->iobase + PCI1753E_ICR3);
717		/* NO break there! */
718	case TYPE_PCI1753:
719		outb(0x88, dev->iobase + PCI1753_ICR0);	// disable & clear interrupts
720		outb(0x80, dev->iobase + PCI1753_ICR1);
721		outb(0x80, dev->iobase + PCI1753_ICR2);
722		outb(0x80, dev->iobase + PCI1753_ICR3);
723		break;
724	case TYPE_PCI1754:
725		outw(0x08, dev->iobase + PCI1754_6_ICR0);	// disable and clear interrupts
726		outw(0x08, dev->iobase + PCI1754_6_ICR1);
727		outw(0x08, dev->iobase + PCI1754_ICR2);
728		outw(0x08, dev->iobase + PCI1754_ICR3);
729		break;
730	case TYPE_PCI1756:
731		outw(0, dev->iobase + PCI1752_6_CFC);	// disable channel freeze function
732		outw(0x08, dev->iobase + PCI1754_6_ICR0);	// disable and clear interrupts
733		outw(0x08, dev->iobase + PCI1754_6_ICR1);
734		outw(0, dev->iobase + PCI1756_IDO);	// clear outputs
735		outw(0, dev->iobase + PCI1756_IDO + 2);
736		break;
737	case TYPE_PCI1760:
738		pci1760_reset(dev);
739		break;
740	case TYPE_PCI1762:
741		outw(0x0101, dev->iobase + PCI1762_ICR);	// disable & clear interrupts
742		break;
743	}
744
745	DPRINTK("adv_pci_dio EDBG: END: pci171x_reset(...)\n");
746
747	return 0;
748}
749
750/*
751==============================================================================
752*/
753static int pci1760_attach(struct comedi_device * dev, comedi_devconfig * it)
754{
755	struct comedi_subdevice *s;
756	int subdev = 0;
757
758	s = dev->subdevices + subdev;
759	s->type = COMEDI_SUBD_DI;
760	s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_COMMON;
761	s->n_chan = 8;
762	s->maxdata = 1;
763	s->len_chanlist = 8;
764	s->range_table = &range_digital;
765	s->insn_bits = pci1760_insn_bits_di;
766	subdev++;
767
768	s = dev->subdevices + subdev;
769	s->type = COMEDI_SUBD_DO;
770	s->subdev_flags = SDF_WRITABLE | SDF_GROUND | SDF_COMMON;
771	s->n_chan = 8;
772	s->maxdata = 1;
773	s->len_chanlist = 8;
774	s->range_table = &range_digital;
775	s->state = 0;
776	s->insn_bits = pci1760_insn_bits_do;
777	subdev++;
778
779	s = dev->subdevices + subdev;
780	s->type = COMEDI_SUBD_TIMER;
781	s->subdev_flags = SDF_WRITABLE | SDF_LSAMPL;
782	s->n_chan = 2;
783	s->maxdata = 0xffffffff;
784	s->len_chanlist = 2;
785//      s->insn_config=pci1760_insn_pwm_cfg;
786	subdev++;
787
788	s = dev->subdevices + subdev;
789	s->type = COMEDI_SUBD_COUNTER;
790	s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
791	s->n_chan = 8;
792	s->maxdata = 0xffff;
793	s->len_chanlist = 8;
794	s->insn_read = pci1760_insn_cnt_read;
795	s->insn_write = pci1760_insn_cnt_write;
796//      s->insn_config=pci1760_insn_cnt_cfg;
797	subdev++;
798
799	return 0;
800}
801
802/*
803==============================================================================
804*/
805static int pci_dio_add_di(struct comedi_device * dev, struct comedi_subdevice * s,
806	const diosubd_data * d, int subdev)
807{
808	s->type = COMEDI_SUBD_DI;
809	s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_COMMON | d->specflags;
810	if (d->chans > 16)
811		s->subdev_flags |= SDF_LSAMPL;
812	s->n_chan = d->chans;
813	s->maxdata = 1;
814	s->len_chanlist = d->chans;
815	s->range_table = &range_digital;
816	switch (this_board->io_access) {
817	case IO_8b:
818		s->insn_bits = pci_dio_insn_bits_di_b;
819		break;
820	case IO_16b:
821		s->insn_bits = pci_dio_insn_bits_di_w;
822		break;
823	}
824	s->private = (void *)d;
825
826	return 0;
827}
828
829/*
830==============================================================================
831*/
832static int pci_dio_add_do(struct comedi_device * dev, struct comedi_subdevice * s,
833	const diosubd_data * d, int subdev)
834{
835	s->type = COMEDI_SUBD_DO;
836	s->subdev_flags = SDF_WRITABLE | SDF_GROUND | SDF_COMMON;
837	if (d->chans > 16)
838		s->subdev_flags |= SDF_LSAMPL;
839	s->n_chan = d->chans;
840	s->maxdata = 1;
841	s->len_chanlist = d->chans;
842	s->range_table = &range_digital;
843	s->state = 0;
844	switch (this_board->io_access) {
845	case IO_8b:
846		s->insn_bits = pci_dio_insn_bits_do_b;
847		break;
848	case IO_16b:
849		s->insn_bits = pci_dio_insn_bits_do_w;
850		break;
851	}
852	s->private = (void *)d;
853
854	return 0;
855}
856
857/*
858==============================================================================
859*/
860static int CheckAndAllocCard(struct comedi_device * dev, comedi_devconfig * it,
861	struct pci_dev *pcidev)
862{
863	pci_dio_private *pr, *prev;
864
865	for (pr = pci_priv, prev = NULL; pr != NULL; prev = pr, pr = pr->next) {
866		if (pr->pcidev == pcidev) {
867			return 0;	// this card is used, look for another
868		}
869	}
870
871	if (prev) {
872		devpriv->prev = prev;
873		prev->next = devpriv;
874	} else {
875		pci_priv = devpriv;
876	}
877
878	devpriv->pcidev = pcidev;
879
880	return 1;
881}
882
883/*
884==============================================================================
885*/
886static int pci_dio_attach(struct comedi_device * dev, comedi_devconfig * it)
887{
888	struct comedi_subdevice *s;
889	int ret, subdev, n_subdevices, i, j;
890	unsigned long iobase;
891	struct pci_dev *pcidev;
892
893	rt_printk("comedi%d: adv_pci_dio: ", dev->minor);
894
895	if ((ret = alloc_private(dev, sizeof(pci_dio_private))) < 0) {
896		rt_printk(", Error: Cann't allocate private memory!\n");
897		return -ENOMEM;
898	}
899
900	for (pcidev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, NULL);
901		pcidev != NULL;
902		pcidev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, pcidev)) {
903		// loop through cards supported by this driver
904		for (i = 0; i < n_boardtypes; ++i) {
905			if (boardtypes[i].vendor_id != pcidev->vendor)
906				continue;
907			if (boardtypes[i].device_id != pcidev->device)
908				continue;
909			// was a particular bus/slot requested?
910			if (it->options[0] || it->options[1]) {
911				// are we on the wrong bus/slot?
912				if (pcidev->bus->number != it->options[0] ||
913					PCI_SLOT(pcidev->devfn) !=
914					it->options[1]) {
915					continue;
916				}
917			}
918			ret = CheckAndAllocCard(dev, it, pcidev);
919			if (ret != 1) continue;
920			dev->board_ptr = boardtypes + i;
921			break;
922		}
923		if (dev->board_ptr)
924			break;
925	}
926
927	if (!dev->board_ptr) {
928		rt_printk
929			(", Error: Requested type of the card was not found!\n");
930		return -EIO;
931	}
932
933	if (comedi_pci_enable(pcidev, driver_pci_dio.driver_name)) {
934		rt_printk
935			(", Error: Can't enable PCI device and request regions!\n");
936		return -EIO;
937	}
938	iobase = pci_resource_start(pcidev, this_board->main_pci_region);
939	rt_printk(", b:s:f=%d:%d:%d, io=0x%4lx",
940		pcidev->bus->number, PCI_SLOT(pcidev->devfn),
941		PCI_FUNC(pcidev->devfn), iobase);
942
943	dev->iobase = iobase;
944	dev->board_name = this_board->name;
945
946	if (this_board->cardtype == TYPE_PCI1760) {
947		n_subdevices = 4;	// 8 IDI, 8 IDO, 2 PWM, 8 CNT
948	} else {
949		n_subdevices = 0;
950		for (i = 0; i < MAX_DI_SUBDEVS; i++)
951			if (this_board->sdi[i].chans)
952				n_subdevices++;
953		for (i = 0; i < MAX_DO_SUBDEVS; i++)
954			if (this_board->sdo[i].chans)
955				n_subdevices++;
956		for (i = 0; i < MAX_DIO_SUBDEVG; i++)
957			n_subdevices += this_board->sdio[i].regs;
958		if (this_board->boardid.chans)
959			n_subdevices++;
960	}
961
962	if ((ret = alloc_subdevices(dev, n_subdevices)) < 0) {
963		rt_printk(", Error: Cann't allocate subdevice memory!\n");
964		return ret;
965	}
966
967	rt_printk(".\n");
968
969	subdev = 0;
970
971	for (i = 0; i < MAX_DI_SUBDEVS; i++)
972		if (this_board->sdi[i].chans) {
973			s = dev->subdevices + subdev;
974			pci_dio_add_di(dev, s, &this_board->sdi[i], subdev);
975			subdev++;
976		}
977
978	for (i = 0; i < MAX_DO_SUBDEVS; i++)
979		if (this_board->sdo[i].chans) {
980			s = dev->subdevices + subdev;
981			pci_dio_add_do(dev, s, &this_board->sdo[i], subdev);
982			subdev++;
983		}
984
985	for (i = 0; i < MAX_DIO_SUBDEVG; i++)
986		for (j = 0; j < this_board->sdio[i].regs; j++) {
987			s = dev->subdevices + subdev;
988			subdev_8255_init(dev, s, NULL,
989				dev->iobase + this_board->sdio[i].addr +
990				SIZE_8255 * j);
991			subdev++;
992		}
993
994	if (this_board->boardid.chans) {
995		s = dev->subdevices + subdev;
996		s->type = COMEDI_SUBD_DI;
997		pci_dio_add_di(dev, s, &this_board->boardid, subdev);
998		subdev++;
999	}
1000
1001	if (this_board->cardtype == TYPE_PCI1760)
1002		pci1760_attach(dev, it);
1003
1004	devpriv->valid = 1;
1005
1006	pci_dio_reset(dev);
1007
1008	return 0;
1009}
1010
1011/*
1012==============================================================================
1013*/
1014static int pci_dio_detach(struct comedi_device * dev)
1015{
1016	int i, j;
1017	struct comedi_subdevice *s;
1018	int subdev;
1019
1020	if (dev->private) {
1021		if (devpriv->valid) {
1022			pci_dio_reset(dev);
1023		}
1024
1025		/* This shows the silliness of using this kind of
1026		 * scheme for numbering subdevices.  Don't do it.  --ds */
1027		subdev = 0;
1028		for (i = 0; i < MAX_DI_SUBDEVS; i++) {
1029			if (this_board->sdi[i].chans) {
1030				subdev++;
1031			}
1032		}
1033		for (i = 0; i < MAX_DO_SUBDEVS; i++) {
1034			if (this_board->sdo[i].chans) {
1035				subdev++;
1036			}
1037		}
1038		for (i = 0; i < MAX_DIO_SUBDEVG; i++) {
1039			for (j = 0; j < this_board->sdio[i].regs; j++) {
1040				s = dev->subdevices + subdev;
1041				subdev_8255_cleanup(dev, s);
1042				subdev++;
1043			}
1044		}
1045
1046		for (i = 0; i < dev->n_subdevices; i++) {
1047			s = dev->subdevices + i;
1048			s->private = NULL;
1049		}
1050
1051		if (devpriv->pcidev) {
1052			if (dev->iobase) {
1053				comedi_pci_disable(devpriv->pcidev);
1054			}
1055			pci_dev_put(devpriv->pcidev);
1056		}
1057
1058		if (devpriv->prev) {
1059			devpriv->prev->next = devpriv->next;
1060		} else {
1061			pci_priv = devpriv->next;
1062		}
1063		if (devpriv->next) {
1064			devpriv->next->prev = devpriv->prev;
1065		}
1066	}
1067
1068	return 0;
1069}
1070
1071/*
1072==============================================================================
1073*/
1074COMEDI_PCI_INITCLEANUP(driver_pci_dio, pci_dio_pci_table);
1075/*
1076==============================================================================
1077*/
1078