• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * @file me0600_ext_irq.c
3  *
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)
8  */
9 
10 /*
11  * Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
12  *
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.
17  *
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.
22  *
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.
26  */
27 
28 #ifndef __KERNEL__
29 #  define __KERNEL__
30 #endif
31 
32 /*
33  * Includes
34  */
35 #include <linux/version.h>
36 #include <linux/module.h>
37 
38 #include <linux/slab.h>
39 #include <linux/spinlock.h>
40 #include <asm/io.h>
41 #include <linux/types.h>
42 #include <linux/interrupt.h>
43 
44 #include "medefines.h"
45 #include "meinternal.h"
46 #include "meerror.h"
47 #include "meids.h"
48 #include "medebug.h"
49 
50 #include "meplx_reg.h"
51 #include "me0600_ext_irq_reg.h"
52 #include "me0600_ext_irq.h"
53 
54 /*
55  * Functions
56  */
57 
me0600_ext_irq_io_irq_start(struct me_subdevice * subdevice,struct file * filep,int channel,int irq_source,int irq_edge,int irq_arg,int flags)58 static int me0600_ext_irq_io_irq_start(struct me_subdevice *subdevice,
59 				       struct file *filep,
60 				       int channel,
61 				       int irq_source,
62 				       int irq_edge, int irq_arg, int flags)
63 {
64 	me0600_ext_irq_subdevice_t *instance;
65 	uint32_t tmp;
66 	unsigned long cpu_flags;
67 
68 	PDEBUG("executed.\n");
69 
70 	instance = (me0600_ext_irq_subdevice_t *) subdevice;
71 
72 	if (flags & ~ME_IO_IRQ_START_DIO_BIT) {
73 		PERROR("Invalid flag specified.\n");
74 		return ME_ERRNO_INVALID_FLAGS;
75 	}
76 
77 	if (instance->lintno > 1) {
78 		PERROR("Wrong idx=%d.\n", instance->lintno);
79 		return ME_ERRNO_INVALID_SUBDEVICE;
80 	}
81 
82 	if (channel) {
83 		PERROR("Invalid channel specified.\n");
84 		return ME_ERRNO_INVALID_CHANNEL;
85 	}
86 
87 	if (irq_source != ME_IRQ_SOURCE_DIO_LINE) {
88 		PERROR("Invalid irq source specified.\n");
89 		return ME_ERRNO_INVALID_IRQ_SOURCE;
90 	}
91 
92 	if (irq_edge != ME_IRQ_EDGE_RISING) {
93 		PERROR("Invalid irq edge specified.\n");
94 		return ME_ERRNO_INVALID_IRQ_EDGE;
95 	}
96 
97 	ME_SUBDEVICE_ENTER;
98 
99 	spin_lock_irqsave(&instance->subdevice_lock, cpu_flags);
100 	spin_lock(instance->intcsr_lock);
101 	tmp = inl(instance->intcsr);
102 	switch (instance->lintno) {
103 	case 0:
104 		tmp |=
105 		    PLX_INTCSR_LOCAL_INT1_EN | PLX_INTCSR_LOCAL_INT1_POL |
106 		    PLX_INTCSR_PCI_INT_EN;
107 		break;
108 	case 1:
109 		tmp |=
110 		    PLX_INTCSR_LOCAL_INT2_EN | PLX_INTCSR_LOCAL_INT2_POL |
111 		    PLX_INTCSR_PCI_INT_EN;
112 		break;
113 	}
114 	outl(tmp, instance->intcsr);
115 	PDEBUG_REG("intcsr outl(plx:0x%X)=0x%x\n", instance->intcsr, tmp);
116 	spin_unlock(instance->intcsr_lock);
117 	instance->rised = 0;
118 	spin_unlock_irqrestore(&instance->subdevice_lock, cpu_flags);
119 
120 	ME_SUBDEVICE_EXIT;
121 
122 	return ME_ERRNO_SUCCESS;
123 }
124 
me0600_ext_irq_io_irq_wait(struct me_subdevice * subdevice,struct file * filep,int channel,int * irq_count,int * value,int time_out,int flags)125 static int me0600_ext_irq_io_irq_wait(struct me_subdevice *subdevice,
126 				      struct file *filep,
127 				      int channel,
128 				      int *irq_count,
129 				      int *value, int time_out, int flags)
130 {
131 	me0600_ext_irq_subdevice_t *instance;
132 	int err = ME_ERRNO_SUCCESS;
133 	long t = 0;
134 	unsigned long cpu_flags;
135 
136 	PDEBUG("executed.\n");
137 
138 	instance = (me0600_ext_irq_subdevice_t *) subdevice;
139 
140 	if (flags) {
141 		PERROR("Invalid flag specified.\n");
142 		return ME_ERRNO_INVALID_FLAGS;
143 	}
144 
145 	if (channel) {
146 		PERROR("Invalid channel specified.\n");
147 		return ME_ERRNO_INVALID_CHANNEL;
148 	}
149 
150 	if (time_out < 0) {
151 		PERROR("Invalid time_out specified.\n");
152 		return ME_ERRNO_INVALID_TIMEOUT;
153 	}
154 
155 	if (time_out) {
156 		t = (time_out * HZ) / 1000;
157 
158 		if (t == 0)
159 			t = 1;
160 	}
161 
162 	ME_SUBDEVICE_ENTER;
163 
164 	if (instance->rised <= 0) {
165 		instance->rised = 0;
166 
167 		if (time_out) {
168 			t = wait_event_interruptible_timeout(instance->
169 							     wait_queue,
170 							     (instance->rised !=
171 							      0), t);
172 
173 			if (t == 0) {
174 				PERROR("Wait on interrupt timed out.\n");
175 				err = ME_ERRNO_TIMEOUT;
176 			}
177 		} else {
178 			wait_event_interruptible(instance->wait_queue,
179 						 (instance->rised != 0));
180 		}
181 
182 		if (instance->rised < 0) {
183 			PERROR("Wait on interrupt aborted by user.\n");
184 			err = ME_ERRNO_CANCELLED;
185 		}
186 	}
187 
188 	if (signal_pending(current)) {
189 		PERROR("Wait on interrupt aborted by signal.\n");
190 		err = ME_ERRNO_SIGNAL;
191 	}
192 
193 	spin_lock_irqsave(&instance->subdevice_lock, cpu_flags);
194 	instance->rised = 0;
195 	*irq_count = instance->n;
196 	*value = 1;
197 	spin_unlock_irqrestore(&instance->subdevice_lock, cpu_flags);
198 
199 	ME_SUBDEVICE_EXIT;
200 
201 	return err;
202 }
203 
me0600_ext_irq_io_irq_stop(struct me_subdevice * subdevice,struct file * filep,int channel,int flags)204 static int me0600_ext_irq_io_irq_stop(struct me_subdevice *subdevice,
205 				      struct file *filep,
206 				      int channel, int flags)
207 {
208 	me0600_ext_irq_subdevice_t *instance;
209 	int err = ME_ERRNO_SUCCESS;
210 	uint32_t tmp;
211 	unsigned long cpu_flags;
212 
213 	PDEBUG("executed.\n");
214 
215 	instance = (me0600_ext_irq_subdevice_t *) subdevice;
216 
217 	if (flags) {
218 		PERROR("Invalid flag specified.\n");
219 		return ME_ERRNO_INVALID_FLAGS;
220 	}
221 
222 	if (instance->lintno > 1) {
223 		PERROR("Wrong idx=%d.\n", instance->lintno);
224 		return ME_ERRNO_INVALID_SUBDEVICE;
225 	}
226 
227 	if (channel) {
228 		PERROR("Invalid channel specified.\n");
229 		return ME_ERRNO_INVALID_CHANNEL;
230 	}
231 
232 	ME_SUBDEVICE_ENTER;
233 
234 	spin_lock_irqsave(&instance->subdevice_lock, cpu_flags);
235 	spin_lock(instance->intcsr_lock);
236 	tmp = inl(instance->intcsr);
237 	switch (instance->lintno) {
238 	case 0:
239 		tmp &= ~PLX_INTCSR_LOCAL_INT1_EN;
240 		break;
241 	case 1:
242 		tmp &= ~PLX_INTCSR_LOCAL_INT2_EN;
243 		break;
244 	}
245 	outl(tmp, instance->intcsr);
246 	PDEBUG_REG("intcsr outl(plx:0x%X)=0x%x\n", instance->intcsr, tmp);
247 	spin_unlock(instance->intcsr_lock);
248 	instance->rised = -1;
249 	spin_unlock_irqrestore(&instance->subdevice_lock, cpu_flags);
250 	wake_up_interruptible_all(&instance->wait_queue);
251 
252 	ME_SUBDEVICE_EXIT;
253 
254 	return err;
255 }
256 
me0600_ext_irq_io_reset_subdevice(struct me_subdevice * subdevice,struct file * filep,int flags)257 static int me0600_ext_irq_io_reset_subdevice(struct me_subdevice *subdevice,
258 					     struct file *filep, int flags)
259 {
260 	me0600_ext_irq_subdevice_t *instance;
261 	uint32_t tmp;
262 	unsigned long cpu_flags;
263 
264 	PDEBUG("executed.\n");
265 
266 	instance = (me0600_ext_irq_subdevice_t *) subdevice;
267 
268 	if (flags) {
269 		PERROR("Invalid flag specified.\n");
270 		return ME_ERRNO_INVALID_FLAGS;
271 	}
272 
273 	ME_SUBDEVICE_ENTER;
274 
275 	spin_lock_irqsave(&instance->subdevice_lock, cpu_flags);
276 	spin_lock(instance->intcsr_lock);
277 	tmp = inl(instance->intcsr);
278 	switch (instance->lintno) {
279 	case 0:
280 		tmp |= PLX_INTCSR_LOCAL_INT1_POL | PLX_INTCSR_PCI_INT_EN;
281 		tmp &= ~PLX_INTCSR_LOCAL_INT1_EN;
282 		break;
283 	case 1:
284 		tmp |= PLX_INTCSR_LOCAL_INT2_POL | PLX_INTCSR_PCI_INT_EN;
285 		tmp &= ~PLX_INTCSR_LOCAL_INT2_EN;
286 		break;
287 	}
288 	outl(tmp, instance->intcsr);
289 	PDEBUG_REG("intcsr outl(plx:0x%X)=0x%x\n", instance->intcsr, tmp);
290 	spin_unlock(instance->intcsr_lock);
291 
292 	instance->rised = -1;
293 	instance->n = 0;
294 	spin_unlock_irqrestore(&instance->subdevice_lock, cpu_flags);
295 	wake_up_interruptible_all(&instance->wait_queue);
296 
297 	ME_SUBDEVICE_EXIT;
298 
299 	return ME_ERRNO_SUCCESS;
300 }
301 
me0600_ext_irq_query_number_channels(struct me_subdevice * subdevice,int * number)302 static int me0600_ext_irq_query_number_channels(struct me_subdevice *subdevice,
303 						int *number)
304 {
305 	PDEBUG("executed.\n");
306 	*number = 1;
307 	return ME_ERRNO_SUCCESS;
308 }
309 
me0600_ext_irq_query_subdevice_type(struct me_subdevice * subdevice,int * type,int * subtype)310 static int me0600_ext_irq_query_subdevice_type(struct me_subdevice *subdevice,
311 					       int *type, int *subtype)
312 {
313 	PDEBUG("executed.\n");
314 	*type = ME_TYPE_EXT_IRQ;
315 	*subtype = ME_SUBTYPE_SINGLE;
316 	return ME_ERRNO_SUCCESS;
317 }
318 
me0600_ext_irq_query_subdevice_caps(struct me_subdevice * subdevice,int * caps)319 static int me0600_ext_irq_query_subdevice_caps(struct me_subdevice *subdevice,
320 					       int *caps)
321 {
322 	PDEBUG("executed.\n");
323 	*caps = ME_CAPS_EXT_IRQ_EDGE_RISING;
324 	return ME_ERRNO_SUCCESS;
325 }
326 
me0600_ext_irq_destructor(struct me_subdevice * subdevice)327 static void me0600_ext_irq_destructor(struct me_subdevice *subdevice)
328 {
329 	me0600_ext_irq_subdevice_t *instance;
330 
331 	PDEBUG("executed.\n");
332 
333 	instance = (me0600_ext_irq_subdevice_t *) subdevice;
334 
335 	free_irq(instance->irq, (void *)instance);
336 	me_subdevice_deinit(&instance->base);
337 	kfree(instance);
338 }
339 
340 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19)
me0600_isr(int irq,void * dev_id)341 static irqreturn_t me0600_isr(int irq, void *dev_id)
342 #else
343 static irqreturn_t me0600_isr(int irq, void *dev_id, struct pt_regs *regs)
344 #endif
345 {
346 	me0600_ext_irq_subdevice_t *instance;
347 	uint32_t status;
348 	uint32_t mask = PLX_INTCSR_PCI_INT_EN;
349 	irqreturn_t ret = IRQ_HANDLED;
350 
351 	instance = (me0600_ext_irq_subdevice_t *) dev_id;
352 
353 	if (irq != instance->irq) {
354 		PERROR("Incorrect interrupt num: %d.\n", irq);
355 		return IRQ_NONE;
356 	}
357 
358 	PDEBUG("executed.\n");
359 
360 	if (instance->lintno > 1) {
361 		PERROR_CRITICAL
362 		    ("%s():Wrong subdevice index=%d plx:irq_status_reg=0x%04X.\n",
363 		     __func__, instance->lintno, inl(instance->intcsr));
364 		return IRQ_NONE;
365 	}
366 
367 	spin_lock(&instance->subdevice_lock);
368 	spin_lock(instance->intcsr_lock);
369 	status = inl(instance->intcsr);
370 	switch (instance->lintno) {
371 	case 0:
372 		mask |= PLX_INTCSR_LOCAL_INT1_STATE | PLX_INTCSR_LOCAL_INT1_EN;
373 		break;
374 	case 1:
375 		mask |= PLX_INTCSR_LOCAL_INT2_STATE | PLX_INTCSR_LOCAL_INT2_EN;
376 		break;
377 	}
378 
379 	if ((status & mask) == mask) {
380 		instance->rised = 1;
381 		instance->n++;
382 		inb(instance->reset_reg);
383 		PDEBUG("Interrupt detected.\n");
384 	} else {
385 		PINFO
386 		    ("%ld Shared interrupt. %s(): idx=0 plx:irq_status_reg=0x%04X\n",
387 		     jiffies, __func__, status);
388 		ret = IRQ_NONE;
389 	}
390 	spin_unlock(instance->intcsr_lock);
391 	spin_unlock(&instance->subdevice_lock);
392 
393 	wake_up_interruptible_all(&instance->wait_queue);
394 
395 	return ret;
396 }
397 
me0600_ext_irq_constructor(uint32_t plx_reg_base,uint32_t me0600_reg_base,spinlock_t * intcsr_lock,unsigned ext_irq_idx,int irq)398 me0600_ext_irq_subdevice_t *me0600_ext_irq_constructor(uint32_t plx_reg_base,
399 						       uint32_t me0600_reg_base,
400 						       spinlock_t * intcsr_lock,
401 						       unsigned ext_irq_idx,
402 						       int irq)
403 {
404 	me0600_ext_irq_subdevice_t *subdevice;
405 	int err;
406 
407 	PDEBUG("executed.\n");
408 
409 	/* Allocate memory for subdevice instance */
410 	subdevice = kmalloc(sizeof(me0600_ext_irq_subdevice_t), GFP_KERNEL);
411 
412 	if (!subdevice) {
413 		PERROR("Cannot get memory for 630_ext_irq instance.\n");
414 		return NULL;
415 	}
416 
417 	memset(subdevice, 0, sizeof(me0600_ext_irq_subdevice_t));
418 
419 	/* Initialize subdevice base class */
420 	err = me_subdevice_init(&subdevice->base);
421 
422 	if (err) {
423 		PERROR("Cannot initialize subdevice base class instance.\n");
424 		kfree(subdevice);
425 		return NULL;
426 	}
427 	// Initialize spin locks.
428 	spin_lock_init(&subdevice->subdevice_lock);
429 
430 	subdevice->intcsr_lock = intcsr_lock;
431 
432 	/* Initialize wait queue */
433 	init_waitqueue_head(&subdevice->wait_queue);
434 
435 	subdevice->lintno = ext_irq_idx;
436 
437 	/* Request interrupt line */
438 	subdevice->irq = irq;
439 
440 	err = request_irq(subdevice->irq, me0600_isr,
441 #ifdef IRQF_DISABLED
442 			  IRQF_DISABLED | IRQF_SHARED,
443 #else
444 			  SA_INTERRUPT | SA_SHIRQ,
445 #endif
446 			  ME0600_NAME, (void *)subdevice);
447 
448 	if (err) {
449 		PERROR("Cannot get interrupt line.\n");
450 		kfree(subdevice);
451 		return NULL;
452 	}
453 	PINFO("Registered irq=%d.\n", subdevice->irq);
454 
455 	/* Initialize registers */
456 	subdevice->intcsr = plx_reg_base + PLX_INTCSR;
457 	subdevice->reset_reg =
458 	    me0600_reg_base + ME0600_INT_0_RESET_REG + ext_irq_idx;
459 
460 	/* Initialize the subdevice methods */
461 	subdevice->base.me_subdevice_io_irq_start = me0600_ext_irq_io_irq_start;
462 	subdevice->base.me_subdevice_io_irq_wait = me0600_ext_irq_io_irq_wait;
463 	subdevice->base.me_subdevice_io_irq_stop = me0600_ext_irq_io_irq_stop;
464 	subdevice->base.me_subdevice_io_reset_subdevice =
465 	    me0600_ext_irq_io_reset_subdevice;
466 	subdevice->base.me_subdevice_query_number_channels =
467 	    me0600_ext_irq_query_number_channels;
468 	subdevice->base.me_subdevice_query_subdevice_type =
469 	    me0600_ext_irq_query_subdevice_type;
470 	subdevice->base.me_subdevice_query_subdevice_caps =
471 	    me0600_ext_irq_query_subdevice_caps;
472 	subdevice->base.me_subdevice_destructor = me0600_ext_irq_destructor;
473 
474 	subdevice->rised = 0;
475 	subdevice->n = 0;
476 
477 	return subdevice;
478 }
479