~ubuntu-branches/ubuntu/precise/linux-ti-omap4/precise

« back to all changes in this revision

Viewing changes to drivers/dsp/syslink/multicore_ipc/ipc_drv.c

  • Committer: Bazaar Package Importer
  • Author(s): Paolo Pisati
  • Date: 2011-06-29 15:23:51 UTC
  • mfrom: (26.1.1 natty-proposed)
  • Revision ID: james.westby@ubuntu.com-20110629152351-xs96tm303d95rpbk
Tags: 3.0.0-1200.2
* Rebased against 3.0.0-6.7
* BSP from TI based on 3.0.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/*
2
 
 *  ipc_drv.c
3
 
 *
4
 
 *  IPC driver module.
5
 
 *
6
 
 *  Copyright (C) 2008-2009 Texas Instruments, Inc.
7
 
 *
8
 
 *  This package is free software; you can redistribute it and/or modify
9
 
 *  it under the terms of the GNU General Public License version 2 as
10
 
 *  published by the Free Software Foundation.
11
 
 *
12
 
 *  THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
13
 
 *  IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
14
 
 *  WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
15
 
 *  PURPOSE.
16
 
 */
17
 
 
18
 
#include <linux/module.h>
19
 
#include <linux/device.h>
20
 
#include <linux/init.h>
21
 
#include <linux/types.h>
22
 
#include <linux/kdev_t.h>
23
 
#include <linux/fs.h>
24
 
#include <linux/moduleparam.h>
25
 
#include <linux/cdev.h>
26
 
#include <linux/uaccess.h>
27
 
#include <linux/slab.h>
28
 
#include <linux/notifier.h>
29
 
#include <ipc_ioctl.h>
30
 
#include <ipc.h>
31
 
#include <drv_notify.h>
32
 
#include <notify_ioctl.h>
33
 
#include <nameserver.h>
34
 
#ifdef CONFIG_SYSLINK_RECOVERY
35
 
#include <plat/iommu.h>
36
 
#include <plat/remoteproc.h>
37
 
#endif
38
 
 
39
 
#define IPC_NAME                "syslink_ipc"
40
 
#define IPC_MAJOR               0
41
 
#define IPC_MINOR               0
42
 
#define IPC_DEVICES             1
43
 
 
44
 
#define ipc_release_resource(x, y, z) (ipc_ioc_router(x, y, z, false))
45
 
 
46
 
struct ipc_device {
47
 
        struct cdev cdev;
48
 
 
49
 
        struct blocking_notifier_head   notifier;
50
 
};
51
 
 
52
 
static struct ipc_device *ipc_device;
53
 
static struct class *ipc_class;
54
 
 
55
 
static s32 ipc_major = IPC_MAJOR;
56
 
static s32 ipc_minor = IPC_MINOR;
57
 
static char *ipc_name = IPC_NAME;
58
 
 
59
 
module_param(ipc_name, charp, 0);
60
 
MODULE_PARM_DESC(ipc_name, "Device name, default = syslink_ipc");
61
 
 
62
 
module_param(ipc_major, int, 0);        /* Driver's major number */
63
 
MODULE_PARM_DESC(ipc_major, "Major device number, default = 0 (auto)");
64
 
 
65
 
module_param(ipc_minor, int, 0);        /* Driver's minor number */
66
 
MODULE_PARM_DESC(ipc_minor, "Minor device number, default = 0 (auto)");
67
 
 
68
 
MODULE_AUTHOR("Texas Instruments");
69
 
MODULE_LICENSE("GPL v2");
70
 
 
71
 
#ifdef CONFIG_SYSLINK_RECOVERY
72
 
#define REC_TIMEOUT 5000       /* recovery timeout in msecs */
73
 
static atomic_t ipc_cref;       /* number of ipc open handles */
74
 
static struct workqueue_struct *ipc_rec_queue;
75
 
static struct work_struct ipc_recovery_work;
76
 
static DECLARE_COMPLETION(ipc_comp);
77
 
static DECLARE_COMPLETION(ipc_open_comp);
78
 
static bool recover;
79
 
static struct iommu *piommu;
80
 
static struct omap_rproc *prproc_sysm3;
81
 
 
82
 
static int ipc_ducati_iommu_notifier_call(struct notifier_block *nb,
83
 
                                                unsigned long val, void *v);
84
 
 
85
 
static int ipc_sysm3_rproc_notifier_call(struct notifier_block *nb,
86
 
                                                unsigned long val, void *v);
87
 
 
88
 
static struct notifier_block ipc_notify_nb_iommu_ducati = {
89
 
        .notifier_call = ipc_ducati_iommu_notifier_call,
90
 
};
91
 
 
92
 
static struct notifier_block ipc_notify_nb_rproc_ducati0 = {
93
 
