2005-07-14 15:57:16 +00:00
/*
* Copyright ( C ) 2005 MIPS Technologies , Inc . All rights reserved .
2006-01-15 18:11:28 +00:00
* Copyright ( C ) 2005 , 06 Ralf Baechle ( ralf @ linux - mips . org )
2005-07-14 15:57:16 +00:00
*
* This program is free software ; you can distribute it and / or modify it
* under the terms of the GNU General Public License ( Version 2 ) as
* published by the Free Software Foundation .
*
* This program is distributed in the hope it will be useful , but WITHOUT
* ANY WARRANTY ; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE . See the GNU General Public License
* for more details .
*
* You should have received a copy of the GNU General Public License along
* with this program ; if not , write to the Free Software Foundation , Inc . ,
* 59 Temple Place - Suite 330 , Boston MA 02111 - 1307 , USA .
*
*/
2007-02-07 15:36:56 +00:00
# include <linux/device.h>
2005-07-14 15:57:16 +00:00
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/fs.h>
# include <linux/init.h>
2006-04-05 09:45:45 +01:00
# include <asm/uaccess.h>
# include <linux/slab.h>
# include <linux/list.h>
# include <linux/vmalloc.h>
# include <linux/elf.h>
# include <linux/seq_file.h>
2008-05-15 09:10:50 -06:00
# include <linux/smp_lock.h>
2006-04-05 09:45:45 +01:00
# include <linux/syscalls.h>
# include <linux/moduleloader.h>
2006-01-15 18:11:28 +00:00
# include <linux/interrupt.h>
2005-07-14 15:57:16 +00:00
# include <linux/poll.h>
# include <linux/sched.h>
# include <linux/wait.h>
# include <asm/mipsmtregs.h>
2007-02-07 15:36:56 +00:00
# include <asm/mips_mt.h>
2006-04-05 09:45:45 +01:00
# include <asm/cacheflush.h>
# include <asm/atomic.h>
2005-07-14 15:57:16 +00:00
# include <asm/cpu.h>
# include <asm/processor.h>
2006-04-05 09:45:45 +01:00
# include <asm/system.h>
# include <asm/vpe.h>
2005-07-14 15:57:16 +00:00
# include <asm/rtlx.h>
2005-10-31 00:30:39 +00:00
static struct rtlx_info * rtlx ;
2005-07-14 15:57:16 +00:00
static int major ;
static char module_name [ ] = " rtlx " ;
static struct chan_waitqueues {
wait_queue_head_t rt_queue ;
wait_queue_head_t lx_queue ;
2007-02-23 13:40:45 +00:00
atomic_t in_open ;
2007-03-15 17:13:47 +00:00
struct mutex mutex ;
2005-07-14 15:57:16 +00:00
} channel_wqs [ RTLX_CHANNELS ] ;
2006-04-05 09:45:45 +01:00
static struct vpe_notifications notify ;
static int sp_stopping = 0 ;
2005-07-14 15:57:16 +00:00
extern void * vpe_get_shared ( int index ) ;
2006-10-07 19:44:33 +01:00
static void rtlx_dispatch ( void )
2005-07-14 15:57:16 +00:00
{
2007-01-08 02:14:29 +09:00
do_IRQ ( MIPS_CPU_IRQ_BASE + MIPS_CPU_RTLX_IRQ ) ;
2005-07-14 15:57:16 +00:00
}
2006-04-05 09:45:45 +01:00
/* Interrupt handler may be called before rtlx_init has otherwise had
a chance to run .
*/
2006-10-07 19:44:33 +01:00
static irqreturn_t rtlx_interrupt ( int irq , void * dev_id )
2005-07-14 15:57:16 +00:00
{
int i ;
2008-04-16 15:32:22 +02:00
unsigned int flags , vpeflags ;
/* Ought not to be strictly necessary for SMTC builds */
local_irq_save ( flags ) ;
vpeflags = dvpe ( ) ;
set_c0_status ( 0x100 < < MIPS_CPU_RTLX_IRQ ) ;
irq_enable_hazard ( ) ;
evpe ( vpeflags ) ;
local_irq_restore ( flags ) ;
2005-07-14 15:57:16 +00:00
for ( i = 0 ; i < RTLX_CHANNELS ; i + + ) {
2006-04-05 09:45:45 +01:00
wake_up ( & channel_wqs [ i ] . lx_queue ) ;
wake_up ( & channel_wqs [ i ] . rt_queue ) ;
2005-07-14 15:57:16 +00:00
}
2005-10-31 00:30:39 +00:00
return IRQ_HANDLED ;
2005-07-14 15:57:16 +00:00
}
2007-07-22 01:01:39 -07:00
static void __used dump_rtlx ( void )
2005-07-14 15:57:16 +00:00
{
int i ;
2006-04-05 09:45:45 +01:00
printk ( " id 0x%lx state %d \n " , rtlx - > id , rtlx - > state ) ;
2005-07-14 15:57:16 +00:00
for ( i = 0 ; i < RTLX_CHANNELS ; i + + ) {
2006-04-05 09:45:45 +01:00
struct rtlx_channel * chan = & rtlx - > channel [ i ] ;
2005-07-14 15:57:16 +00:00
2006-04-05 09:45:45 +01:00
printk ( " rt_state %d lx_state %d buffer_size %d \n " ,
chan - > rt_state , chan - > lx_state , chan - > buffer_size ) ;
2005-07-14 15:57:16 +00:00
2006-04-05 09:45:45 +01:00
printk ( " rt_read %d rt_write %d \n " ,
chan - > rt_read , chan - > rt_write ) ;
2005-07-14 15:57:16 +00:00
2006-04-05 09:45:45 +01:00
printk ( " lx_read %d lx_write %d \n " ,
chan - > lx_read , chan - > lx_write ) ;
printk ( " rt_buffer <%s> \n " , chan - > rt_buffer ) ;
printk ( " lx_buffer <%s> \n " , chan - > lx_buffer ) ;
}
}
/* call when we have the address of the shared structure from the SP side. */
static int rtlx_init ( struct rtlx_info * rtlxi )
{
if ( rtlxi - > id ! = RTLX_ID ) {
2008-04-16 15:32:22 +02:00
printk ( KERN_ERR " no valid RTLX id at 0x%p 0x%lx \n " ,
rtlxi , rtlxi - > id ) ;
2006-04-05 09:45:45 +01:00
return - ENOEXEC ;
}
2005-07-14 15:57:16 +00:00
rtlx = rtlxi ;
2005-10-31 00:30:39 +00:00
return 0 ;
2005-07-14 15:57:16 +00:00
}
2006-04-05 09:45:45 +01:00
/* notifications */
static void starting ( int vpe )
2005-07-14 15:57:16 +00:00
{
2006-04-05 09:45:45 +01:00
int i ;
sp_stopping = 0 ;
/* force a reload of rtlx */
rtlx = NULL ;
/* wake up any sleeping rtlx_open's */
for ( i = 0 ; i < RTLX_CHANNELS ; i + + )
wake_up_interruptible ( & channel_wqs [ i ] . lx_queue ) ;
}
static void stopping ( int vpe )
{
int i ;
sp_stopping = 1 ;
for ( i = 0 ; i < RTLX_CHANNELS ; i + + )
wake_up_interruptible ( & channel_wqs [ i ] . lx_queue ) ;
}
int rtlx_open ( int index , int can_sleep )
{
2007-03-15 17:08:28 +00:00
struct rtlx_info * * p ;
2007-02-22 14:19:48 +00:00
struct rtlx_channel * chan ;
2007-02-23 13:40:45 +00:00
enum rtlx_state state ;
2007-02-22 14:19:48 +00:00
int ret = 0 ;
2005-07-14 15:57:16 +00:00
2006-04-05 09:45:45 +01:00
if ( index > = RTLX_CHANNELS ) {
printk ( KERN_DEBUG " rtlx_open index out of range \n " ) ;
return - ENOSYS ;
}
2007-02-23 13:40:45 +00:00
if ( atomic_inc_return ( & channel_wqs [ index ] . in_open ) > 1 ) {
printk ( KERN_DEBUG " rtlx_open channel %d already opened \n " ,
index ) ;
ret = - EBUSY ;
goto out_fail ;
2006-04-05 09:45:45 +01:00
}
2005-07-14 15:57:16 +00:00
if ( rtlx = = NULL ) {
2007-07-27 19:31:10 +01:00
if ( ( p = vpe_get_shared ( tclimit ) ) = = NULL ) {
2008-04-16 15:32:22 +02:00
if ( can_sleep ) {
__wait_event_interruptible ( channel_wqs [ index ] . lx_queue ,
( p = vpe_get_shared ( tclimit ) ) , ret ) ;
if ( ret )
2007-02-22 14:19:48 +00:00
goto out_fail ;
2008-04-16 15:32:22 +02:00
} else {
printk ( KERN_DEBUG " No SP program loaded, and device "
" opened with O_NONBLOCK \n " ) ;
ret = - ENOSYS ;
goto out_fail ;
}
2005-07-14 15:57:16 +00:00
}
2007-03-15 17:08:28 +00:00
smp_rmb ( ) ;
2005-07-14 15:57:16 +00:00
if ( * p = = NULL ) {
2006-04-05 09:45:45 +01:00
if ( can_sleep ) {
2007-03-15 17:08:28 +00:00
DEFINE_WAIT ( wait ) ;
for ( ; ; ) {
2008-04-16 15:32:22 +02:00
prepare_to_wait (
& channel_wqs [ index ] . lx_queue ,
& wait , TASK_INTERRUPTIBLE ) ;
2007-03-15 17:08:28 +00:00
smp_rmb ( ) ;
if ( * p ! = NULL )
break ;
if ( ! signal_pending ( current ) ) {
schedule ( ) ;
continue ;
}
ret = - ERESTARTSYS ;
2007-02-22 14:19:48 +00:00
goto out_fail ;
2007-03-15 17:08:28 +00:00
}
finish_wait ( & channel_wqs [ index ] . lx_queue , & wait ) ;
2007-02-22 14:19:48 +00:00
} else {
2008-04-16 15:32:22 +02:00
pr_err ( " *vpe_get_shared is NULL. "
2006-04-05 09:45:45 +01:00
" Has an SP program been loaded? \n " ) ;
2007-02-22 14:19:48 +00:00
ret = - ENOSYS ;
goto out_fail ;
2006-04-05 09:45:45 +01:00
}
}
if ( ( unsigned int ) * p < KSEG0 ) {
2008-04-16 15:32:22 +02:00
printk ( KERN_WARNING " vpe_get_shared returned an "
" invalid pointer maybe an error code %d \n " ,
( int ) * p ) ;
2007-02-22 14:19:48 +00:00
ret = - ENOSYS ;
goto out_fail ;
2005-07-14 15:57:16 +00:00
}
2007-02-23 13:40:45 +00:00
if ( ( ret = rtlx_init ( * p ) ) < 0 )
goto out_ret ;
2005-07-14 15:57:16 +00:00
}
2006-04-05 09:45:45 +01:00
chan = & rtlx - > channel [ index ] ;
2005-07-14 15:57:16 +00:00
2007-02-23 13:40:45 +00:00
state = xchg ( & chan - > lx_state , RTLX_STATE_OPENED ) ;
if ( state = = RTLX_STATE_OPENED ) {
ret = - EBUSY ;
goto out_fail ;
}
2007-02-22 14:19:48 +00:00
out_fail :
2007-02-23 13:40:45 +00:00
smp_mb ( ) ;
atomic_dec ( & channel_wqs [ index ] . in_open ) ;
smp_mb ( ) ;
2007-02-22 14:19:48 +00:00
2007-02-23 13:40:45 +00:00
out_ret :
2007-02-22 14:19:48 +00:00
return ret ;
2005-07-14 15:57:16 +00:00
}
2006-04-05 09:45:45 +01:00
int rtlx_release ( int index )
2005-07-14 15:57:16 +00:00
{
2008-04-16 15:32:22 +02:00
if ( rtlx = = NULL ) {
pr_err ( " rtlx_release() with null rtlx \n " ) ;
return 0 ;
}
2006-04-05 09:45:45 +01:00
rtlx - > channel [ index ] . lx_state = RTLX_STATE_UNUSED ;
2005-10-31 00:30:39 +00:00
return 0 ;
2005-07-14 15:57:16 +00:00
}
2006-04-05 09:45:45 +01:00
unsigned int rtlx_read_poll ( int index , int can_sleep )
2005-07-14 15:57:16 +00:00
{
2006-04-05 09:45:45 +01:00
struct rtlx_channel * chan ;
2005-07-14 15:57:16 +00:00
2006-04-05 09:45:45 +01:00
if ( rtlx = = NULL )
return 0 ;
2005-07-14 15:57:16 +00:00
2006-04-05 09:45:45 +01:00
chan = & rtlx - > channel [ index ] ;
2005-07-14 15:57:16 +00:00
/* data available to read? */
2006-04-05 09:45:45 +01:00
if ( chan - > lx_read = = chan - > lx_write ) {
if ( can_sleep ) {
2007-02-22 14:19:48 +00:00
int ret = 0 ;
2005-07-14 15:57:16 +00:00
2007-02-22 14:19:48 +00:00
__wait_event_interruptible ( channel_wqs [ index ] . lx_queue ,
2008-04-16 15:32:22 +02:00
( chan - > lx_read ! = chan - > lx_write ) | |
sp_stopping , ret ) ;
2007-02-22 14:19:48 +00:00
if ( ret )
return ret ;
2005-07-14 15:57:16 +00:00
2007-02-22 14:19:48 +00:00
if ( sp_stopping )
return 0 ;
} else
2006-04-05 09:45:45 +01:00
return 0 ;
}
return ( chan - > lx_write + chan - > buffer_size - chan - > lx_read )
% chan - > buffer_size ;
2005-07-14 15:57:16 +00:00
}
2006-04-05 09:45:45 +01:00
static inline int write_spacefree ( int read , int write , int size )
2005-07-14 15:57:16 +00:00
{
2006-04-05 09:45:45 +01:00
if ( read = = write ) {
/*
* Never fill the buffer completely , so indexes are always
* equal if empty and only empty , or ! equal if data available
*/
return size - 1 ;
}
2005-07-14 15:57:16 +00:00
2006-04-05 09:45:45 +01:00
return ( ( read + size - write ) % size ) - 1 ;
}
2005-07-14 15:57:16 +00:00
2006-04-05 09:45:45 +01:00
unsigned int rtlx_write_poll ( int index )
{
struct rtlx_channel * chan = & rtlx - > channel [ index ] ;
2008-04-16 15:32:22 +02:00
return write_spacefree ( chan - > rt_read , chan - > rt_write ,
chan - > buffer_size ) ;
2006-04-05 09:45:45 +01:00
}
2005-07-14 15:57:16 +00:00
2007-04-25 15:08:57 +01:00
ssize_t rtlx_read ( int index , void __user * buff , size_t count )
2006-04-05 09:45:45 +01:00
{
2007-03-15 17:10:16 +00:00
size_t lx_write , fl = 0L ;
2006-04-05 09:45:45 +01:00
struct rtlx_channel * lx ;
2007-03-16 12:16:27 +00:00
unsigned long failed ;
2005-07-14 15:57:16 +00:00
2006-04-05 09:45:45 +01:00
if ( rtlx = = NULL )
return - ENOSYS ;
lx = & rtlx - > channel [ index ] ;
2005-07-14 15:57:16 +00:00
2007-03-15 17:13:47 +00:00
mutex_lock ( & channel_wqs [ index ] . mutex ) ;
2007-03-15 17:10:16 +00:00
smp_rmb ( ) ;
lx_write = lx - > lx_write ;
2005-07-14 15:57:16 +00:00
/* find out how much in total */
2005-10-31 00:30:39 +00:00
count = min ( count ,
2007-03-15 17:10:16 +00:00
( size_t ) ( lx_write + lx - > buffer_size - lx - > lx_read )
2006-04-05 09:45:45 +01:00
% lx - > buffer_size ) ;
2005-07-14 15:57:16 +00:00
/* then how much from the read pointer onwards */
2007-03-15 17:10:16 +00:00
fl = min ( count , ( size_t ) lx - > buffer_size - lx - > lx_read ) ;
2005-07-14 15:57:16 +00:00
2007-03-16 12:16:27 +00:00
failed = copy_to_user ( buff , lx - > lx_buffer + lx - > lx_read , fl ) ;
if ( failed )
goto out ;
2005-07-14 15:57:16 +00:00
/* and if there is anything left at the beginning of the buffer */
2007-03-15 17:10:16 +00:00
if ( count - fl )
2007-03-16 12:16:27 +00:00
failed = copy_to_user ( buff + fl , lx - > lx_buffer , count - fl ) ;
out :
count - = failed ;
2005-07-14 15:57:16 +00:00
2007-03-15 17:10:16 +00:00
smp_wmb ( ) ;
lx - > lx_read = ( lx - > lx_read + count ) % lx - > buffer_size ;
smp_wmb ( ) ;
2007-03-15 17:13:47 +00:00
mutex_unlock ( & channel_wqs [ index ] . mutex ) ;
2005-07-14 15:57:16 +00:00
2005-10-31 00:30:39 +00:00
return count ;
2005-07-14 15:57:16 +00:00
}
2007-04-25 15:08:57 +01:00
ssize_t rtlx_write ( int index , const void __user * buffer , size_t count )
2006-04-05 09:45:45 +01:00
{
struct rtlx_channel * rt ;
2007-04-25 15:08:57 +01:00
unsigned long failed ;
2007-03-15 17:10:16 +00:00
size_t rt_read ;
2006-04-05 09:45:45 +01:00
size_t fl ;
if ( rtlx = = NULL )
return ( - ENOSYS ) ;
rt = & rtlx - > channel [ index ] ;
2007-03-15 17:13:47 +00:00
mutex_lock ( & channel_wqs [ index ] . mutex ) ;
2007-03-15 17:10:16 +00:00
smp_rmb ( ) ;
rt_read = rt - > rt_read ;
2006-04-05 09:45:45 +01:00
/* total number of bytes to copy */
2008-04-16 15:32:22 +02:00
count = min ( count , ( size_t ) write_spacefree ( rt_read , rt - > rt_write ,
rt - > buffer_size ) ) ;
2006-04-05 09:45:45 +01:00
/* first bit from write pointer to the end of the buffer, or count */
fl = min ( count , ( size_t ) rt - > buffer_size - rt - > rt_write ) ;
2007-03-16 12:16:27 +00:00
failed = copy_from_user ( rt - > rt_buffer + rt - > rt_write , buffer , fl ) ;
if ( failed )
goto out ;
2006-04-05 09:45:45 +01:00
/* if there's any left copy to the beginning of the buffer */
2007-03-16 12:16:27 +00:00
if ( count - fl ) {
failed = copy_from_user ( rt - > rt_buffer , buffer + fl , count - fl ) ;
}
out :
2007-04-25 15:08:57 +01:00
count - = failed ;
2006-04-05 09:45:45 +01:00
2007-03-15 17:10:16 +00:00
smp_wmb ( ) ;
rt - > rt_write = ( rt - > rt_write + count ) % rt - > buffer_size ;
smp_wmb ( ) ;
2007-03-15 17:13:47 +00:00
mutex_unlock ( & channel_wqs [ index ] . mutex ) ;
2006-04-05 09:45:45 +01:00
2007-03-15 17:10:16 +00:00
return count ;
2006-04-05 09:45:45 +01:00
}
static int file_open ( struct inode * inode , struct file * filp )
{
2006-04-24 17:15:10 +01:00
int minor = iminor ( inode ) ;
2008-05-15 09:10:50 -06:00
int err ;
2006-04-05 09:45:45 +01:00
2008-05-15 09:10:50 -06:00
lock_kernel ( ) ;
err = rtlx_open ( minor , ( filp - > f_flags & O_NONBLOCK ) ? 0 : 1 ) ;
unlock_kernel ( ) ;
return err ;
2006-04-05 09:45:45 +01:00
}
static int file_release ( struct inode * inode , struct file * filp )
{
2006-04-24 17:15:10 +01:00
int minor = iminor ( inode ) ;
2006-04-05 09:45:45 +01:00
return rtlx_release ( minor ) ;
}
static unsigned int file_poll ( struct file * file , poll_table * wait )
{
int minor ;
unsigned int mask = 0 ;
2006-12-08 02:37:20 -08:00
minor = iminor ( file - > f_path . dentry - > d_inode ) ;
2006-04-05 09:45:45 +01:00
poll_wait ( file , & channel_wqs [ minor ] . rt_queue , wait ) ;
poll_wait ( file , & channel_wqs [ minor ] . lx_queue , wait ) ;
if ( rtlx = = NULL )
return 0 ;
/* data available to read? */
if ( rtlx_read_poll ( minor , 0 ) )
mask | = POLLIN | POLLRDNORM ;
/* space to write */
if ( rtlx_write_poll ( minor ) )
mask | = POLLOUT | POLLWRNORM ;
return mask ;
}
static ssize_t file_read ( struct file * file , char __user * buffer , size_t count ,
loff_t * ppos )
{
2006-12-08 02:37:20 -08:00
int minor = iminor ( file - > f_path . dentry - > d_inode ) ;
2006-04-05 09:45:45 +01:00
/* data available? */
if ( ! rtlx_read_poll ( minor , ( file - > f_flags & O_NONBLOCK ) ? 0 : 1 ) ) {
return 0 ; // -EAGAIN makes cat whinge
}
2007-03-16 12:16:27 +00:00
return rtlx_read ( minor , buffer , count ) ;
2006-04-05 09:45:45 +01:00
}
static ssize_t file_write ( struct file * file , const char __user * buffer ,
2005-07-14 15:57:16 +00:00
size_t count , loff_t * ppos )
{
int minor ;
struct rtlx_channel * rt ;
2006-12-08 02:37:20 -08:00
minor = iminor ( file - > f_path . dentry - > d_inode ) ;
2005-07-14 15:57:16 +00:00
rt = & rtlx - > channel [ minor ] ;
/* any space left... */
2006-04-05 09:45:45 +01:00
if ( ! rtlx_write_poll ( minor ) ) {
2007-02-22 14:19:48 +00:00
int ret = 0 ;
2005-07-14 15:57:16 +00:00
if ( file - > f_flags & O_NONBLOCK )
2005-10-31 00:30:39 +00:00
return - EAGAIN ;
2005-07-14 15:57:16 +00:00
2007-02-22 14:19:48 +00:00
__wait_event_interruptible ( channel_wqs [ minor ] . rt_queue ,
rtlx_write_poll ( minor ) ,
ret ) ;
if ( ret )
return ret ;
2005-07-14 15:57:16 +00:00
}
2007-03-16 12:16:27 +00:00
return rtlx_write ( minor , buffer , count ) ;
2005-07-14 15:57:16 +00:00
}
2007-02-12 00:55:31 -08:00
static const struct file_operations rtlx_fops = {
2006-04-05 09:45:45 +01:00
. owner = THIS_MODULE ,
. open = file_open ,
. release = file_release ,
. write = file_write ,
. read = file_read ,
. poll = file_poll
2005-07-14 15:57:16 +00:00
} ;
2006-04-05 09:45:45 +01:00
static struct irqaction rtlx_irq = {
. handler = rtlx_interrupt ,
2006-07-01 19:29:20 -07:00
. flags = IRQF_DISABLED ,
2006-04-05 09:45:45 +01:00
. name = " RTLX " ,
} ;
2007-01-08 02:14:29 +09:00
static int rtlx_irq_num = MIPS_CPU_IRQ_BASE + MIPS_CPU_RTLX_IRQ ;
2006-04-05 09:45:45 +01:00
2005-10-31 00:30:39 +00:00
static char register_chrdev_failed [ ] __initdata =
KERN_ERR " rtlx_module_init: unable to register device \n " ;
2007-07-28 00:51:45 +01:00
static int __init rtlx_module_init ( void )
2005-07-14 15:57:16 +00:00
{
2007-02-07 15:36:56 +00:00
struct device * dev ;
int i , err ;
2006-04-05 09:45:45 +01:00
2007-07-27 19:31:10 +01:00
if ( ! cpu_has_mipsmt ) {
printk ( " VPE loader: not a MIPS MT capable processor \n " ) ;
return - ENODEV ;
}
if ( tclimit = = 0 ) {
printk ( KERN_WARNING " No TCs reserved for AP/SP, not "
" initializing RTLX. \n Pass maxtcs=<n> argument as kernel "
" argument \n " ) ;
return - ENODEV ;
}
2005-10-31 00:30:39 +00:00
major = register_chrdev ( 0 , module_name , & rtlx_fops ) ;
if ( major < 0 ) {
printk ( register_chrdev_failed ) ;
return major ;
2005-07-14 15:57:16 +00:00
}
2006-04-05 09:45:45 +01:00
/* initialise the wait queues */
for ( i = 0 ; i < RTLX_CHANNELS ; i + + ) {
init_waitqueue_head ( & channel_wqs [ i ] . rt_queue ) ;
init_waitqueue_head ( & channel_wqs [ i ] . lx_queue ) ;
2007-02-23 13:40:45 +00:00
atomic_set ( & channel_wqs [ i ] . in_open , 0 ) ;
2007-03-15 17:13:47 +00:00
mutex_init ( & channel_wqs [ i ] . mutex ) ;
2007-02-07 15:36:56 +00:00
2008-07-21 20:03:34 -07:00
dev = device_create ( mt_class , NULL , MKDEV ( major , i ) , NULL ,
" %s%d " , module_name , i ) ;
2007-02-07 15:36:56 +00:00
if ( IS_ERR ( dev ) ) {
err = PTR_ERR ( dev ) ;
goto out_chrdev ;
}
2006-04-05 09:45:45 +01:00
}
/* set up notifiers */
notify . start = starting ;
notify . stop = stopping ;
2007-07-27 19:31:10 +01:00
vpe_notify ( tclimit , & notify ) ;
2006-04-05 09:45:45 +01:00
if ( cpu_has_vint )
set_vi_handler ( MIPS_CPU_RTLX_IRQ , rtlx_dispatch ) ;
2008-04-16 15:32:22 +02:00
else {
pr_err ( " APRP RTLX init on non-vectored-interrupt processor \n " ) ;
err = - ENODEV ;
goto out_chrdev ;
}
2006-04-05 09:45:45 +01:00
rtlx_irq . dev_id = rtlx ;
setup_irq ( rtlx_irq_num , & rtlx_irq ) ;
2005-10-31 00:30:39 +00:00
return 0 ;
2007-02-07 15:36:56 +00:00
out_chrdev :
for ( i = 0 ; i < RTLX_CHANNELS ; i + + )
device_destroy ( mt_class , MKDEV ( major , i ) ) ;
return err ;
2005-07-14 15:57:16 +00:00
}
2005-10-31 00:30:39 +00:00
static void __exit rtlx_module_exit ( void )
2005-07-14 15:57:16 +00:00
{
2007-02-07 15:36:56 +00:00
int i ;
for ( i = 0 ; i < RTLX_CHANNELS ; i + + )
device_destroy ( mt_class , MKDEV ( major , i ) ) ;
2005-07-14 15:57:16 +00:00
unregister_chrdev ( major , module_name ) ;
}
module_init ( rtlx_module_init ) ;
module_exit ( rtlx_module_exit ) ;
2005-10-31 00:30:39 +00:00
2005-07-14 15:57:16 +00:00
MODULE_DESCRIPTION ( " MIPS RTLX " ) ;
2006-04-05 09:45:45 +01:00
MODULE_AUTHOR ( " Elizabeth Oldham, MIPS Technologies, Inc. " ) ;
2005-07-14 15:57:16 +00:00
MODULE_LICENSE ( " GPL " ) ;