2 * @file me0600_ext_irq.c
4 * @brief ME-630 external interrupt subdevice instance.
5 * @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
6 * @author Guenter Gebhardt
7 * @author Krzysztof Gantzke (k.gantzke@meilhaus.de)
11 * Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
13 * This file is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License as published by
15 * the Free Software Foundation; either version 2 of the License, or
16 * (at your option) any later version.
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
23 * You should have received a copy of the GNU General Public License
24 * along with this program; if not, write to the Free Software
25 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
35 #include <linux/module.h>
37 #include <linux/slab.h>
38 #include <linux/spinlock.h>
40 #include <linux/types.h>
41 #include <linux/interrupt.h>
43 #include "medefines.h"
44 #include "meinternal.h"
49 #include "meplx_reg.h"
50 #include "me0600_ext_irq_reg.h"
51 #include "me0600_ext_irq.h"
57 static int me0600_ext_irq_io_irq_start(struct me_subdevice
*subdevice
,
61 int irq_edge
, int irq_arg
, int flags
)
63 me0600_ext_irq_subdevice_t
*instance
;
65 unsigned long cpu_flags
;
67 PDEBUG("executed.\n");
69 instance
= (me0600_ext_irq_subdevice_t
*) subdevice
;
71 if (flags
& ~ME_IO_IRQ_START_DIO_BIT
) {
72 PERROR("Invalid flag specified.\n");
73 return ME_ERRNO_INVALID_FLAGS
;
76 if (instance
->lintno
> 1) {
77 PERROR("Wrong idx=%d.\n", instance
->lintno
);
78 return ME_ERRNO_INVALID_SUBDEVICE
;
82 PERROR("Invalid channel specified.\n");
83 return ME_ERRNO_INVALID_CHANNEL
;
86 if (irq_source
!= ME_IRQ_SOURCE_DIO_LINE
) {
87 PERROR("Invalid irq source specified.\n");
88 return ME_ERRNO_INVALID_IRQ_SOURCE
;
91 if (irq_edge
!= ME_IRQ_EDGE_RISING
) {
92 PERROR("Invalid irq edge specified.\n");
93 return ME_ERRNO_INVALID_IRQ_EDGE
;
98 spin_lock_irqsave(&instance
->subdevice_lock
, cpu_flags
);
99 spin_lock(instance
->intcsr_lock
);
100 tmp
= inl(instance
->intcsr
);
101 switch (instance
->lintno
) {
104 PLX_INTCSR_LOCAL_INT1_EN
| PLX_INTCSR_LOCAL_INT1_POL
|
105 PLX_INTCSR_PCI_INT_EN
;
109 PLX_INTCSR_LOCAL_INT2_EN
| PLX_INTCSR_LOCAL_INT2_POL
|
110 PLX_INTCSR_PCI_INT_EN
;
113 outl(tmp
, instance
->intcsr
);
114 PDEBUG_REG("intcsr outl(plx:0x%X)=0x%x\n", instance
->intcsr
, tmp
);
115 spin_unlock(instance
->intcsr_lock
);
117 spin_unlock_irqrestore(&instance
->subdevice_lock
, cpu_flags
);
121 return ME_ERRNO_SUCCESS
;
124 static int me0600_ext_irq_io_irq_wait(struct me_subdevice
*subdevice
,
128 int *value
, int time_out
, int flags
)
130 me0600_ext_irq_subdevice_t
*instance
;
131 int err
= ME_ERRNO_SUCCESS
;
133 unsigned long cpu_flags
;
135 PDEBUG("executed.\n");
137 instance
= (me0600_ext_irq_subdevice_t
*) subdevice
;
140 PERROR("Invalid flag specified.\n");
141 return ME_ERRNO_INVALID_FLAGS
;
145 PERROR("Invalid channel specified.\n");
146 return ME_ERRNO_INVALID_CHANNEL
;
150 PERROR("Invalid time_out specified.\n");
151 return ME_ERRNO_INVALID_TIMEOUT
;
155 t
= (time_out
* HZ
) / 1000;
163 if (instance
->rised
<= 0) {
167 t
= wait_event_interruptible_timeout(instance
->
173 PERROR("Wait on interrupt timed out.\n");
174 err
= ME_ERRNO_TIMEOUT
;
177 wait_event_interruptible(instance
->wait_queue
,
178 (instance
->rised
!= 0));
181 if (instance
->rised
< 0) {
182 PERROR("Wait on interrupt aborted by user.\n");
183 err
= ME_ERRNO_CANCELLED
;
187 if (signal_pending(current
)) {
188 PERROR("Wait on interrupt aborted by signal.\n");
189 err
= ME_ERRNO_SIGNAL
;
192 spin_lock_irqsave(&instance
->subdevice_lock
, cpu_flags
);
194 *irq_count
= instance
->n
;
196 spin_unlock_irqrestore(&instance
->subdevice_lock
, cpu_flags
);
203 static int me0600_ext_irq_io_irq_stop(struct me_subdevice
*subdevice
,
205 int channel
, int flags
)
207 me0600_ext_irq_subdevice_t
*instance
;
208 int err
= ME_ERRNO_SUCCESS
;
210 unsigned long cpu_flags
;
212 PDEBUG("executed.\n");
214 instance
= (me0600_ext_irq_subdevice_t
*) subdevice
;
217 PERROR("Invalid flag specified.\n");
218 return ME_ERRNO_INVALID_FLAGS
;
221 if (instance
->lintno
> 1) {
222 PERROR("Wrong idx=%d.\n", instance
->lintno
);
223 return ME_ERRNO_INVALID_SUBDEVICE
;
227 PERROR("Invalid channel specified.\n");
228 return ME_ERRNO_INVALID_CHANNEL
;
233 spin_lock_irqsave(&instance
->subdevice_lock
, cpu_flags
);
234 spin_lock(instance
->intcsr_lock
);
235 tmp
= inl(instance
->intcsr
);
236 switch (instance
->lintno
) {
238 tmp
&= ~PLX_INTCSR_LOCAL_INT1_EN
;
241 tmp
&= ~PLX_INTCSR_LOCAL_INT2_EN
;
244 outl(tmp
, instance
->intcsr
);
245 PDEBUG_REG("intcsr outl(plx:0x%X)=0x%x\n", instance
->intcsr
, tmp
);
246 spin_unlock(instance
->intcsr_lock
);
247 instance
->rised
= -1;
248 spin_unlock_irqrestore(&instance
->subdevice_lock
, cpu_flags
);
249 wake_up_interruptible_all(&instance
->wait_queue
);
256 static int me0600_ext_irq_io_reset_subdevice(struct me_subdevice
*subdevice
,
257 struct file
*filep
, int flags
)
259 me0600_ext_irq_subdevice_t
*instance
;
261 unsigned long cpu_flags
;
263 PDEBUG("executed.\n");
265 instance
= (me0600_ext_irq_subdevice_t
*) subdevice
;
268 PERROR("Invalid flag specified.\n");
269 return ME_ERRNO_INVALID_FLAGS
;
274 spin_lock_irqsave(&instance
->subdevice_lock
, cpu_flags
);
275 spin_lock(instance
->intcsr_lock
);
276 tmp
= inl(instance
->intcsr
);
277 switch (instance
->lintno
) {
279 tmp
|= PLX_INTCSR_LOCAL_INT1_POL
| PLX_INTCSR_PCI_INT_EN
;
280 tmp
&= ~PLX_INTCSR_LOCAL_INT1_EN
;
283 tmp
|= PLX_INTCSR_LOCAL_INT2_POL
| PLX_INTCSR_PCI_INT_EN
;
284 tmp
&= ~PLX_INTCSR_LOCAL_INT2_EN
;
287 outl(tmp
, instance
->intcsr
);
288 PDEBUG_REG("intcsr outl(plx:0x%X)=0x%x\n", instance
->intcsr
, tmp
);
289 spin_unlock(instance
->intcsr_lock
);
291 instance
->rised
= -1;
293 spin_unlock_irqrestore(&instance
->subdevice_lock
, cpu_flags
);
294 wake_up_interruptible_all(&instance
->wait_queue
);
298 return ME_ERRNO_SUCCESS
;
301 static int me0600_ext_irq_query_number_channels(struct me_subdevice
*subdevice
,
304 PDEBUG("executed.\n");
306 return ME_ERRNO_SUCCESS
;
309 static int me0600_ext_irq_query_subdevice_type(struct me_subdevice
*subdevice
,
310 int *type
, int *subtype
)
312 PDEBUG("executed.\n");
313 *type
= ME_TYPE_EXT_IRQ
;
314 *subtype
= ME_SUBTYPE_SINGLE
;
315 return ME_ERRNO_SUCCESS
;
318 static int me0600_ext_irq_query_subdevice_caps(struct me_subdevice
*subdevice
,
321 PDEBUG("executed.\n");
322 *caps
= ME_CAPS_EXT_IRQ_EDGE_RISING
;
323 return ME_ERRNO_SUCCESS
;
326 static void me0600_ext_irq_destructor(struct me_subdevice
*subdevice
)
328 me0600_ext_irq_subdevice_t
*instance
;
330 PDEBUG("executed.\n");
332 instance
= (me0600_ext_irq_subdevice_t
*) subdevice
;
334 free_irq(instance
->irq
, (void *)instance
);
335 me_subdevice_deinit(&instance
->base
);
339 static irqreturn_t
me0600_isr(int irq
, void *dev_id
)
341 me0600_ext_irq_subdevice_t
*instance
;
343 uint32_t mask
= PLX_INTCSR_PCI_INT_EN
;
344 irqreturn_t ret
= IRQ_HANDLED
;
346 instance
= (me0600_ext_irq_subdevice_t
*) dev_id
;
348 if (irq
!= instance
->irq
) {
349 PERROR("Incorrect interrupt num: %d.\n", irq
);
353 PDEBUG("executed.\n");
355 if (instance
->lintno
> 1) {
357 ("%s():Wrong subdevice index=%d plx:irq_status_reg=0x%04X.\n",
358 __func__
, instance
->lintno
, inl(instance
->intcsr
));
362 spin_lock(&instance
->subdevice_lock
);
363 spin_lock(instance
->intcsr_lock
);
364 status
= inl(instance
->intcsr
);
365 switch (instance
->lintno
) {
367 mask
|= PLX_INTCSR_LOCAL_INT1_STATE
| PLX_INTCSR_LOCAL_INT1_EN
;
370 mask
|= PLX_INTCSR_LOCAL_INT2_STATE
| PLX_INTCSR_LOCAL_INT2_EN
;
374 if ((status
& mask
) == mask
) {
377 inb(instance
->reset_reg
);
378 PDEBUG("Interrupt detected.\n");
381 ("%ld Shared interrupt. %s(): idx=0 plx:irq_status_reg=0x%04X\n",
382 jiffies
, __func__
, status
);
385 spin_unlock(instance
->intcsr_lock
);
386 spin_unlock(&instance
->subdevice_lock
);
388 wake_up_interruptible_all(&instance
->wait_queue
);
393 me0600_ext_irq_subdevice_t
*me0600_ext_irq_constructor(uint32_t plx_reg_base
,
394 uint32_t me0600_reg_base
,
395 spinlock_t
*intcsr_lock
,
396 unsigned ext_irq_idx
,
399 me0600_ext_irq_subdevice_t
*subdevice
;
402 PDEBUG("executed.\n");
404 /* Allocate memory for subdevice instance */
405 subdevice
= kmalloc(sizeof(me0600_ext_irq_subdevice_t
), GFP_KERNEL
);
408 PERROR("Cannot get memory for 630_ext_irq instance.\n");
412 memset(subdevice
, 0, sizeof(me0600_ext_irq_subdevice_t
));
414 /* Initialize subdevice base class */
415 err
= me_subdevice_init(&subdevice
->base
);
418 PERROR("Cannot initialize subdevice base class instance.\n");
422 // Initialize spin locks.
423 spin_lock_init(&subdevice
->subdevice_lock
);
425 subdevice
->intcsr_lock
= intcsr_lock
;
427 /* Initialize wait queue */
428 init_waitqueue_head(&subdevice
->wait_queue
);
430 subdevice
->lintno
= ext_irq_idx
;
432 /* Request interrupt line */
433 subdevice
->irq
= irq
;
435 err
= request_irq(subdevice
->irq
, me0600_isr
,
436 IRQF_DISABLED
| IRQF_SHARED
,
437 ME0600_NAME
, (void *)subdevice
);
440 PERROR("Cannot get interrupt line.\n");
444 PINFO("Registered irq=%d.\n", subdevice
->irq
);
446 /* Initialize registers */
447 subdevice
->intcsr
= plx_reg_base
+ PLX_INTCSR
;
448 subdevice
->reset_reg
=
449 me0600_reg_base
+ ME0600_INT_0_RESET_REG
+ ext_irq_idx
;
451 /* Initialize the subdevice methods */
452 subdevice
->base
.me_subdevice_io_irq_start
= me0600_ext_irq_io_irq_start
;
453 subdevice
->base
.me_subdevice_io_irq_wait
= me0600_ext_irq_io_irq_wait
;
454 subdevice
->base
.me_subdevice_io_irq_stop
= me0600_ext_irq_io_irq_stop
;
455 subdevice
->base
.me_subdevice_io_reset_subdevice
=
456 me0600_ext_irq_io_reset_subdevice
;
457 subdevice
->base
.me_subdevice_query_number_channels
=
458 me0600_ext_irq_query_number_channels
;
459 subdevice
->base
.me_subdevice_query_subdevice_type
=
460 me0600_ext_irq_query_subdevice_type
;
461 subdevice
->base
.me_subdevice_query_subdevice_caps
=
462 me0600_ext_irq_query_subdevice_caps
;
463 subdevice
->base
.me_subdevice_destructor
= me0600_ext_irq_destructor
;
465 subdevice
->rised
= 0;