]> pilppa.org Git - linux-2.6-omap-h63xx.git/blob - drivers/cbus/retu.c
ARM: OMAP: Add CBUS support
[linux-2.6-omap-h63xx.git] / drivers / cbus / retu.c
1 /**
2  * drivers/cbus/retu.c
3  *
4  * Support functions for Retu ASIC
5  *
6  * Copyright (C) 2004, 2005 Nokia Corporation
7  *
8  * Written by Juha Yrjölä <juha.yrjola@nokia.com>,
9  *            David Weinehall <david.weinehall@nokia.com>, and
10  *            Mikko Ylinen <mikko.k.ylinen@nokia.com>
11  *
12  * This file is subject to the terms and conditions of the GNU General
13  * Public License. See the file "COPYING" in the main directory of this
14  * archive for more details.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
24  */
25
26 #include <linux/module.h>
27 #include <linux/init.h>
28
29 #include <linux/config.h>
30 #include <linux/kernel.h>
31 #include <linux/errno.h>
32 #include <linux/device.h>
33 #include <linux/miscdevice.h>
34 #include <linux/poll.h>
35 #include <linux/fs.h>
36 #include <linux/interrupt.h>
37 #include <linux/platform_device.h>
38
39 #include <asm/uaccess.h>
40
41 #include <asm/arch/mux.h>
42 #include <asm/arch/gpio.h>
43 #include <asm/arch/board.h>
44
45 #include "cbus.h"
46 #include "retu.h"
47
48 #define RETU_ID                 0x01
49 #define PFX                     "retu: "
50
51 static int retu_initialized;
52 static int retu_irq_pin;
53
54 static struct tasklet_struct retu_tasklet;
55 spinlock_t retu_lock = SPIN_LOCK_UNLOCKED;
56
57 static struct completion device_release;
58
59 struct retu_irq_handler_desc {
60         int (*func)(unsigned long);
61         unsigned long arg;
62         char name[8];
63 };
64
65 static struct retu_irq_handler_desc retu_irq_handlers[MAX_RETU_IRQ_HANDLERS];
66
67 /**
68  * retu_read_reg - Read a value from a register in Retu
69  * @reg: the register to read from
70  *
71  * This function returns the contents of the specified register
72  */
73 int retu_read_reg(int reg)
74 {
75         BUG_ON(!retu_initialized);
76         return cbus_read_reg(cbus_host, RETU_ID, reg);
77 }
78
79 /**
80  * retu_write_reg - Write a value to a register in Retu
81  * @reg: the register to write to
82  * @reg: the value to write to the register
83  *
84  * This function writes a value to the specified register
85  */
86 void retu_write_reg(int reg, u16 val)
87 {
88         BUG_ON(!retu_initialized);
89         cbus_write_reg(cbus_host, RETU_ID, reg, val);
90 }
91
92 #define ADC_MAX_CHAN_NUMBER     13
93
94 int retu_read_adc(int channel)
95 {
96         unsigned long flags;
97         int res;
98
99         if (channel < 0 || channel > ADC_MAX_CHAN_NUMBER)
100                 return -EINVAL;
101
102         spin_lock_irqsave(&retu_lock, flags);
103         /* Select the channel and read result */
104         retu_write_reg(RETU_REG_ADCR, channel << 10);
105         res = retu_read_reg(RETU_REG_ADCR) & 0x3ff;
106         /* Unlock retu */
107         spin_unlock_irqrestore(&retu_lock, flags);
108
109         return res;
110 }
111
112
113 static u16 retu_disable_bogus_irqs(u16 mask)
114 {
115        int i;
116
117        for (i = 0; i < MAX_RETU_IRQ_HANDLERS; i++) {
118                if (mask & (1 << i))
119                        continue;
120                if (retu_irq_handlers[i].func != NULL)
121                        continue;
122                /* an IRQ was enabled but we don't have a handler for it */
123                printk(KERN_INFO PFX "disabling bogus IRQ %d\n", i);
124                mask |= (1 << i);
125        }
126        return mask;
127 }
128
129 /*
130  * Disable given RETU interrupt
131  */
132 void retu_disable_irq(int id)
133 {
134         unsigned long flags;
135         u16 mask;
136
137         spin_lock_irqsave(&retu_lock, flags);
138         mask = retu_read_reg(RETU_REG_IMR);
139         mask |= 1 << id;
140         mask = retu_disable_bogus_irqs(mask);
141         retu_write_reg(RETU_REG_IMR, mask);
142         spin_unlock_irqrestore(&retu_lock, flags);
143 }
144
145 /*
146  * Enable given RETU interrupt
147  */
148 void retu_enable_irq(int id)
149 {
150         unsigned long flags;
151         u16 mask;
152
153         if (id == 3) {
154                 printk("Enabling Retu IRQ %d\n", id);
155                 dump_stack();
156         }
157         spin_lock_irqsave(&retu_lock, flags);
158         mask = retu_read_reg(RETU_REG_IMR);
159         mask &= ~(1 << id);
160         mask = retu_disable_bogus_irqs(mask);
161         retu_write_reg(RETU_REG_IMR, mask);
162         spin_unlock_irqrestore(&retu_lock, flags);
163 }
164
165 /*
166  * Acknowledge given RETU interrupt
167  */
168 void retu_ack_irq(int id)
169 {
170         retu_write_reg(RETU_REG_IDR, 1 << id);
171 }
172
173 /*
174  * RETU interrupt handler. Only schedules the tasklet.
175  */
176 static irqreturn_t retu_irq_handler(int irq, void *dev_id, struct pt_regs *regs)
177 {
178         tasklet_schedule(&retu_tasklet);
179         return IRQ_HANDLED;
180 }
181
182 /*
183  * Tasklet handler
184  */
185 static void retu_tasklet_handler(unsigned long data)
186 {
187         struct retu_irq_handler_desc *hnd;
188         u16 id;
189         u16 im;
190         int i;
191
192         for (;;) {
193                 id = retu_read_reg(RETU_REG_IDR);
194                 im = ~retu_read_reg(RETU_REG_IMR);
195                 id &= im;
196
197                 if (!id)
198                         break;
199
200                 for (i = 0; id != 0; i++, id >>= 1) {
201                         if (!(id & 1))
202                                 continue;
203                         hnd = &retu_irq_handlers[i];
204                         if (hnd->func == NULL) {
205                                /* Spurious retu interrupt - disable and ack it */
206                                 printk(KERN_INFO "Spurious Retu interrupt "
207                                                  "(id %d)\n", i);
208                                 retu_disable_irq(i);
209                                 retu_ack_irq(i);
210                                 continue;
211                         }
212                         hnd->func(hnd->arg);
213                         /*
214                          * Don't acknowledge the interrupt here
215                          * It must be done explicitly
216                          */
217                 }
218         }
219 }
220
221 /*
222  * Register the handler for a given RETU interrupt source.
223  */
224 int retu_request_irq(int id, void *irq_handler, unsigned long arg, char *name)
225 {
226         struct retu_irq_handler_desc *hnd;
227
228         if (irq_handler == NULL || id >= MAX_RETU_IRQ_HANDLERS ||
229             name == NULL) {
230                 printk(KERN_ERR PFX "Invalid arguments to %s\n",
231                        __FUNCTION__);
232                 return -EINVAL;
233         }
234         hnd = &retu_irq_handlers[id];
235         if (hnd->func != NULL) {
236                 printk(KERN_ERR PFX "IRQ %d already reserved\n", id);
237                 return -EBUSY;
238         }
239         printk(KERN_INFO PFX "Registering interrupt %d for device %s\n",
240                id, name);
241         hnd->func = irq_handler;
242         hnd->arg = arg;
243         strlcpy(hnd->name, name, sizeof(hnd->name));
244
245         retu_ack_irq(id);
246         retu_enable_irq(id);
247
248         return 0;
249 }
250
251 /*
252  * Unregister the handler for a given RETU interrupt source.
253  */
254 void retu_free_irq(int id)
255 {
256         struct retu_irq_handler_desc *hnd;
257
258         if (id >= MAX_RETU_IRQ_HANDLERS) {
259                 printk(KERN_ERR PFX "Invalid argument to %s\n",
260                        __FUNCTION__);
261                 return;
262         }
263         hnd = &retu_irq_handlers[id];
264         if (hnd->func == NULL) {
265                 printk(KERN_ERR PFX "IRQ %d already freed\n", id);
266                 return;
267         }
268
269         retu_disable_irq(id);
270         hnd->func = NULL;
271 }
272
273 /**
274  * retu_power_off - Shut down power to system
275  *
276  * This function puts the system in power off state
277  */
278 static void retu_power_off(void)
279 {
280         /* Ignore power button state */
281         retu_write_reg(RETU_REG_CC1, retu_read_reg(RETU_REG_CC1) | 2);
282         /* Expire watchdog immediately */
283         retu_write_reg(RETU_REG_WATCHDOG, 0);
284         /* Wait for poweroff*/
285         for (;;);
286 }
287
288 /**
289  * retu_probe - Probe for Retu ASIC
290  * @dev: the Retu device
291  *
292  * Probe for the Retu ASIC and allocate memory
293  * for its device-struct if found
294  */
295 static int __devinit retu_probe(struct device *dev)
296 {
297         const struct omap_em_asic_bb5_config * em_asic_config;
298         int ret;
299
300         /* Prepare tasklet */
301         tasklet_init(&retu_tasklet, retu_tasklet_handler, 0);
302
303         em_asic_config = omap_get_config(OMAP_TAG_EM_ASIC_BB5,
304                                          struct omap_em_asic_bb5_config);
305         if (em_asic_config == NULL) {
306                 printk(KERN_ERR PFX "Unable to retrieve config data\n");
307                 return -ENODATA;
308         }
309
310         retu_irq_pin = em_asic_config->retu_irq_gpio;
311
312         if ((ret = omap_request_gpio(retu_irq_pin)) < 0) {
313                 printk(KERN_ERR PFX "Unable to reserve IRQ GPIO\n");
314                 return ret;
315         }
316
317         /* Set the pin as input */
318         omap_set_gpio_direction(retu_irq_pin, 1);
319
320         /* Rising edge triggers the IRQ */
321         set_irq_type(OMAP_GPIO_IRQ(retu_irq_pin), IRQT_RISING);
322
323         retu_initialized = 1;
324         /* Mask all RETU interrupts */
325         retu_write_reg(RETU_REG_IMR, 0xffff);
326
327         ret = request_irq(OMAP_GPIO_IRQ(retu_irq_pin), retu_irq_handler, 0,
328                           "retu", 0);
329
330         if (ret < 0) {
331                 printk(KERN_ERR PFX "Unable to register IRQ handler\n");
332                 omap_free_gpio(retu_irq_pin);
333                 return ret;
334         }
335
336         /* Register power off function */
337         pm_power_off = retu_power_off;
338
339 #ifdef CONFIG_CBUS_RETU_USER
340         /* Initialize user-space interface */
341         if (retu_user_init() < 0) {
342                 printk(KERN_ERR "Unable to initialize driver\n");
343                 free_irq(OMAP_GPIO_IRQ(retu_irq_pin), 0);
344                 omap_free_gpio(retu_irq_pin);
345                 return ret;
346         }
347 #endif
348
349         return 0;
350 }
351
352 static int retu_remove(struct device *dev)
353 {
354 #ifdef CONFIG_CBUS_RETU_USER
355         retu_user_cleanup();
356 #endif
357         /* Mask all RETU interrupts */
358         retu_write_reg(RETU_REG_IMR, 0xffff);
359         free_irq(OMAP_GPIO_IRQ(retu_irq_pin), 0);
360         omap_free_gpio(retu_irq_pin);
361         tasklet_kill(&retu_tasklet);
362
363         return 0;
364 }
365
366 static void retu_device_release(struct device *dev)
367 {
368         complete(&device_release);
369 }
370
371 static struct device_driver retu_driver = {
372         .name           = "retu",
373         .bus            = &platform_bus_type,
374         .probe          = retu_probe,
375         .remove         = retu_remove,
376 };
377
378 static struct platform_device retu_device = {
379         .name           = "retu",
380         .id             = -1,
381         .dev = {
382                 .release = retu_device_release,
383         }
384 };
385
386 /**
387  * retu_init - initialise Retu driver
388  *
389  * Initialise the Retu driver and return 0 if everything worked ok
390  */
391 static int __init retu_init(void)
392 {
393         int ret = 0;
394
395         printk(KERN_INFO "Retu driver initialising\n");
396
397         init_completion(&device_release);
398
399         if ((ret = driver_register(&retu_driver)) < 0)
400                 return ret;
401
402         if ((ret = platform_device_register(&retu_device)) < 0) {
403                 driver_unregister(&retu_driver);
404                 return ret;
405         }
406         return 0;
407 }
408
409 /*
410  * Cleanup
411  */
412 static void __exit retu_exit(void)
413 {
414         platform_device_unregister(&retu_device);
415         driver_unregister(&retu_driver);
416         wait_for_completion(&device_release);
417 }
418
419 EXPORT_SYMBOL(retu_request_irq);
420 EXPORT_SYMBOL(retu_free_irq);
421 EXPORT_SYMBOL(retu_enable_irq);
422 EXPORT_SYMBOL(retu_disable_irq);
423 EXPORT_SYMBOL(retu_ack_irq);
424 EXPORT_SYMBOL(retu_read_reg);
425 EXPORT_SYMBOL(retu_write_reg);
426
427 subsys_initcall(retu_init);
428 module_exit(retu_exit);
429
430 MODULE_DESCRIPTION("Retu ASIC control");
431 MODULE_LICENSE("GPL");
432 MODULE_AUTHOR("Juha Yrjölä, David Weinehall, and Mikko Ylinen");