Initial commit
[kernel/linux-3.0.git] / drivers / media / video / samsung / mali_r2p3 / linux / mali_osk_irq.c
1 /*
2  * Copyright (C) 2010 ARM Limited. All rights reserved.
3  * 
4  * This program is free software and is provided to you under the terms of the GNU General Public License version 2
5  * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence.
6  * 
7  * A copy of the licence is included with the program, and can also be obtained from Free Software
8  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
9  */
10
11 /**
12  * @file mali_osk_irq.c
13  * Implementation of the OS abstraction layer for the kernel device driver
14  */
15
16 #include <linux/slab.h> /* For memory allocation */
17 #include <linux/workqueue.h>
18
19 #include "mali_osk.h"
20 #include "mali_kernel_core.h"
21 #include "mali_kernel_common.h"
22 #include "mali_kernel_license.h"
23 #include "linux/interrupt.h"
24
25 typedef struct _mali_osk_irq_t_struct
26 {
27         u32 irqnum;
28         void *data;
29         _mali_osk_irq_uhandler_t uhandler;
30         _mali_osk_irq_bhandler_t bhandler;
31         struct work_struct work_queue_irq_handle; /* Workqueue for the bottom half of the IRQ-handling. This job is activated when this core gets an IRQ.*/
32 } mali_osk_irq_object_t;
33
34 static struct workqueue_struct *mali_irq_wq=NULL;
35
36 typedef void (*workqueue_func_t)(void *);
37 typedef irqreturn_t (*irq_handler_func_t)(int, void *, struct pt_regs *);
38 static irqreturn_t irq_handler_upper_half (int port_name, void* dev_id ); /* , struct pt_regs *regs*/
39
40 #if defined(INIT_DELAYED_WORK)
41 static void irq_handler_bottom_half ( struct work_struct *work );
42 #else
43 static void irq_handler_bottom_half ( void *  input );
44 #endif
45
46 /**
47  * Linux kernel version has marked SA_SHIRQ as deprecated, IRQF_SHARED should be used.
48  * This is to handle older kernels which haven't done this swap.
49  */
50 #ifndef IRQF_SHARED
51 #define IRQF_SHARED SA_SHIRQ
52 #endif /* IRQF_SHARED */
53
54 _mali_osk_irq_t *_mali_osk_irq_init( u32 irqnum, _mali_osk_irq_uhandler_t uhandler,     _mali_osk_irq_bhandler_t bhandler, _mali_osk_irq_trigger_t trigger_func, _mali_osk_irq_ack_t ack_func, void *data, const char *description )
55 {
56         mali_osk_irq_object_t *irq_object;
57
58         irq_object = kmalloc(sizeof(mali_osk_irq_object_t), GFP_KERNEL);
59         if (NULL == irq_object) return NULL;
60
61         /* workqueue API changed in 2.6.20, support both versions: */
62 #if defined(INIT_DELAYED_WORK)
63         /* New syntax: INIT_WORK( struct work_struct *work, void (*function)(struct work_struct *)) */
64         INIT_WORK( &irq_object->work_queue_irq_handle, irq_handler_bottom_half);
65 #else
66         /* Old syntax: INIT_WORK( struct work_struct *work, void (*function)(void *), void *data) */
67         INIT_WORK( &irq_object->work_queue_irq_handle, irq_handler_bottom_half, irq_object);
68 #endif /* defined(INIT_DELAYED_WORK) */
69
70         if (-1 == irqnum)
71         {
72                 /* Probe for IRQ */
73                 if ( (NULL != trigger_func) && (NULL != ack_func) )
74                 {
75                         unsigned long probe_count = 3;
76                         _mali_osk_errcode_t err;
77                         int irq;
78
79                         MALI_DEBUG_PRINT(2, ("Probing for irq\n"));
80
81                         do
82                         {
83                                 unsigned long mask;
84
85                                 mask = probe_irq_on();
86                                 trigger_func(data);
87
88                                 _mali_osk_time_ubusydelay(5);
89
90                                 irq = probe_irq_off(mask);
91                                 err = ack_func(data);
92                         }
93                         while (irq < 0 && (err == _MALI_OSK_ERR_OK) && probe_count--);
94
95                         if (irq < 0 || (_MALI_OSK_ERR_OK != err)) irqnum = -1;
96                         else irqnum = irq;
97                 }
98                 else irqnum = -1; /* no probe functions, fault */
99
100                 if (-1 != irqnum)
101                 {
102                         /* found an irq */
103                         MALI_DEBUG_PRINT(2, ("Found irq %d\n", irqnum));
104                 }
105                 else
106                 {
107                         MALI_DEBUG_PRINT(2, ("Probe for irq failed\n"));
108                 }
109         }
110
111         irq_object->irqnum = irqnum;
112         irq_object->uhandler = uhandler;
113         irq_object->bhandler = bhandler;
114         irq_object->data = data;
115
116         /* Is this a real IRQ handler we need? */
117         if (!mali_benchmark && irqnum != _MALI_OSK_IRQ_NUMBER_FAKE && irqnum != _MALI_OSK_IRQ_NUMBER_PMM)
118         {
119                 if (-1 == irqnum)
120                 {
121                         MALI_DEBUG_PRINT(2, ("No IRQ for core '%s' found during probe\n", description));
122                         kfree(irq_object);
123                         return NULL;
124                 }
125
126                 if (0 != request_irq(irqnum, irq_handler_upper_half, IRQF_SHARED, description, irq_object))
127                 {
128                         MALI_DEBUG_PRINT(2, ("Unable to install IRQ handler for core '%s'\n", description));
129                         kfree(irq_object);
130                         return NULL;
131                 }
132         }
133
134         if (mali_irq_wq == NULL)
135         {
136                 mali_irq_wq = create_singlethread_workqueue("mali-pmm-wq");
137         }
138
139         return irq_object;
140 }
141
142 void _mali_osk_irq_schedulework( _mali_osk_irq_t *irq )
143 {
144         mali_osk_irq_object_t *irq_object = (mali_osk_irq_object_t *)irq;
145         queue_work_on(0, mali_irq_wq,&irq_object->work_queue_irq_handle);
146 }
147
148 void _mali_osk_flush_workqueue( _mali_osk_irq_t *irq )
149 {
150                 flush_workqueue(mali_irq_wq );
151 }
152
153 void _mali_osk_irq_term( _mali_osk_irq_t *irq )
154 {
155         mali_osk_irq_object_t *irq_object = (mali_osk_irq_object_t *)irq;
156
157         if(mali_irq_wq != NULL)
158         {
159                 flush_workqueue(mali_irq_wq);
160                 destroy_workqueue(mali_irq_wq);
161                 mali_irq_wq = NULL;
162         }       
163
164         if (!mali_benchmark)
165         {
166                 free_irq(irq_object->irqnum, irq_object);
167         }
168         kfree(irq_object);
169         flush_scheduled_work();
170 }
171
172
173 /** This function is called directly in interrupt context from the OS just after
174  * the CPU get the hw-irq from mali, or other devices on the same IRQ-channel.
175  * It is registered one of these function for each mali core. When an interrupt
176  * arrives this function will be called equal times as registered mali cores.
177  * That means that we only check one mali core in one function call, and the
178  * core we check for each turn is given by the \a dev_id variable.
179  * If we detect an pending interrupt on the given core, we mask the interrupt
180  * out by settging the core's IRQ_MASK register to zero.
181  * Then we schedule the mali_core_irq_handler_bottom_half to run as high priority
182  * work queue job.
183  */
184 static irqreturn_t irq_handler_upper_half (int port_name, void* dev_id ) /* , struct pt_regs *regs*/
185 {
186         mali_osk_irq_object_t *irq_object = (mali_osk_irq_object_t *)dev_id;
187
188         if (irq_object->uhandler(irq_object->data) == _MALI_OSK_ERR_OK)
189         {
190                 return IRQ_HANDLED;
191         }
192         return IRQ_NONE;
193 }
194
195 /* Is executed when an interrupt occur on one core */
196 /* workqueue API changed in 2.6.20, support both versions: */
197 #if defined(INIT_DELAYED_WORK)
198 static void irq_handler_bottom_half ( struct work_struct *work )
199 #else
200 static void irq_handler_bottom_half ( void *  input )
201 #endif
202 {
203         mali_osk_irq_object_t *irq_object;
204
205 #if defined(INIT_DELAYED_WORK)
206         irq_object = _MALI_OSK_CONTAINER_OF(work, mali_osk_irq_object_t, work_queue_irq_handle);
207 #else
208         if ( NULL == input )
209         {
210                 MALI_PRINT_ERROR(("IRQ: Null pointer! Illegal!"));
211                 return; /* Error */
212         }
213         irq_object = (mali_osk_irq_object_t *) input;
214 #endif
215
216         irq_object->bhandler(irq_object->data);
217 }
218