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