        .notifier_call = ipc_sysm3_rproc_notifier_call,
94
 
};
95
 
 
96
 
static void ipc_recover(struct work_struct *work)
97
 
{
98
 
        if (atomic_read(&ipc_cref)) {
99
 
                INIT_COMPLETION(ipc_comp);
100
 
                while (!wait_for_completion_timeout(&ipc_comp,
101
 
                                                msecs_to_jiffies(REC_TIMEOUT)))
102
 
                        pr_info("%s:%d handle(s) still opened\n",
103
 
                                        __func__, atomic_read(&ipc_cref));
104
 
        }
105
 
        recover = false;
106
 
        complete_all(&ipc_open_comp);
107
 
}
108
 
 
109
 
void ipc_recover_schedule(void)
110
 
{
111
 
        INIT_COMPLETION(ipc_open_comp);
112
 
        recover = true;
113
 
        queue_work(ipc_rec_queue, &ipc_recovery_work);
114
 
}
115
 
EXPORT_SYMBOL_GPL(ipc_recover_schedule);
116
 
 
117
 
static int ipc_ducati_iommu_notifier_call(struct notifier_block *nb,
118
 
                                                unsigned long val, void *v)
119
 
{
120
 
        switch ((int)val) {
121
 
        case IOMMU_FAULT:
122
 
                ipc_recover_schedule();
123
 
                return 0;
124
 
        default:
125
 
                return 0;
126
 
        }
127
 
}
128
 
 
129
 
static int ipc_sysm3_rproc_notifier_call(struct notifier_block *nb,
130
 
                                                unsigned long val, void *v)
131
 
{
132
 
        switch ((int)val) {
133
 
        case OMAP_RPROC_START:
134
 
                piommu = iommu_get("ducati");
135
 
                if (piommu != ERR_PTR(-ENODEV) &&
136
 
                        piommu != ERR_PTR(-EINVAL))
137
 
                                iommu_register_notifier(piommu,
138
 
                                                &ipc_notify_nb_iommu_ducati);
139
 
                else
140
 
                        piommu = NULL;
141
 
                return 0;
142
 
        case OMAP_RPROC_STOP:
143
 
                if (piommu != NULL) {
144
 
                        iommu_unregister_notifier(piommu,
145
 
                                                &ipc_notify_nb_iommu_ducati);
146
 
                        iommu_put(piommu);
147
 
                        piommu = NULL;
148
 
                }
149
 
                return 0;
150
 
        default:
151
 
                return 0;
152
 
        }
153
 
}
154
 
 
155
 
bool ipc_recovering()
156
 
{
157
 
        return recover;
158
 
}
159
 
#endif
160
 
 
161
 
/*
162
 
 * ======== ipc_open ========
163
 
 *  This function is invoked when an application
164
 
 *  opens handle to the ipc driver
165
 
 */
166
 
static int ipc_open(struct inode *inode, struct file *filp)
167
 
{
168
 
        s32 retval = 0;
169
 
        struct ipc_device *dev;
170
 
        struct ipc_process_context *pr_ctxt = NULL;
171
 
 
172
 
#ifdef CONFIG_SYSLINK_RECOVERY
173
 
        if (recover) {
174
 
                if (filp->f_flags & O_NONBLOCK ||
175
 
                        wait_for_completion_killable(&ipc_open_comp))
176
 
                        return -EBUSY;
177
 
        }
178
 
#endif
179
 
 
180
 
        pr_ctxt = kzalloc(sizeof(struct ipc_process_context), GFP_KERNEL);
181
 
        if (!pr_ctxt)
182
 
                retval = -ENOMEM;
183
 
        else {
184
 
                INIT_LIST_HEAD(&pr_ctxt->resources);
185
 
                spin_lock_init(&pr_ctxt->res_lock);
186
 
                dev = container_of(inode->i_cdev, struct ipc_device,
187
 
                                        cdev);
188
 
                pr_ctxt->dev = dev;
189
 
                filp->private_data = pr_ctxt;
190
 
        }
191
 
 
192
 
#ifdef CONFIG_SYSLINK_RECOVERY
193
 
        if (!retval)
194
 
                atomic_inc(&ipc_cref);
195
 
#endif
196
 
 
197
 
        return retval;
198
 
}
199
 
 
200
 
/*
201
 
 * ======== ipc_release ========
202
 
 *  This function is invoked when an application
203
 
 *  closes handle to the ipc driver
204
 
 */
205
 
static int ipc_release(struct inode *inode, struct file *filp)
206
 
