8
8
9
9
struct apci1564_private {
10
10
unsigned int amcc_iobase ; /* base of AMCC I/O registers */
11
+ unsigned int mode1 ; /* riding-edge/high level channels */
12
+ unsigned int mode2 ; /* falling-edge/low level channels */
13
+ unsigned int ctrl ; /* interrupt mode OR (edge) . AND (level) */
11
14
unsigned int do_int_type ;
12
15
unsigned char timer_select_mode ;
13
16
unsigned char mode_select_register ;
@@ -16,6 +19,38 @@ struct apci1564_private {
16
19
17
20
#include "addi-data/hwdrv_apci1564.c"
18
21
22
+ static int apci1564_reset (struct comedi_device * dev )
23
+ {
24
+ struct apci1564_private * devpriv = dev -> private ;
25
+
26
+ devpriv -> do_int_type = 0 ;
27
+
28
+ /* Disable the input interrupts and reset status register */
29
+ outl (0x0 , devpriv -> amcc_iobase + APCI1564_DI_IRQ_REG );
30
+ inl (devpriv -> amcc_iobase + APCI1564_DI_INT_STATUS_REG );
31
+ outl (0x0 , devpriv -> amcc_iobase + APCI1564_DI_INT_MODE1_REG );
32
+ outl (0x0 , devpriv -> amcc_iobase + APCI1564_DI_INT_MODE2_REG );
33
+
34
+ /* Reset the output channels and disable interrupts */
35
+ outl (0x0 , devpriv -> amcc_iobase + APCI1564_DO_REG );
36
+ outl (0x0 , devpriv -> amcc_iobase + APCI1564_DO_INT_CTRL_REG );
37
+
38
+ /* Reset the watchdog registers */
39
+ addi_watchdog_reset (devpriv -> amcc_iobase + APCI1564_WDOG_REG );
40
+
41
+ /* Reset the timer registers */
42
+ outl (0x0 , devpriv -> amcc_iobase + APCI1564_TIMER_CTRL_REG );
43
+ outl (0x0 , devpriv -> amcc_iobase + APCI1564_TIMER_RELOAD_REG );
44
+
45
+ /* Reset the counter registers */
46
+ outl (0x0 , dev -> iobase + APCI1564_TCW_CTRL_REG (APCI1564_COUNTER1 ));
47
+ outl (0x0 , dev -> iobase + APCI1564_TCW_CTRL_REG (APCI1564_COUNTER2 ));
48
+ outl (0x0 , dev -> iobase + APCI1564_TCW_CTRL_REG (APCI1564_COUNTER3 ));
49
+ outl (0x0 , dev -> iobase + APCI1564_TCW_CTRL_REG (APCI1564_COUNTER4 ));
50
+
51
+ return 0 ;
52
+ }
53
+
19
54
static irqreturn_t v_ADDI_Interrupt (int irq , void * d )
20
55
{
21
56
apci1564_interrupt (irq , d );
@@ -51,34 +86,187 @@ static int apci1564_do_insn_bits(struct comedi_device *dev,
51
86
return insn -> n ;
52
87
}
53
88
54
- static int apci1564_reset (struct comedi_device * dev )
89
+ /*
90
+ * Change-Of-State (COS) interrupt configuration
91
+ *
92
+ * Channels 0 to 15 are interruptible. These channels can be configured
93
+ * to generate interrupts based on AND/OR logic for the desired channels.
94
+ *
95
+ * OR logic
96
+ * - reacts to rising or falling edges
97
+ * - interrupt is generated when any enabled channel
98
+ * meet the desired interrupt condition
99
+ *
100
+ * AND logic
101
+ * - reacts to changes in level of the selected inputs
102
+ * - interrupt is generated when all enabled channels
103
+ * meet the desired interrupt condition
104
+ * - after an interrupt, a change in level must occur on
105
+ * the selected inputs to release the IRQ logic
106
+ *
107
+ * The COS interrupt must be configured before it can be enabled.
108
+ *
109
+ * data[0] : INSN_CONFIG_DIGITAL_TRIG
110
+ * data[1] : trigger number (= 0)
111
+ * data[2] : configuration operation:
112
+ * COMEDI_DIGITAL_TRIG_DISABLE = no interrupts
113
+ * COMEDI_DIGITAL_TRIG_ENABLE_EDGES = OR (edge) interrupts
114
+ * COMEDI_DIGITAL_TRIG_ENABLE_LEVELS = AND (level) interrupts
115
+ * data[3] : left-shift for data[4] and data[5]
116
+ * data[4] : rising-edge/high level channels
117
+ * data[5] : falling-edge/low level channels
118
+ */
119
+ static int apci1564_cos_insn_config (struct comedi_device * dev ,
120
+ struct comedi_subdevice * s ,
121
+ struct comedi_insn * insn ,
122
+ unsigned int * data )
55
123
{
56
124
struct apci1564_private * devpriv = dev -> private ;
125
+ unsigned int shift , oldmask ;
126
+
127
+ switch (data [0 ]) {
128
+ case INSN_CONFIG_DIGITAL_TRIG :
129
+ if (data [1 ] != 0 )
130
+ return - EINVAL ;
131
+ shift = data [3 ];
132
+ oldmask = (1U << shift ) - 1 ;
133
+ switch (data [2 ]) {
134
+ case COMEDI_DIGITAL_TRIG_DISABLE :
135
+ devpriv -> ctrl = 0 ;
136
+ devpriv -> mode1 = 0 ;
137
+ devpriv -> mode2 = 0 ;
138
+ apci1564_reset (dev );
139
+ break ;
140
+ case COMEDI_DIGITAL_TRIG_ENABLE_EDGES :
141
+ if (devpriv -> ctrl != (APCI1564_DI_INT_ENABLE |
142
+ APCI1564_DI_INT_OR )) {
143
+ /* switching to 'OR' mode */
144
+ devpriv -> ctrl = APCI1564_DI_INT_ENABLE |
145
+ APCI1564_DI_INT_OR ;
146
+ /* wipe old channels */
147
+ devpriv -> mode1 = 0 ;
148
+ devpriv -> mode2 = 0 ;
149
+ } else {
150
+ /* preserve unspecified channels */
151
+ devpriv -> mode1 &= oldmask ;
152
+ devpriv -> mode2 &= oldmask ;
153
+ }
154
+ /* configure specified channels */
155
+ devpriv -> mode1 |= data [4 ] << shift ;
156
+ devpriv -> mode2 |= data [5 ] << shift ;
157
+ break ;
158
+ case COMEDI_DIGITAL_TRIG_ENABLE_LEVELS :
159
+ if (devpriv -> ctrl != (APCI1564_DI_INT_ENABLE |
160
+ APCI1564_DI_INT_AND )) {
161
+ /* switching to 'AND' mode */
162
+ devpriv -> ctrl = APCI1564_DI_INT_ENABLE |
163
+ APCI1564_DI_INT_AND ;
164
+ /* wipe old channels */
165
+ devpriv -> mode1 = 0 ;
166
+ devpriv -> mode2 = 0 ;
167
+ } else {
168
+ /* preserve unspecified channels */
169
+ devpriv -> mode1 &= oldmask ;
170
+ devpriv -> mode2 &= oldmask ;
171
+ }
172
+ /* configure specified channels */
173
+ devpriv -> mode1 |= data [4 ] << shift ;
174
+ devpriv -> mode2 |= data [5 ] << shift ;
175
+ break ;
176
+ default :
177
+ return - EINVAL ;
178
+ }
179
+ break ;
180
+ default :
181
+ return - EINVAL ;
182
+ }
183
+ return insn -> n ;
184
+ }
57
185
58
- devpriv -> do_int_type = 0 ;
186
+ static int apci1564_cos_insn_bits (struct comedi_device * dev ,
187
+ struct comedi_subdevice * s ,
188
+ struct comedi_insn * insn ,
189
+ unsigned int * data )
190
+ {
191
+ data [1 ] = s -> state ;
59
192
60
- /* Disable the input interrupts and reset status register */
61
- outl (0x0 , devpriv -> amcc_iobase + APCI1564_DI_IRQ_REG );
62
- inl (devpriv -> amcc_iobase + APCI1564_DI_INT_STATUS_REG );
63
- outl (0x0 , devpriv -> amcc_iobase + APCI1564_DI_INT_MODE1_REG );
64
- outl (0x0 , devpriv -> amcc_iobase + APCI1564_DI_INT_MODE2_REG );
193
+ return 0 ;
194
+ }
65
195
66
- /* Reset the output channels and disable interrupts */
67
- outl (0x0 , devpriv -> amcc_iobase + APCI1564_DO_REG );
68
- outl (0x0 , devpriv -> amcc_iobase + APCI1564_DO_INT_CTRL_REG );
196
+ static int apci1564_cos_cmdtest (struct comedi_device * dev ,
197
+ struct comedi_subdevice * s ,
198
+ struct comedi_cmd * cmd )
199
+ {
200
+ int err = 0 ;
69
201
70
- /* Reset the watchdog registers */
71
- addi_watchdog_reset (devpriv -> amcc_iobase + APCI1564_WDOG_REG );
202
+ /* Step 1 : check if triggers are trivially valid */
72
203
73
- /* Reset the timer registers */
74
- outl (0x0 , devpriv -> amcc_iobase + APCI1564_TIMER_CTRL_REG );
75
- outl (0x0 , devpriv -> amcc_iobase + APCI1564_TIMER_RELOAD_REG );
204
+ err |= cfc_check_trigger_src (& cmd -> start_src , TRIG_NOW );
205
+ err |= cfc_check_trigger_src (& cmd -> scan_begin_src , TRIG_EXT );
206
+ err |= cfc_check_trigger_src (& cmd -> convert_src , TRIG_FOLLOW );
207
+ err |= cfc_check_trigger_src (& cmd -> scan_end_src , TRIG_COUNT );
208
+ err |= cfc_check_trigger_src (& cmd -> stop_src , TRIG_NONE );
76
209
77
- /* Reset the counter registers */
78
- outl (0x0 , dev -> iobase + APCI1564_TCW_CTRL_REG (APCI1564_COUNTER1 ));
79
- outl (0x0 , dev -> iobase + APCI1564_TCW_CTRL_REG (APCI1564_COUNTER2 ));
80
- outl (0x0 , dev -> iobase + APCI1564_TCW_CTRL_REG (APCI1564_COUNTER3 ));
81
- outl (0x0 , dev -> iobase + APCI1564_TCW_CTRL_REG (APCI1564_COUNTER4 ));
210
+ if (err )
211
+ return 1 ;
212
+
213
+ /* Step 2a : make sure trigger sources are unique */
214
+ /* Step 2b : and mutually compatible */
215
+
216
+ if (err )
217
+ return 2 ;
218
+
219
+ /* Step 3: check if arguments are trivially valid */
220
+
221
+ err |= cfc_check_trigger_arg_is (& cmd -> start_arg , 0 );
222
+ err |= cfc_check_trigger_arg_is (& cmd -> scan_begin_arg , 0 );
223
+ err |= cfc_check_trigger_arg_is (& cmd -> convert_arg , 0 );
224
+ err |= cfc_check_trigger_arg_is (& cmd -> scan_end_arg , cmd -> chanlist_len );
225
+ err |= cfc_check_trigger_arg_is (& cmd -> stop_arg , 0 );
226
+
227
+ if (err )
228
+ return 3 ;
229
+
230
+ /* step 4: ignored */
231
+
232
+ if (err )
233
+ return 4 ;
234
+
235
+ return 0 ;
236
+ }
237
+
238
+ /*
239
+ * Change-Of-State (COS) 'do_cmd' operation
240
+ *
241
+ * Enable the COS interrupt as configured by apci1564_cos_insn_config().
242
+ */
243
+ static int apci1564_cos_cmd (struct comedi_device * dev ,
244
+ struct comedi_subdevice * s )
245
+ {
246
+ struct apci1564_private * devpriv = dev -> private ;
247
+
248
+ if (!devpriv -> ctrl ) {
249
+ dev_warn (dev -> class_dev ,
250
+ "Interrupts disabled due to mode configuration!\n" );
251
+ return - EINVAL ;
252
+ }
253
+
254
+ outl (devpriv -> mode1 , devpriv -> amcc_iobase + APCI1564_DI_INT_MODE1_REG );
255
+ outl (devpriv -> mode2 , devpriv -> amcc_iobase + APCI1564_DI_INT_MODE2_REG );
256
+ outl (devpriv -> ctrl , devpriv -> amcc_iobase + APCI1564_DI_IRQ_REG );
257
+
258
+ return 0 ;
259
+ }
260
+
261
+ static int apci1564_cos_cancel (struct comedi_device * dev ,
262
+ struct comedi_subdevice * s )
263
+ {
264
+ struct apci1564_private * devpriv = dev -> private ;
265
+
266
+ outl (0x0 , devpriv -> amcc_iobase + APCI1564_DI_IRQ_REG );
267
+ inl (devpriv -> amcc_iobase + APCI1564_DI_INT_STATUS_REG );
268
+ outl (0x0 , devpriv -> amcc_iobase + APCI1564_DI_INT_MODE1_REG );
269
+ outl (0x0 , devpriv -> amcc_iobase + APCI1564_DI_INT_MODE2_REG );
82
270
83
271
return 0 ;
84
272
}
@@ -113,7 +301,7 @@ static int apci1564_auto_attach(struct comedi_device *dev,
113
301
dev -> irq = pcidev -> irq ;
114
302
}
115
303
116
- ret = comedi_alloc_subdevices (dev , 3 );
304
+ ret = comedi_alloc_subdevices (dev , 4 );
117
305
if (ret )
118
306
return ret ;
119
307
@@ -125,7 +313,6 @@ static int apci1564_auto_attach(struct comedi_device *dev,
125
313
s -> maxdata = 1 ;
126
314
s -> len_chanlist = 32 ;
127
315
s -> range_table = & range_digital ;
128
- s -> insn_config = apci1564_di_config ;
129
316
s -> insn_bits = apci1564_di_insn_bits ;
130
317
131
318
/* Allocate and Initialise DO Subdevice Structures */
@@ -152,6 +339,25 @@ static int apci1564_auto_attach(struct comedi_device *dev,
152
339
s -> insn_read = apci1564_timer_read ;
153
340
s -> insn_config = apci1564_timer_config ;
154
341
342
+ /* Change-Of-State (COS) interrupt subdevice */
343
+ s = & dev -> subdevices [3 ];
344
+ if (dev -> irq ) {
345
+ dev -> read_subdev = s ;
346
+ s -> type = COMEDI_SUBD_DI ;
347
+ s -> subdev_flags = SDF_READABLE | SDF_CMD_READ ;
348
+ s -> n_chan = 1 ;
349
+ s -> maxdata = 1 ;
350
+ s -> range_table = & range_digital ;
351
+ s -> len_chanlist = 1 ;
352
+ s -> insn_config = apci1564_cos_insn_config ;
353
+ s -> insn_bits = apci1564_cos_insn_bits ;
354
+ s -> do_cmdtest = apci1564_cos_cmdtest ;
355
+ s -> do_cmd = apci1564_cos_cmd ;
356
+ s -> cancel = apci1564_cos_cancel ;
357
+ } else {
358
+ s -> type = COMEDI_SUBD_UNUSED ;
359
+ }
360
+
155
361
return 0 ;
156
362
}
157
363
0 commit comments