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