{
207
 
        s32 retval = 0;
208
 
        struct ipc_process_context *pr_ctxt;
209
 
        struct resource_info *info = NULL, *temp = NULL;
210
 
 
211
 
        if (!filp->private_data) {
212
 
                retval = -EIO;
213
 
                goto err;
214
 
        }
215
 
 
216
 
        ipc_notify_event(IPC_CLOSE, (void *)NULL);
217
 
 
218
 
        pr_ctxt = filp->private_data;
219
 
 
220
 
        list_for_each_entry_safe(info, temp, &pr_ctxt->resources, res) {
221
 
                retval = ipc_release_resource(info->cmd, (ulong)info->data,
222
 
                                        filp);
223
 
        }
224
 
 
225
 
        list_del(&pr_ctxt->resources);
226
 
        kfree(pr_ctxt);
227
 
 
228
 
        filp->private_data = NULL;
229
 
err:
230
 
#ifdef CONFIG_SYSLINK_RECOVERY
231
 
        if (!atomic_dec_return(&ipc_cref))
232
 
                complete(&ipc_comp);
233
 
#endif
234
 
        return retval;
235
 
}
236
 
 
237
 
/*
238
 
 * ======== ipc_ioctl ========
239
 
 *  This function  provides IO interface  to the
240
 
 *  ipc driver
241
 
 */
242
 
static long ipc_ioctl(struct file *filp, u32 cmd, ulong arg)
243
 
{
244
 
        s32 retval = 0;
245
 
        void __user *argp = (void __user *)arg;
246
 
 
247
 
        /* Verify the memory and ensure that it is not is kernel
248
 
             address space
249
 
        */
250
 
        if (_IOC_DIR(cmd) & _IOC_READ)
251
 
                retval = !access_ok(VERIFY_WRITE, argp, _IOC_SIZE(cmd));
252
 
        else if (_IOC_DIR(cmd) & _IOC_WRITE)
253
 
                retval = !access_ok(VERIFY_READ, argp, _IOC_SIZE(cmd));
254
 
 
255
 
        if (retval) {
256
 
                retval = -EFAULT;
257
 
                goto exit;
258
 
        }
259
 
 
260
 
        retval = ipc_ioc_router(cmd, (ulong)argp, filp, true);
261
 
        return retval;
262
 
 
263
 
exit:
264
 
        return retval;
265
 
}
266
 
 
267
 
static const struct file_operations ipc_fops = {
268
 
        .open = ipc_open,
269
 
        .release = ipc_release,
270
 
        .unlocked_ioctl = ipc_ioctl,
271
 
        .read = notify_drv_read,
272
 
        .mmap = notify_drv_mmap,
273
 
};
274
 
 
275
 
/*
276
 
 * ======== ipc_notify_event ========
277
 
 *  IPC event notifications.
278
 
 */
279
 
int ipc_notify_event(int event, void *data)
280
 
{
281
 
        return blocking_notifier_call_chain(&ipc_device->notifier, event, data);
282
 
}
283
 
 
284
 
/*
285
 
 * ======== ipc_register_notifier ========
286
 
 *  Register for IPC events.
287
 
 */
288
 
int ipc_register_notifier(struct notifier_block *nb)
289
 
{
290
 
        if (!nb)
291
 
                return -EINVAL;
292
 
        return blocking_notifier_chain_register(&ipc_device->notifier, nb);
293
 
}
294
 
EXPORT_SYMBOL_GPL(ipc_register_notifier);
295
 
 
296
 
/*
297
 
 * ======== ipc_unregister_notifier ========
298
 
 *  Un-register for IPC events.
299
 
 */
300
 
int ipc_unregister_notifier(struct notifier_block *nb)
301
 
{
302
 
        if (!nb)
303
 
                return -EINVAL;
304
 
        return blocking_notifier_chain_unregister(&ipc_device->notifier, nb);
305
 
}
306
 
EXPORT_SYMBOL_GPL(ipc_unregister_notifier);
307
 
 
308
 
/*
309
 
 * ======== ipc_modules_init ========
310
 
 *  IPC Initialization routine. will initialize various
311
 
 *  sub components (modules) of IPC.
312
 
 */
313
 
static int ipc_modules_init(void)
314
 
{
315
 
        /* Setup the notify_drv module */
316
 
        _notify_drv_setup();
317
 
 
318
 
#ifdef CONFIG_SYSLINK_RECOVERY
319
 
        ipc_rec_queue = create_workqueue("ipc_rec_queue");
320
 
        INIT_WORK(&ipc_recovery_work, ipc_recover);
321
 
        INIT_COMPLETION(ipc_comp);
322
 
 
323
 
        prproc_sysm3 = omap_rproc_get("ducati-proc0");
324
 
        if (prproc_sysm3 != ERR_PTR(-ENODEV))
325
 
                omap_rproc_register_notifier(prproc_sysm3,
326
 
                                                &ipc_notify_nb_rproc_ducati0);
327
 
        else
328
 
                prproc_sysm3 = NULL;
329
 
#endif
330
 
 
331
 
        return 0;
332
 
}
333
 
 
334
 
