Save sram context after changing MPU, DSP or core clocks
[linux-ginger.git] / drivers / cbus / tahvo-user.c
blobc0e8dafe470d5c30201c0c81a9f03168cbbf3cf2
1 /**
2 * drivers/cbus/tahvo-user.c
4 * Tahvo user space interface functions
6 * Copyright (C) 2004, 2005 Nokia Corporation
8 * Written by Mikko Ylinen <mikko.k.ylinen@nokia.com>
10 * This file is subject to the terms and conditions of the GNU General
11 * Public License. See the file "COPYING" in the main directory of this
12 * archive for more details.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 #include <linux/types.h>
25 #include <linux/kernel.h>
26 #include <linux/interrupt.h>
27 #include <linux/module.h>
28 #include <linux/init.h>
29 #include <linux/fs.h>
30 #include <linux/miscdevice.h>
31 #include <linux/poll.h>
32 #include <linux/list.h>
33 #include <linux/spinlock.h>
34 #include <linux/sched.h>
35 #include <linux/mutex.h>
37 #include <asm/uaccess.h>
39 #include "tahvo.h"
41 #include "user_retu_tahvo.h"
43 /* Maximum size of IRQ node buffer/pool */
44 #define TAHVO_MAX_IRQ_BUF_LEN 16
46 #define PFX "tahvo-user: "
48 /* Bitmap for marking the interrupt sources as having the handlers */
49 static u32 tahvo_irq_bits;
51 /* For allowing only one user process to subscribe to the tahvo interrupts */
52 static struct file *tahvo_irq_subscr = NULL;
54 /* For poll and IRQ passing */
55 struct tahvo_irq {
56 u32 id;
57 struct list_head node;
60 static spinlock_t tahvo_irqs_lock;
61 static struct tahvo_irq *tahvo_irq_block;
62 static LIST_HEAD(tahvo_irqs);
63 static LIST_HEAD(tahvo_irqs_reserve);
65 /* Wait queue - used when user wants to read the device */
66 DECLARE_WAIT_QUEUE_HEAD(tahvo_user_waitqueue);
68 /* Semaphore to protect irq subscription sequence */
69 static struct mutex tahvo_mutex;
71 /* This array specifies TAHVO register types (read/write/toggle) */
72 static const u8 tahvo_access_bits[] = {
90 * The handler for all TAHVO interrupts.
92 * arg is the interrupt source in TAHVO.
94 static void tahvo_user_irq_handler(unsigned long arg)
96 struct tahvo_irq *irq;
98 /* user has to re-enable the interrupt once ready
99 * for receiving them again */
100 tahvo_disable_irq(arg);
101 tahvo_ack_irq(arg);
103 spin_lock(&tahvo_irqs_lock);
104 if (list_empty(&tahvo_irqs_reserve)) {
105 spin_unlock(&tahvo_irqs_lock);
106 return;
108 irq = list_entry((&tahvo_irqs_reserve)->next, struct tahvo_irq, node);
109 irq->id = arg;
110 list_move_tail(&irq->node, &tahvo_irqs);
111 spin_unlock(&tahvo_irqs_lock);
113 /* wake up waiting thread */
114 wake_up(&tahvo_user_waitqueue);
118 * This routine sets up the interrupt handler and marks an interrupt source
119 * in TAHVO as a candidate for signal delivery to the user process.
121 static int tahvo_user_subscribe_to_irq(int id, struct file *filp)
123 int ret;
125 mutex_lock(&tahvo_mutex);
126 if ((tahvo_irq_subscr != NULL) && (tahvo_irq_subscr != filp)) {
127 mutex_unlock(&tahvo_mutex);
128 return -EBUSY;
130 /* Store the file pointer of the first user process registering IRQs */
131 tahvo_irq_subscr = filp;
132 mutex_unlock(&tahvo_mutex);
134 if (tahvo_irq_bits & (1 << id))
135 return 0;
137 ret = tahvo_request_irq(id, tahvo_user_irq_handler, id, "");
138 if (ret < 0)
139 return ret;
141 /* Mark that this interrupt has a handler */
142 tahvo_irq_bits |= 1 << id;
144 return 0;
148 * Unregister all TAHVO interrupt handlers
150 static void tahvo_unreg_irq_handlers(void)
152 int id;
154 if (!tahvo_irq_bits)
155 return;
157 for (id = 0; id < MAX_TAHVO_IRQ_HANDLERS; id++)
158 if (tahvo_irq_bits & (1 << id))
159 tahvo_free_irq(id);
161 tahvo_irq_bits = 0;
165 * Write to TAHVO register.
166 * Returns 0 upon success, a negative error value otherwise.
168 static int tahvo_user_write_with_mask(u32 field, u16 value)
170 u32 mask;
171 u32 reg;
172 u_short tmp;
173 unsigned long flags;
175 mask = MASK(field);
176 reg = REG(field);
178 /* Detect bad mask and reg */
179 if (mask == 0 || reg > TAHVO_REG_MAX ||
180 tahvo_access_bits[reg] == READ_ONLY) {
181 printk(KERN_ERR PFX "invalid arguments (reg=%#x, mask=%#x)\n",
182 reg, mask);
183 return -EINVAL;
186 /* Justify value according to mask */
187 while (!(mask & 1)) {
188 value = value << 1;
189 mask = mask >> 1;
192 spin_lock_irqsave(&tahvo_lock, flags);
193 if (tahvo_access_bits[reg] == TOGGLE) {
194 /* No need to detect previous content of register */
195 tmp = 0;
196 } else {
197 /* Read current value of register */
198 tmp = tahvo_read_reg(reg);
200 /* Generate a new value */
201 tmp = (tmp & ~MASK(field)) | (value & MASK(field));
202 /* Write data to TAHVO */
203 tahvo_write_reg(reg, tmp);
204 spin_unlock_irqrestore(&tahvo_lock, flags);
206 return 0;
210 * Read TAHVO register.
212 static u32 tahvo_user_read_with_mask(u32 field)
214 u_short value;
215 u32 mask, reg;
217 mask = MASK(field);
218 reg = REG(field);
220 /* Detect bad mask and reg */
221 if (mask == 0 || reg > TAHVO_REG_MAX) {
222 printk(KERN_ERR PFX "invalid arguments (reg=%#x, mask=%#x)\n",
223 reg, mask);
224 return -EINVAL;
227 /* Read the register */
228 value = tahvo_read_reg(reg) & mask;
230 /* Right justify value */
231 while (!(mask & 1)) {
232 value = value >> 1;
233 mask = mask >> 1;
236 return value;
240 * Close device
242 static int tahvo_close(struct inode *inode, struct file *filp)
244 /* Unregister all interrupts that have been registered */
245 if (tahvo_irq_subscr == filp) {
246 tahvo_unreg_irq_handlers();
247 tahvo_irq_subscr = NULL;
250 return 0;
254 * Device control (ioctl)
256 static int tahvo_ioctl(struct inode *inode, struct file *filp,
257 unsigned int cmd, unsigned long arg)
259 struct retu_tahvo_write_parms par;
260 int ret;
262 switch (cmd) {
263 case URT_IOCT_IRQ_SUBSCR:
264 return tahvo_user_subscribe_to_irq(arg, filp);
265 case TAHVO_IOCH_READ:
266 return tahvo_user_read_with_mask(arg);
267 case TAHVO_IOCX_WRITE:
268 ret = copy_from_user(&par, (void __user *) arg, sizeof(par));
269 if (ret)
270 printk(KERN_ERR "copy_from_user failed: %d\n", ret);
271 par.result = tahvo_user_write_with_mask(par.field, par.value);
272 ret = copy_to_user((void __user *) arg, &par, sizeof(par));
273 if (ret)
274 printk(KERN_ERR "copy_to_user failed: %d\n", ret);
275 break;
276 default:
277 return -ENOIOCTLCMD;
279 return 0;
283 * Read from device
285 static ssize_t tahvo_read(struct file *filp, char *buf, size_t count,
286 loff_t * offp)
288 struct tahvo_irq *irq;
290 u32 nr, i;
292 /* read not permitted if neither filp nor anyone has registered IRQs */
293 if (tahvo_irq_subscr != filp)
294 return -EPERM;
296 if ((count < sizeof(u32)) || ((count % sizeof(u32)) != 0))
297 return -EINVAL;
299 nr = count / sizeof(u32);
301 for (i = 0; i < nr; i++) {
302 unsigned long flags;
303 u32 irq_id;
304 int ret;
306 ret = wait_event_interruptible(tahvo_user_waitqueue,
307 !list_empty(&tahvo_irqs));
308 if (ret < 0)
309 return ret;
311 spin_lock_irqsave(&tahvo_irqs_lock, flags);
312 irq = list_entry((&tahvo_irqs)->next, struct tahvo_irq, node);
313 irq_id = irq->id;
314 list_move(&irq->node, &tahvo_irqs_reserve);
315 spin_unlock_irqrestore(&tahvo_irqs_lock, flags);
317 ret = copy_to_user(buf + i * sizeof(irq_id), &irq_id,
318 sizeof(irq_id));
319 if (ret)
320 printk(KERN_ERR "copy_to_user failed: %d\n", ret);
323 return count;
327 * Poll method
329 static unsigned tahvo_poll(struct file *filp, struct poll_table_struct *pt)
331 if (!list_empty(&tahvo_irqs))
332 return POLLIN;
334 poll_wait(filp, &tahvo_user_waitqueue, pt);
336 if (!list_empty(&tahvo_irqs))
337 return POLLIN;
338 else
339 return 0;
342 static struct file_operations tahvo_user_fileops = {
343 .owner = THIS_MODULE,
344 .ioctl = tahvo_ioctl,
345 .read = tahvo_read,
346 .release = tahvo_close,
347 .poll = tahvo_poll
350 static struct miscdevice tahvo_device = {
351 .minor = MISC_DYNAMIC_MINOR,
352 .name = "tahvo",
353 .fops = &tahvo_user_fileops
357 * Initialization
359 * @return 0 if successful, error value otherwise.
361 int tahvo_user_init(void)
363 struct tahvo_irq *irq;
364 int res, i;
366 irq = kmalloc(sizeof(*irq) * TAHVO_MAX_IRQ_BUF_LEN, GFP_KERNEL);
367 if (irq == NULL) {
368 printk(KERN_ERR PFX "kmalloc failed\n");
369 return -ENOMEM;
371 memset(irq, 0, sizeof(*irq) * TAHVO_MAX_IRQ_BUF_LEN);
372 for (i = 0; i < TAHVO_MAX_IRQ_BUF_LEN; i++)
373 list_add(&irq[i].node, &tahvo_irqs_reserve);
375 tahvo_irq_block = irq;
377 spin_lock_init(&tahvo_irqs_lock);
378 mutex_init(&tahvo_mutex);
380 /* Request a misc device */
381 res = misc_register(&tahvo_device);
382 if (res < 0) {
383 printk(KERN_ERR PFX "unable to register misc device for %s\n",
384 tahvo_device.name);
385 kfree(irq);
386 return res;
389 return 0;
393 * Cleanup.
395 void tahvo_user_cleanup(void)
397 /* Unregister our misc device */
398 misc_deregister(&tahvo_device);
399 /* Unregister and disable all TAHVO interrupts */
400 tahvo_unreg_irq_handlers();
401 kfree(tahvo_irq_block);
404 MODULE_DESCRIPTION("Tahvo ASIC user space functions");
405 MODULE_LICENSE("GPL");
406 MODULE_AUTHOR("Mikko Ylinen");