Merge current mainline tree into linux-omap tree
[pandora-kernel.git] / drivers / cbus / tahvo.c
1 /**
2  * drivers/cbus/tahvo.c
3  *
4  * Support functions for Tahvo 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 <asm/arch/mux.h>
42 #include <asm/arch/gpio.h>
43 #include <asm/arch/board.h>
44
45 #include "cbus.h"
46 #include "tahvo.h"
47
48 #define TAHVO_ID                0x02
49 #define PFX                     "tahvo: "
50
51 static int tahvo_initialized;
52 static int tahvo_irq_pin;
53 static int tahvo_is_betty;
54
55 static struct tasklet_struct tahvo_tasklet;
56 spinlock_t tahvo_lock = SPIN_LOCK_UNLOCKED;
57
58 static struct completion device_release;
59
60 struct tahvo_irq_handler_desc {
61         int (*func)(unsigned long);
62         unsigned long arg;
63         char name[8];
64 };
65
66 static struct tahvo_irq_handler_desc tahvo_irq_handlers[MAX_TAHVO_IRQ_HANDLERS];
67
68 /**
69  * tahvo_read_reg - Read a value from a register in Tahvo
70  * @reg: the register to read from
71  *
72  * This function returns the contents of the specified register
73  */
74 int tahvo_read_reg(int reg)
75 {
76         BUG_ON(!tahvo_initialized);
77         return cbus_read_reg(cbus_host, TAHVO_ID, reg);
78 }
79
80 /**
81  * tahvo_write_reg - Write a value to a register in Tahvo
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 tahvo_write_reg(int reg, u16 val)
88 {
89         BUG_ON(!tahvo_initialized);
90         cbus_write_reg(cbus_host, TAHVO_ID, reg, val);
91 }
92
93 /**
94  * tahvo_set_clear_reg_bits - set and clear register bits atomically
95  * @reg: the register to write to
96  * @bits: the bits to set
97  *
98  * This function sets and clears the specified Tahvo register bits atomically
99  */
100 void tahvo_set_clear_reg_bits(int reg, u16 set, u16 clear)
101 {
102         unsigned long flags;
103         u16 w;
104
105         spin_lock_irqsave(&tahvo_lock, flags);
106         w = tahvo_read_reg(reg);
107         w &= ~clear;
108         w |= set;
109         tahvo_write_reg(reg, w);
110         spin_unlock_irqrestore(&tahvo_lock, flags);
111 }
112
113 /*
114  * Disable given TAHVO interrupt
115  */
116 void tahvo_disable_irq(int id)
117 {
118         unsigned long flags;
119         u16 mask;
120
121         spin_lock_irqsave(&tahvo_lock, flags);
122         mask = tahvo_read_reg(TAHVO_REG_IMR);
123         mask |= 1 << id;
124         tahvo_write_reg(TAHVO_REG_IMR, mask);
125         spin_unlock_irqrestore(&tahvo_lock, flags);
126 }
127
128 /*
129  * Enable given TAHVO interrupt
130  */
131 void tahvo_enable_irq(int id)
132 {
133         unsigned long flags;
134         u16 mask;
135
136         spin_lock_irqsave(&tahvo_lock, flags);
137         mask = tahvo_read_reg(TAHVO_REG_IMR);
138         mask &= ~(1 << id);
139         tahvo_write_reg(TAHVO_REG_IMR, mask);
140         spin_unlock_irqrestore(&tahvo_lock, flags);
141 }
142
143 /*
144  * Acknowledge given TAHVO interrupt
145  */
146 void tahvo_ack_irq(int id)
147 {
148         tahvo_write_reg(TAHVO_REG_IDR, 1 << id);
149 }
150
151 static int tahvo_7bit_backlight;
152
153 int tahvo_get_backlight_level(void)
154 {
155         int mask;
156
157         if (tahvo_7bit_backlight)
158                 mask = 0x7f;
159         else
160                 mask = 0x0f;
161         return tahvo_read_reg(TAHVO_REG_LEDPWMR) & mask;
162 }
163
164 int tahvo_get_max_backlight_level(void)
165 {
166         if (tahvo_7bit_backlight)
167                 return 0x7f;
168         else
169                 return 0x0f;
170 }
171
172 void tahvo_set_backlight_level(int level)
173 {
174         int max_level;
175
176         max_level = tahvo_get_max_backlight_level();
177         if (level > max_level)
178                 level = max_level;
179         tahvo_write_reg(TAHVO_REG_LEDPWMR, level);
180 }
181
182 /*
183  * TAHVO interrupt handler. Only schedules the tasklet.
184  */
185 static irqreturn_t tahvo_irq_handler(int irq, void *dev_id)
186 {
187         tasklet_schedule(&tahvo_tasklet);
188         return IRQ_HANDLED;
189 }
190
191 /*
192  * Tasklet handler
193  */
194 static void tahvo_tasklet_handler(unsigned long data)
195 {
196         struct tahvo_irq_handler_desc *hnd;
197         u16 id;
198         u16 im;
199         int i;
200
201         for (;;) {
202                 id = tahvo_read_reg(TAHVO_REG_IDR);
203                 im = ~tahvo_read_reg(TAHVO_REG_IMR);
204                 id &= im;
205
206                 if (!id)
207                         break;
208
209                 for (i = 0; id != 0; i++, id >>= 1) {
210                         if (!(id & 1))
211                                 continue;
212                         hnd = &tahvo_irq_handlers[i];
213                         if (hnd->func == NULL) {
214                                 /* Spurious tahvo interrupt - just ack it */
215                                 printk(KERN_INFO "Spurious Tahvo interrupt "
216                                                  "(id %d)\n", i);
217                                 tahvo_disable_irq(i);
218                                 tahvo_ack_irq(i);
219                                 continue;
220                         }
221                         hnd->func(hnd->arg);
222                         /*
223                          * Don't acknowledge the interrupt here
224                          * It must be done explicitly
225                          */
226                 }
227         }
228 }
229
230 /*
231  * Register the handler for a given TAHVO interrupt source.
232  */
233 int tahvo_request_irq(int id, void *irq_handler, unsigned long arg, char *name)
234 {
235         struct tahvo_irq_handler_desc *hnd;
236
237         if (irq_handler == NULL || id >= MAX_TAHVO_IRQ_HANDLERS ||
238             name == NULL) {
239                 printk(KERN_ERR PFX "Invalid arguments to %s\n",
240                        __FUNCTION__);
241                 return -EINVAL;
242         }
243         hnd = &tahvo_irq_handlers[id];
244         if (hnd->func != NULL) {
245                 printk(KERN_ERR PFX "IRQ %d already reserved\n", id);
246                 return -EBUSY;
247         }
248         printk(KERN_INFO PFX "Registering interrupt %d for device %s\n",
249                id, name);
250         hnd->func = irq_handler;
251         hnd->arg = arg;
252         strlcpy(hnd->name, name, sizeof(hnd->name));
253
254         tahvo_ack_irq(id);
255         tahvo_enable_irq(id);
256
257         return 0;
258 }
259
260 /*
261  * Unregister the handler for a given TAHVO interrupt source.
262  */
263 void tahvo_free_irq(int id)
264 {
265         struct tahvo_irq_handler_desc *hnd;
266
267         if (id >= MAX_TAHVO_IRQ_HANDLERS) {
268                 printk(KERN_ERR PFX "Invalid argument to %s\n",
269                        __FUNCTION__);
270                 return;
271         }
272         hnd = &tahvo_irq_handlers[id];
273         if (hnd->func == NULL) {
274                 printk(KERN_ERR PFX "IRQ %d already freed\n", id);
275                 return;
276         }
277
278         tahvo_disable_irq(id);
279         hnd->func = NULL;
280 }
281
282 /**
283  * tahvo_probe - Probe for Tahvo ASIC
284  * @dev: the Tahvo device
285  *
286  * Probe for the Tahvo ASIC and allocate memory
287  * for its device-struct if found
288  */
289 static int __devinit tahvo_probe(struct device *dev)
290 {
291         const struct omap_em_asic_bb5_config * em_asic_config;
292         int rev, id, ret;
293
294         /* Prepare tasklet */
295         tasklet_init(&tahvo_tasklet, tahvo_tasklet_handler, 0);
296
297         em_asic_config = omap_get_config(OMAP_TAG_EM_ASIC_BB5,
298                                          struct omap_em_asic_bb5_config);
299         if (em_asic_config == NULL) {
300                 printk(KERN_ERR PFX "Unable to retrieve config data\n");
301                 return -ENODATA;
302         }
303
304         tahvo_initialized = 1;
305
306         rev = tahvo_read_reg(TAHVO_REG_ASICR);
307
308         id = (rev >> 8) & 0xff;
309         if (id == 0x03) {
310                 if ((rev & 0xff) >= 0x50)
311                         tahvo_7bit_backlight = 1;
312         } else if (id == 0x0b) {
313                 tahvo_is_betty = 1;
314                 tahvo_7bit_backlight = 1;
315         } else {
316                 printk(KERN_ERR "Tahvo/Betty chip not found");
317                 return -ENODEV;
318         }
319
320         printk(KERN_INFO "%s v%d.%d found\n", tahvo_is_betty ? "Betty" : "Tahvo",
321                (rev >> 4) & 0x0f, rev & 0x0f);
322
323         tahvo_irq_pin = em_asic_config->tahvo_irq_gpio;
324
325         if ((ret = omap_request_gpio(tahvo_irq_pin)) < 0) {
326                 printk(KERN_ERR PFX "Unable to reserve IRQ GPIO\n");
327                 return ret;
328         }
329
330         /* Set the pin as input */
331         omap_set_gpio_direction(tahvo_irq_pin, 1);
332
333         /* Rising edge triggers the IRQ */
334         set_irq_type(OMAP_GPIO_IRQ(tahvo_irq_pin), IRQT_RISING);
335
336         /* Mask all TAHVO interrupts */
337         tahvo_write_reg(TAHVO_REG_IMR, 0xffff);
338
339         ret = request_irq(OMAP_GPIO_IRQ(tahvo_irq_pin), tahvo_irq_handler, 0,
340                           "tahvo", 0);
341         if (ret < 0) {
342                 printk(KERN_ERR PFX "Unable to register IRQ handler\n");
343                 omap_free_gpio(tahvo_irq_pin);
344                 return ret;
345         }
346 #ifdef CONFIG_CBUS_TAHVO_USER
347         /* Initialize user-space interface */
348         if (tahvo_user_init() < 0) {
349                 printk(KERN_ERR "Unable to initialize driver\n");
350                 free_irq(OMAP_GPIO_IRQ(tahvo_irq_pin), 0);
351                 omap_free_gpio(tahvo_irq_pin);
352                 return ret;
353         }
354 #endif
355         return 0;
356 }
357
358 static int tahvo_remove(struct device *dev)
359 {
360 #ifdef CONFIG_CBUS_TAHVO_USER
361         tahvo_user_cleanup();
362 #endif
363         /* Mask all TAHVO interrupts */
364         tahvo_write_reg(TAHVO_REG_IMR, 0xffff);
365         free_irq(OMAP_GPIO_IRQ(tahvo_irq_pin), 0);
366         omap_free_gpio(tahvo_irq_pin);
367         tasklet_kill(&tahvo_tasklet);
368
369         return 0;
370 }
371
372 static void tahvo_device_release(struct device *dev)
373 {
374         complete(&device_release);
375 }
376
377 static struct device_driver tahvo_driver = {
378         .name           = "tahvo",
379         .bus            = &platform_bus_type,
380         .probe          = tahvo_probe,
381         .remove         = tahvo_remove,
382 };
383
384 static struct platform_device tahvo_device = {
385         .name           = "tahvo",
386         .id             = -1,
387         .dev = {
388                 .release = tahvo_device_release,
389         }
390 };
391
392 /**
393  * tahvo_init - initialise Tahvo driver
394  *
395  * Initialise the Tahvo driver and return 0 if everything worked ok
396  */
397 static int __init tahvo_init(void)
398 {
399         int ret = 0;
400
401         printk(KERN_INFO "Tahvo/Betty driver initialising\n");
402
403         init_completion(&device_release);
404
405         if ((ret = driver_register(&tahvo_driver)) < 0)
406                 return ret;
407
408         if ((ret = platform_device_register(&tahvo_device)) < 0) {
409                 driver_unregister(&tahvo_driver);
410                 return ret;
411         }
412         return 0;
413 }
414
415 /*
416  * Cleanup
417  */
418 static void __exit tahvo_exit(void)
419 {
420         platform_device_unregister(&tahvo_device);
421         driver_unregister(&tahvo_driver);
422         wait_for_completion(&device_release);
423 }
424
425 EXPORT_SYMBOL(tahvo_request_irq);
426 EXPORT_SYMBOL(tahvo_free_irq);
427 EXPORT_SYMBOL(tahvo_enable_irq);
428 EXPORT_SYMBOL(tahvo_disable_irq);
429 EXPORT_SYMBOL(tahvo_ack_irq);
430 EXPORT_SYMBOL(tahvo_read_reg);
431 EXPORT_SYMBOL(tahvo_write_reg);
432 EXPORT_SYMBOL(tahvo_get_backlight_level);
433 EXPORT_SYMBOL(tahvo_get_max_backlight_level);
434 EXPORT_SYMBOL(tahvo_set_backlight_level);
435
436 subsys_initcall(tahvo_init);
437 module_exit(tahvo_exit);
438
439 MODULE_DESCRIPTION("Tahvo ASIC control");
440 MODULE_LICENSE("GPL");
441 MODULE_AUTHOR("Juha Yrjölä, David Weinehall, and Mikko Ylinen");