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