/*
335
 
 * ======== ipc_modules_exit ========
336
 
 *  IPC cleanup routine. will cleanup of various
337
 
 *  sub components (modules) of IPC.
338
 
 */
339
 
static void ipc_modules_exit(void)
340
 
{
341
 
#ifdef CONFIG_SYSLINK_RECOVERY
342
 
        if (prproc_sysm3) {
343
 
                omap_rproc_unregister_notifier(prproc_sysm3,
344
 
                                                &ipc_notify_nb_rproc_ducati0);
345
 
                omap_rproc_put(prproc_sysm3);
346
 
        }
347
 
        destroy_workqueue(ipc_rec_queue);
348
 
#endif
349
 
        /* Destroy the notify_drv module */
350
 
        _notify_drv_destroy();
351
 
}
352
 
 
353
 
/*
354
 
 * ======== ipc_init ========
355
 
 *  Initialization routine. Executed when the driver is
356
 
 *  loaded (as a kernel module), or when the system
357
 
 *  is booted (when included as part of the kernel
358
 
 *  image).
359
 
 */
360
 
static int __init ipc_init(void)
361
 
{
362
 
        dev_t dev ;
363
 
        s32 retval = 0;
364
 
 
365
 
        retval = alloc_chrdev_region(&dev, ipc_minor, IPC_DEVICES,
366
 
                                                        ipc_name);
367
 
        ipc_major = MAJOR(dev);
368
 
 
369
 
        if (retval < 0) {
370
 
                pr_err("ipc_init: can't get major %x\n", ipc_major);
371
 
                goto exit;
372
 
        }
373
 
 
374
 
        ipc_device = kmalloc(sizeof(struct ipc_device), GFP_KERNEL);
375
 
        if (!ipc_device) {
376
 
                pr_err("ipc_init: memory allocation failed for ipc_device\n");
377
 
                retval = -ENOMEM;
378
 
                goto unreg_exit;
379
 
        }
380
 
 
381
 
        memset(ipc_device, 0, sizeof(struct ipc_device));
382
 
 
383
 
        BLOCKING_INIT_NOTIFIER_HEAD(&ipc_device->notifier);
384
 
 
385
 
        retval = ipc_modules_init();
386
 
        if (retval) {
387
 
                pr_err("ipc_init: ipc initialization failed\n");
388
 
                goto unreg_exit;
389
 
 
390
 
        }
391
 
        ipc_class = class_create(THIS_MODULE, "syslink_ipc");
392
 
        if (IS_ERR(ipc_class)) {
393
 
                pr_err("ipc_init: error creating ipc class\n");
394
 
                retval = PTR_ERR(ipc_class);
395
 
                goto unreg_exit;
396
 
        }
397
 
 
398
 
        device_create(ipc_class, NULL, MKDEV(ipc_major, ipc_minor), NULL,
399
 
                                                                ipc_name);
400
 
        cdev_init(&ipc_device->cdev, &ipc_fops);
401
 
        ipc_device->cdev.owner = THIS_MODULE;
402
 
        retval = cdev_add(&ipc_device->cdev, dev, IPC_DEVICES);
403
 
        if (retval) {
404
 
                pr_err("ipc_init: failed to add the ipc device\n");
405
 
                goto class_exit;
406
 
        }
407
 
        return retval;
408
 
 
409
 
class_exit:
410
 
        class_destroy(ipc_class);
411
 
 
412
 
unreg_exit:
413
 
        unregister_chrdev_region(dev, IPC_DEVICES);
414
 
        kfree(ipc_device);
415
 
 
416
 
exit:
417
 
        return retval;
418
 
}
419
 
 
420
 
/*
421
 
 * ======== ipc_exit ========
422
 
 *  This function is invoked during unlinking of ipc
423
 
 *  module from the kernel. ipc resources are
424
 
 *  freed in this function.
425
 
 */
426
 
static void __exit ipc_exit(void)
427
 
{
428
 
        dev_t devno;
429
 
 
430
 
        ipc_modules_exit();
431
 
        devno = MKDEV(ipc_major, ipc_minor);
432
 
        if (ipc_device) {
433
 
                cdev_del(&ipc_device->cdev);
434
 
                kfree(ipc_device);
435
 
        }
436
 
        unregister_chrdev_region(devno, IPC_DEVICES);
437
 
        if (ipc_class) {
438
 
                /* remove the device from sysfs */
439
 
                device_destroy(ipc_class, MKDEV(ipc_major, ipc_minor));
440
 
                class_destroy(ipc_class);
441
 
        }
442
 
}
443
 
 
444
 
/*
445
 
 *  ipc driver initialization and de-initialization functions
446
 
 */
447
 
module_init(ipc_init);
448
 
module_exit(ipc_exit);