2 * linux/arch/arm/mach-omap/dsp/dsp_ctl.c
4 * OMAP DSP control device driver
6 * Copyright (C) 2002-2005 Nokia Corporation
8 * Written by Toshihiro Kobayashi <toshihiro.kobayashi@nokia.com>
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 * 2005/06/09: DSP Gateway version 3.3
27 #include <linux/module.h>
28 #include <linux/slab.h>
29 #include <linux/major.h>
31 #include <linux/device.h>
32 #include <linux/proc_fs.h>
33 #include <linux/init.h>
34 #include <linux/sched.h>
35 #include <linux/delay.h>
36 #include <linux/platform_device.h>
37 #include <linux/clk.h>
38 #include <linux/mutex.h>
39 #include <asm/uaccess.h>
41 #include <asm/ioctls.h>
42 #include <asm/arch/dsp.h>
43 #include "hardware_dsp.h"
47 static ssize_t loadinfo_show(struct device *dev, struct device_attribute *attr,
49 static struct device_attribute dev_attr_loadinfo = __ATTR_RO(loadinfo);
50 extern struct device_attribute dev_attr_ipbuf;
58 static DECLARE_WAIT_QUEUE_HEAD(ioctl_wait_q);
59 static unsigned short ioctl_wait_cmd;
60 static DEFINE_MUTEX(ioctl_lock);
62 static unsigned char n_stask;
67 static short varread_val[5]; /* maximum */
69 static int dsp_regread(unsigned short cmd_l, unsigned short adr,
75 if (mutex_lock_interruptible(&ioctl_lock))
78 ioctl_wait_cmd = MBCMD(REGRW);
79 mbcmd_set(mb, MBCMD(REGRW), cmd_l, adr);
80 dsp_mbcmd_send_and_wait(&mb, &ioctl_wait_q);
82 if (ioctl_wait_cmd != 0) {
83 printk(KERN_ERR "omapdsp: register read error!\n");
88 *val = varread_val[0];
91 mutex_unlock(&ioctl_lock);
95 static int dsp_regwrite(unsigned short cmd_l, unsigned short adr,
99 struct mb_exarg arg = {
100 .tid = OMAP_DSP_TID_ANON,
105 mbcmd_set(mb, MBCMD(REGRW), cmd_l, adr);
106 dsp_mbcmd_send_exarg(&mb, &arg);
110 static int dsp_getvar(unsigned char varid, unsigned short *val, int sz)
115 if (mutex_lock_interruptible(&ioctl_lock))
118 ioctl_wait_cmd = MBCMD(GETVAR);
119 mbcmd_set(mb, MBCMD(GETVAR), varid, 0);
120 dsp_mbcmd_send_and_wait(&mb, &ioctl_wait_q);
122 if (ioctl_wait_cmd != 0) {
123 printk(KERN_ERR "omapdsp: variable read error!\n");
128 memcpy(val, varread_val, sz * sizeof(short));
131 mutex_unlock(&ioctl_lock);
135 static int dsp_setvar(unsigned char varid, unsigned short val)
137 dsp_mbsend(MBCMD(SETVAR), varid, val);
141 static int dspcfg(void)
146 if (mutex_lock_interruptible(&ioctl_lock))
149 if (cfgstat != CFG_ERR) {
151 "omapdsp: DSP has been already configured. "
158 dsp_mem_usecount_clear();
161 * DSPCFG command and dsp_mem_start() must be called
162 * while internal mem is on.
164 dsp_mem_enable((void *)dspmem_base);
172 ioctl_wait_cmd = MBCMD(DSPCFG);
173 mbcmd_set(mb, MBCMD(DSPCFG), OMAP_DSP_MBCMD_DSPCFG_REQ, 0);
174 dsp_mbcmd_send_and_wait(&mb, &ioctl_wait_q);
176 if (ioctl_wait_cmd != 0) {
177 printk(KERN_ERR "omapdsp: configuration error!\n");
183 #ifdef OLD_BINARY_SUPPORT
185 * MBREV 3.2 or earlier doesn't assume DMA domain is on
186 * when DSPCFG command is sent
188 if ((mbx_revision == MBREV_3_0) ||
189 (mbx_revision == MBREV_3_2)) {
190 ret = dsp_mbsend(MBCMD(PM), OMAP_DSP_MBCMD_PM_ENABLE,
191 DSPREG_ICR_DMA_IDLE_DOMAIN);
195 if ((ret = dsp_task_config_all(n_stask)) < 0) {
196 mutex_unlock(&ioctl_lock);
198 dsp_mem_disable((void *)dspmem_base);
205 if ((ret = dsp_setvar(OMAP_DSP_MBCMD_VARID_ICRMASK,
206 dsp_cpustat_get_icrmask())) < 0)
209 /* create runtime sysfs entries */
210 device_create_file(&dsp_device.dev, &dev_attr_loadinfo);
211 device_create_file(&dsp_device.dev, &dev_attr_ipbuf);
214 dsp_mem_disable((void *)dspmem_base);
215 mutex_unlock(&ioctl_lock);
221 if (dsp_taskmod_busy()) {
222 printk(KERN_WARNING "omapdsp: tasks are busy.\n");
226 if (mutex_lock_interruptible(&ioctl_lock))
229 /* FIXME: lock task module */
231 /* remove runtime sysfs entries */
232 device_remove_file(&dsp_device.dev, &dev_attr_loadinfo);
233 device_remove_file(&dsp_device.dev, &dev_attr_ipbuf);
240 dsp_task_unconfig_all();
244 mutex_unlock(&ioctl_lock);
248 int dsp_is_ready(void)
250 return (cfgstat == CFG_READY) ? 1 : 0;
261 if (mutex_lock_interruptible(&ioctl_lock))
264 ioctl_wait_cmd = MBCMD(POLL);
265 mbcmd_set(mb, MBCMD(POLL), 0, 0);
266 dsp_mbcmd_send_and_wait(&mb, &ioctl_wait_q);
268 if (ioctl_wait_cmd != 0) {
269 printk(KERN_ERR "omapdsp: poll error!\n");
275 mutex_unlock(&ioctl_lock);
279 void dsp_runlevel(unsigned char level)
281 if (level == OMAP_DSP_MBCMD_RUNLEVEL_RECOVERY)
282 dsp_mbsend_recovery(MBCMD(RUNLEVEL), level, 0);
284 dsp_mbsend(MBCMD(RUNLEVEL), level, 0);
287 static enum cfgstat cfgstat_save_suspend;
290 * suspend / resume callbacks
291 * DSP is not reset within this code, but done in omap_pm_suspend.
292 * so if these functions are called as OMAP_DSP_IOCTL_SUSPEND,
293 * DSP should be reset / unreset out of these functions.
295 int dsp_suspend(void)
300 if (cfgstat == CFG_SUSPEND) {
301 printk(KERN_ERR "omapdsp: DSP is already in suspend state.\n");
305 if (mutex_lock_interruptible(&ioctl_lock))
308 cfgstat_save_suspend = cfgstat;
309 if (!dsp_is_ready()) {
310 if (dsp_cpustat_get_stat() == CPUSTAT_RUN) {
312 "omapdsp: illegal operation: trying suspend DSP "
313 "while it is running but has not configured "
315 " Resetting DSP...\n");
320 ioctl_wait_cmd = MBCMD(SUSPEND);
321 mbcmd_set(mb, MBCMD(SUSPEND), 0, 0);
322 dsp_mbcmd_send_and_wait(&mb, &ioctl_wait_q);
324 if (ioctl_wait_cmd != 0) {
325 printk(KERN_ERR "omapdsp: DSP suspend error!\n");
332 cfgstat = CFG_SUSPEND;
334 mutex_unlock(&ioctl_lock);
340 if (cfgstat != CFG_SUSPEND) {
341 printk(KERN_ERR "omapdsp: DSP is not in suspend state.\n");
345 cfgstat = cfgstat_save_suspend;
349 static void dsp_fbctl_enable(void)
351 dsp_mbsend(MBCMD(KFUNC), OMAP_DSP_MBCMD_KFUNC_FBCTL,
352 OMAP_DSP_MBCMD_FBCTL_ENABLE);
355 static int dsp_fbctl_disable(void)
360 if (mutex_lock_interruptible(&ioctl_lock))
363 ioctl_wait_cmd = MBCMD(KFUNC);
364 mbcmd_set(mb, MBCMD(KFUNC), OMAP_DSP_MBCMD_KFUNC_FBCTL,
365 OMAP_DSP_MBCMD_FBCTL_DISABLE);
366 dsp_mbcmd_send_and_wait(&mb, &ioctl_wait_q);
367 if (ioctl_wait_cmd != 0) {
368 printk(KERN_ERR "omapdsp: fb disable error!\n");
371 mutex_unlock(&ioctl_lock);
377 * DSP control device file operations
379 static int dsp_ctl_ioctl(struct inode *inode, struct file *file,
380 unsigned int cmd, unsigned long arg)
386 * command level 1: commands which don't need lock
388 case OMAP_DSP_IOCTL_RUN:
389 dsp_cpustat_request(CPUSTAT_RUN);
392 case OMAP_DSP_IOCTL_RESET:
393 dsp_cpustat_request(CPUSTAT_RESET);
396 case OMAP_DSP_IOCTL_SETRSTVECT:
397 ret = dsp_set_rstvect((unsigned long)arg);
400 case OMAP_DSP_IOCTL_CPU_IDLE:
401 dsp_cpustat_request(CPUSTAT_CPU_IDLE);
404 case OMAP_DSP_IOCTL_GBL_IDLE:
405 dsp_cpustat_request(CPUSTAT_GBL_IDLE);
408 case OMAP_DSP_IOCTL_MPUI_WORDSWAP_ON:
412 case OMAP_DSP_IOCTL_MPUI_WORDSWAP_OFF:
416 case OMAP_DSP_IOCTL_MPUI_BYTESWAP_ON:
420 case OMAP_DSP_IOCTL_MPUI_BYTESWAP_OFF:
424 case OMAP_DSP_IOCTL_MBSEND:
426 struct omap_dsp_mailbox_cmd u_cmd;
428 if (copy_from_user(&u_cmd, (void *)arg, sizeof(u_cmd)))
431 mb.data = u_cmd.data;
432 ret = dsp_mbcmd_send((struct mbcmd *)&mb);
436 case OMAP_DSP_IOCTL_SETVAR:
438 struct omap_dsp_varinfo var;
439 if (copy_from_user(&var, (void *)arg, sizeof(var)))
441 ret = dsp_setvar(var.varid, var.val[0]);
445 case OMAP_DSP_IOCTL_RUNLEVEL:
449 case OMAP_DSP_IOCTL_FBEN:
454 * command level 2: commands which need lock
456 case OMAP_DSP_IOCTL_DSPCFG:
460 case OMAP_DSP_IOCTL_DSPUNCFG:
464 case OMAP_DSP_IOCTL_TASKCNT:
465 ret = dsp_task_count();
468 case OMAP_DSP_IOCTL_POLL:
472 case OMAP_DSP_IOCTL_FBDIS:
473 ret = dsp_fbctl_disable();
477 * FIXME: cpu status control for suspend - resume
479 case OMAP_DSP_IOCTL_SUSPEND:
480 if ((ret = dsp_suspend()) < 0)
482 dsp_cpustat_request(CPUSTAT_RESET);
485 case OMAP_DSP_IOCTL_RESUME:
486 if ((ret = dsp_resume()) < 0)
488 dsp_cpustat_request(CPUSTAT_RUN);
491 case OMAP_DSP_IOCTL_REGMEMR:
493 struct omap_dsp_reginfo *u_reg = (void *)arg;
494 unsigned short adr, val;
496 if (copy_from_user(&adr, &u_reg->adr, sizeof(short)))
498 if ((ret = dsp_regread(OMAP_DSP_MBCMD_REGRW_MEMR,
501 if (copy_to_user(&u_reg->val, &val, sizeof(short)))
506 case OMAP_DSP_IOCTL_REGMEMW:
508 struct omap_dsp_reginfo reg;
510 if (copy_from_user(®, (void *)arg, sizeof(reg)))
512 ret = dsp_regwrite(OMAP_DSP_MBCMD_REGRW_MEMW,
517 case OMAP_DSP_IOCTL_REGIOR:
519 struct omap_dsp_reginfo *u_reg = (void *)arg;
520 unsigned short adr, val;
522 if (copy_from_user(&adr, &u_reg->adr, sizeof(short)))
524 if ((ret = dsp_regread(OMAP_DSP_MBCMD_REGRW_IOR,
527 if (copy_to_user(&u_reg->val, &val, sizeof(short)))
532 case OMAP_DSP_IOCTL_REGIOW:
534 struct omap_dsp_reginfo reg;
536 if (copy_from_user(®, (void *)arg, sizeof(reg)))
538 ret = dsp_regwrite(OMAP_DSP_MBCMD_REGRW_IOW,
543 case OMAP_DSP_IOCTL_GETVAR:
545 struct omap_dsp_varinfo *u_var = (void *)arg;
547 unsigned short val[5]; /* maximum */
550 if (copy_from_user(&varid, &u_var->varid, sizeof(char)))
553 case OMAP_DSP_MBCMD_VARID_ICRMASK:
556 case OMAP_DSP_MBCMD_VARID_LOADINFO:
562 if ((ret = dsp_getvar(varid, val, argc)) < 0)
564 if (copy_to_user(&u_var->val, val, sizeof(short) * argc))
577 * functions called from mailbox1 interrupt routine
579 void mbx1_suspend(struct mbcmd *mb)
581 if (!waitqueue_active(&ioctl_wait_q) ||
582 (ioctl_wait_cmd != MBCMD(SUSPEND))) {
584 "mbx: SUSPEND command received, "
585 "but nobody is waiting for it...\n");
590 wake_up_interruptible(&ioctl_wait_q);
593 void mbx1_dspcfg(struct mbcmd *mb)
595 unsigned char last = mb->cmd_l & 0x80;
596 unsigned char cfgcmd = mb->cmd_l & 0x7f;
597 static unsigned long tmp_ipb_adr;
599 /* mailbox protocol check */
600 if (cfgcmd == OMAP_DSP_MBCMD_DSPCFG_PROTREV) {
601 if (!waitqueue_active(&ioctl_wait_q) ||
602 (ioctl_wait_cmd != MBCMD(DSPCFG))) {
604 "mbx: DSPCFG command received, "
605 "but nobody is waiting for it...\n");
609 mbx_revision = mb->data;
610 if (mbx_revision == OMAP_DSP_MBPROT_REVISION)
612 #ifdef OLD_BINARY_SUPPORT
613 else if ((mbx_revision == MBREV_3_0) ||
614 (mbx_revision == MBREV_3_2)) {
616 "mbx: ***** old DSP binary *****\n"
617 " Please update your DSP application.\n");
623 "mbx: protocol revision check error!\n"
624 " expected=0x%04x, received=0x%04x\n",
625 OMAP_DSP_MBPROT_REVISION, mb->data);
632 * following commands are accepted only after
633 * revision check has been passed.
635 if (!mbx_revision < 0) {
637 "mbx: DSPCFG command received, "
638 "but revision check has not been passed.\n");
642 if (!waitqueue_active(&ioctl_wait_q) ||
643 (ioctl_wait_cmd != MBCMD(DSPCFG))) {
645 "mbx: DSPCFG command received, "
646 "but nobody is waiting for it...\n");
651 case OMAP_DSP_MBCMD_DSPCFG_SYSADRH:
652 tmp_ipb_adr = (unsigned long)mb->data << 16;
655 case OMAP_DSP_MBCMD_DSPCFG_SYSADRL:
656 tmp_ipb_adr |= mb->data;
659 case OMAP_DSP_MBCMD_DSPCFG_ABORT:
664 "mbx: Unknown CFG command: cmd_l=0x%02x, data=0x%04x\n",
665 mb->cmd_l, mb->data);
673 volatile unsigned short *buf;
674 void *ipb_sys_da, *ipb_sys_ad;
677 unsigned short dbg_buf_sz, dbg_line_sz;
678 struct mem_sync_struct mem_sync, *mem_syncp;
680 ipb_sys_da = dspword_to_virt(tmp_ipb_adr);
681 if (ipbuf_sys_config(ipb_sys_da, DIR_D2A) < 0)
684 if (sync_with_dsp(&ipbuf_sys_da->s, OMAP_DSP_TID_ANON, 10) < 0) {
685 printk(KERN_ERR "mbx: DSPCFG - IPBUF sync failed!\n");
689 * read configuration data on system IPBUF
690 * we must read with 16bit-access
692 #ifdef OLD_BINARY_SUPPORT
693 if (mbx_revision == OMAP_DSP_MBPROT_REVISION) {
695 buf = ipbuf_sys_da->d;
699 badr = MKVIRT(buf[3], buf[4]);
700 /* ipb_sys_da = MKVIRT(buf[5], buf[6]); */
701 ipb_sys_ad = MKVIRT(buf[7], buf[8]);
702 mbseq = MKVIRT(buf[9], buf[10]);
703 dbg_buf = MKVIRT(buf[11], buf[12]);
704 dbg_buf_sz = buf[13];
705 dbg_line_sz = buf[14];
706 mem_sync.DARAM = MKVIRT(buf[15], buf[16]);
707 mem_sync.SARAM = MKVIRT(buf[17], buf[18]);
708 mem_sync.SDRAM = MKVIRT(buf[19], buf[20]);
709 mem_syncp = &mem_sync;
710 #ifdef OLD_BINARY_SUPPORT
711 } else if (mbx_revision == MBREV_3_2) {
712 buf = ipbuf_sys_da->d;
716 badr = MKVIRT(buf[3], buf[4]);
717 /* ipb_sys_da = MKVIRT(buf[5], buf[6]); */
718 ipb_sys_ad = MKVIRT(buf[7], buf[8]);
719 mbseq = MKVIRT(buf[9], buf[10]);
724 } else if (mbx_revision == MBREV_3_0) {
725 buf = ipbuf_sys_da->d;
729 badr = MKVIRT(buf[3], buf[4]);
730 /* bkeep = buf[5]; */
731 /* ipb_sys_da = MKVIRT(buf[6], buf[7]); */
732 ipb_sys_ad = MKVIRT(buf[8], buf[9]);
733 mbseq = MKVIRT(buf[10], buf[11]);
738 } else /* should not occur */
740 #endif /* OLD_BINARY_SUPPORT */
742 release_ipbuf_pvt(ipbuf_sys_da);
745 * following configurations need to be done before
746 * waking up the dspcfg initiator process.
748 if (ipbuf_sys_config(ipb_sys_ad, DIR_A2D) < 0)
750 if (ipbuf_config(bln, bsz, badr) < 0)
752 if (dsp_mb_config(mbseq) < 0)
754 if (dsp_dbg_config(dbg_buf, dbg_buf_sz, dbg_line_sz) < 0)
756 if (dsp_mem_sync_config(mem_syncp) < 0)
760 wake_up_interruptible(&ioctl_wait_q);
767 wake_up_interruptible(&ioctl_wait_q);
771 void mbx1_poll(struct mbcmd *mb)
773 if (!waitqueue_active(&ioctl_wait_q) ||
774 (ioctl_wait_cmd != MBCMD(POLL))) {
776 "mbx: POLL command received, "
777 "but nobody is waiting for it...\n");
782 wake_up_interruptible(&ioctl_wait_q);
785 void mbx1_regrw(struct mbcmd *mb)
787 if (!waitqueue_active(&ioctl_wait_q) ||
788 (ioctl_wait_cmd != MBCMD(REGRW))) {
790 "mbx: REGRW command received, "
791 "but nobody is waiting for it...\n");
796 case OMAP_DSP_MBCMD_REGRW_DATA:
798 varread_val[0] = mb->data;
799 wake_up_interruptible(&ioctl_wait_q);
804 "mbx: Illegal REGRW command: "
805 "cmd_l=0x%02x, data=0x%04x\n", mb->cmd_l, mb->data);
810 void mbx1_getvar(struct mbcmd *mb)
812 unsigned char varid = mb->cmd_l;
814 volatile unsigned short *buf;
816 if (!waitqueue_active(&ioctl_wait_q) ||
817 (ioctl_wait_cmd != MBCMD(GETVAR))) {
819 "mbx: GETVAR command received, "
820 "but nobody is waiting for it...\n");
826 case OMAP_DSP_MBCMD_VARID_ICRMASK:
827 varread_val[0] = mb->data;
829 case OMAP_DSP_MBCMD_VARID_LOADINFO:
831 if (sync_with_dsp(&ipbuf_sys_da->s, OMAP_DSP_TID_ANON, 10) < 0) {
833 "mbx: GETVAR - IPBUF sync failed!\n");
836 /* need word access. do not use memcpy. */
837 buf = ipbuf_sys_da->d;
838 for (i = 0; i < 5; i++) {
839 varread_val[i] = buf[i];
841 release_ipbuf_pvt(ipbuf_sys_da);
845 wake_up_interruptible(&ioctl_wait_q);
853 static ssize_t ifver_show(struct device *dev, struct device_attribute *attr,
859 * I/F VERSION descriptions:
861 * 3.2: sysfs / udev support
862 * KMEM_RESERVE / KMEM_RELEASE ioctls for mem device
863 * 3.3: added following ioctls
864 * OMAP_DSP_IOCTL_GBL_IDLE
865 * OMAP_DSP_IOCTL_CPU_IDLE (instead of OMAP_DSP_IOCTL_IDLE)
866 * OMAP_DSP_IOCTL_POLL
870 * print all supporting I/F VERSIONs, like followings.
872 * len += sprintf(buf, "3.2\n");
873 * len += sprintf(buf, "3.3\n");
875 len += sprintf(buf + len, "3.2\n");
876 len += sprintf(buf + len, "3.3\n");
881 static struct device_attribute dev_attr_ifver = __ATTR_RO(ifver);
883 static ssize_t cpustat_show(struct device *dev, struct device_attribute *attr,
886 return sprintf(buf, "%s\n", cpustat_name(dsp_cpustat_get_stat()));
889 static struct device_attribute dev_attr_cpustat = __ATTR_RO(cpustat);
891 static ssize_t icrmask_show(struct device *dev, struct device_attribute *attr,
894 return sprintf(buf, "0x%04x\n", dsp_cpustat_get_icrmask());
897 static ssize_t icrmask_store(struct device *dev, struct device_attribute *attr,
898 const char *buf, size_t count)
903 if (!capable(CAP_SYS_ADMIN))
906 mask = simple_strtol(buf, NULL, 16);
907 dsp_cpustat_set_icrmask(mask);
909 if (dsp_is_ready()) {
910 ret = dsp_setvar(OMAP_DSP_MBCMD_VARID_ICRMASK, mask);
918 static struct device_attribute dev_attr_icrmask =
919 __ATTR(icrmask, S_IWUSR | S_IRUGO, icrmask_show, icrmask_store);
921 static ssize_t loadinfo_show(struct device *dev, struct device_attribute *attr,
926 static unsigned short val[5];
928 if ((ret = dsp_getvar(OMAP_DSP_MBCMD_VARID_LOADINFO, val, 5)) < 0)
931 /* load info value range is 0(free) - 10000(busy) */
934 " 10ms average = %3d.%02d%%\n"
935 " 1sec average = %3d.%02d%% busiest 10ms = %3d.%02d%%\n"
936 " 1min average = %3d.%02d%% busiest 1s = %3d.%02d%%\n",
937 val[0]/100, val[0]%100,
938 val[1]/100, val[1]%100, val[2]/100, val[2]%100,
939 val[3]/100, val[3]%100, val[4]/100, val[4]%100);
944 * This is declared at the top of this file.
946 * static struct device_attribute dev_attr_loadinfo = __ATTR_RO(loadinfo);
949 void mbx1_fbctl_disable(void)
951 if (!waitqueue_active(&ioctl_wait_q) ||
952 (ioctl_wait_cmd != MBCMD(KFUNC))) {
954 "mbx: KFUNC:FBCTL command received, "
955 "but nobody is waiting for it...\n");
959 wake_up_interruptible(&ioctl_wait_q);
962 #ifdef CONFIG_PROC_FS
963 /* for backward compatibility */
964 static int version_read_proc(char *page, char **start, off_t off, int count,
965 int *eof, void *data)
968 * This entry is read by 3.1 tools only, so leave it as is.
969 * 3.2 and later will read from sysfs file.
971 return sprintf(page, "3.1\n");
974 static void __init dsp_ctl_create_proc(void)
976 struct proc_dir_entry *ent;
979 ent = create_proc_read_entry("version", 0, procdir_dsp,
980 version_read_proc, NULL);
983 "omapdsp: failed to register proc device: version\n");
987 static void dsp_ctl_remove_proc(void)
989 remove_proc_entry("version", procdir_dsp);
991 #endif /* CONFIG_PROC_FS */
993 struct file_operations dsp_ctl_fops = {
994 .owner = THIS_MODULE,
995 .ioctl = dsp_ctl_ioctl,
998 void __init dsp_ctl_init(void)
1000 device_create_file(&dsp_device.dev, &dev_attr_ifver);
1001 device_create_file(&dsp_device.dev, &dev_attr_cpustat);
1002 device_create_file(&dsp_device.dev, &dev_attr_icrmask);
1003 #ifdef CONFIG_PROC_FS
1004 dsp_ctl_create_proc();
1008 void dsp_ctl_exit(void)
1010 device_remove_file(&dsp_device.dev, &dev_attr_ifver);
1011 device_remove_file(&dsp_device.dev, &dev_attr_cpustat);
1012 device_remove_file(&dsp_device.dev, &dev_attr_icrmask);
1013 #ifdef CONFIG_PROC_FS
1014 dsp_ctl_remove_proc();