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