88
99struct apci1564_private {
1010 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) */
1114 unsigned int do_int_type ;
1215 unsigned char timer_select_mode ;
1316 unsigned char mode_select_register ;
@@ -16,6 +19,38 @@ struct apci1564_private {
1619
1720#include "addi-data/hwdrv_apci1564.c"
1821
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+
1954static irqreturn_t v_ADDI_Interrupt (int irq , void * d )
2055{
2156 apci1564_interrupt (irq , d );
@@ -51,34 +86,187 @@ static int apci1564_do_insn_bits(struct comedi_device *dev,
5186 return insn -> n ;
5287}
5388
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 )
55123{
56124 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+ }
57185
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 ;
59192
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+ }
65195
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 ;
69201
70- /* Reset the watchdog registers */
71- addi_watchdog_reset (devpriv -> amcc_iobase + APCI1564_WDOG_REG );
202+ /* Step 1 : check if triggers are trivially valid */
72203
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 );
76209
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 );
82270
83271 return 0 ;
84272}
@@ -113,7 +301,7 @@ static int apci1564_auto_attach(struct comedi_device *dev,
113301 dev -> irq = pcidev -> irq ;
114302 }
115303
116- ret = comedi_alloc_subdevices (dev , 3 );
304+ ret = comedi_alloc_subdevices (dev , 4 );
117305 if (ret )
118306 return ret ;
119307
@@ -125,7 +313,6 @@ static int apci1564_auto_attach(struct comedi_device *dev,
125313 s -> maxdata = 1 ;
126314 s -> len_chanlist = 32 ;
127315 s -> range_table = & range_digital ;
128- s -> insn_config = apci1564_di_config ;
129316 s -> insn_bits = apci1564_di_insn_bits ;
130317
131318 /* Allocate and Initialise DO Subdevice Structures */
@@ -152,6 +339,25 @@ static int apci1564_auto_attach(struct comedi_device *dev,
152339 s -> insn_read = apci1564_timer_read ;
153340 s -> insn_config = apci1564_timer_config ;
154341
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+
155361 return 0 ;
156362}
157363
0 commit comments