Loading arch/mips/Kconfig +72 −0 Original line number Diff line number Diff line Loading @@ -13,6 +13,22 @@ mainmenu "Linux/MIPS Kernel Configuration" source "init/Kconfig" config CPU_MIPS32 bool default y if CPU_MIPS32_R1 || CPU_MIPS32_R2 config CPU_MIPS64 bool default y if CPU_MIPS64_R1 || CPU_MIPS64_R2 config CPU_MIPSR1 bool default y if CPU_MIPS32_R1 || CPU_MIPS64_R1 config CPU_MIPSR2 bool default y if CPU_MIPS32_R2 || CPU_MIPS64_R2 config SYS_SUPPORTS_32BIT_KERNEL bool config SYS_SUPPORTS_64BIT_KERNEL Loading Loading @@ -233,6 +249,7 @@ config MIPS_EV64120 bool "Support for Galileo EV64120 Evaluation board (EXPERIMENTAL)" depends on EXPERIMENTAL select DMA_NONCOHERENT select IRQ_CPU select HW_HAS_PCI select MIPS_GT64120 select SYS_SUPPORTS_32BIT_KERNEL Loading Loading @@ -344,6 +361,7 @@ config MIPS_MALTA select BOOT_ELF32 select HAVE_STD_PC_SERIAL_PORT select DMA_NONCOHERENT select IRQ_CPU select GENERIC_ISA_DMA select HW_HAS_PCI select I8259 Loading Loading @@ -1277,6 +1295,31 @@ config CPU_HAS_PREFETCH bool "Enable prefetches" if CPU_SB1 && !CPU_SB1_PASS_2 default y if CPU_MIPS32 || CPU_MIPS64 || CPU_RM7000 || CPU_RM9000 || CPU_R10000 config MIPS_MT bool "Enable MIPS MT" config MIPS_VPE_LOADER bool "VPE loader support." depends on MIPS_MT help Includes a loader for loading an elf relocatable object onto another VPE and running it. config MIPS_VPE_LOADER_TOM bool "Load VPE program into memory hidden from linux" depends on MIPS_VPE_LOADER default y help The loader can use memory that is present but has been hidden from Linux using the kernel command line option "mem=xxMB". It's up to you to ensure the amount you put in the option and the space your program requires is less or equal to the amount physically present. # this should possibly be in drivers/char, but it is rather cpu related. Hmmm config MIPS_VPE_APSP_API bool "Enable support for AP/SP API (RTLX)" depends on MIPS_VPE_LOADER config VTAG_ICACHE bool "Support for Virtual Tagged I-cache" if CPU_MIPS64 || CPU_MIPS32 default y if CPU_SB1 Loading Loading @@ -1335,6 +1378,35 @@ config CPU_HAS_WB machines which require flushing of write buffers in software. Saying Y is the safe option; N may result in kernel malfunction and crashes. menu "MIPSR2 Interrupt handling" depends on CPU_MIPSR2 && CPU_ADVANCED config CPU_MIPSR2_IRQ_VI bool "Vectored interrupt mode" help Vectored interrupt mode allowing faster dispatching of interrupts. The board support code needs to be written to take advantage of this mode. Compatibility code is included to allow the kernel to run on a CPU that does not support vectored interrupts. It's safe to say Y here. config CPU_MIPSR2_IRQ_EI bool "External interrupt controller mode" help Extended interrupt mode takes advantage of an external interrupt controller to allow fast dispatching from many possible interrupt sources. Say N unless you know that external interrupt support is required. config CPU_MIPSR2_SRS bool "Make shadow set registers available for interrupt handlers" depends on CPU_MIPSR2_IRQ_VI || CPU_MIPSR2_IRQ_EI help Allow the kernel to use shadow register sets for fast interrupts. Interrupt handlers must be specially written to use shadow sets. Say N unless you know that shadow register set upport is needed. endmenu config CPU_HAS_SYNC bool depends on !CPU_R3000 Loading arch/mips/kernel/Makefile +4 −0 Original line number Diff line number Diff line Loading @@ -34,12 +34,16 @@ obj-$(CONFIG_CPU_R6000) += r6000_fpu.o r4k_switch.o obj-$(CONFIG_SMP) += smp.o obj-$(CONFIG_MIPS_VPE_LOADER) += vpe.o obj-$(CONFIG_MIPS_VPE_APSP_API) += rtlx.o obj-$(CONFIG_NO_ISA) += dma-no-isa.o obj-$(CONFIG_I8259) += i8259.o obj-$(CONFIG_IRQ_CPU) += irq_cpu.o obj-$(CONFIG_IRQ_CPU_RM7K) += irq-rm7000.o obj-$(CONFIG_IRQ_CPU_RM9K) += irq-rm9000.o obj-$(CONFIG_IRQ_MV64340) += irq-mv6434x.o obj-$(CONFIG_MIPS_BOARDS_GEN) += irq-msc01.o obj-$(CONFIG_32BIT) += scall32-o32.o obj-$(CONFIG_64BIT) += scall64-64.o Loading arch/mips/kernel/genex.S +32 −0 Original line number Diff line number Diff line Loading @@ -147,6 +147,38 @@ NESTED(except_vec_ejtag_debug, 0, sp) __FINIT /* * Vectored interrupt handler. * This prototype is copied to ebase + n*IntCtl.VS and patched * to invoke the handler */ NESTED(except_vec_vi, 0, sp) SAVE_SOME SAVE_AT .set push .set noreorder EXPORT(except_vec_vi_lui) lui v0, 0 /* Patched */ j except_vec_vi_handler EXPORT(except_vec_vi_ori) ori v0, 0 /* Patched */ .set pop END(except_vec_vi) EXPORT(except_vec_vi_end) /* * Common Vectored Interrupt code * Complete the register saves and invoke the handler which is passed in $v0 */ NESTED(except_vec_vi_handler, 0, sp) SAVE_TEMP SAVE_STATIC CLI move a0, sp jalr v0 j ret_from_irq END(except_vec_vi_handler) /* * EJTAG debug exception handler. */ Loading arch/mips/kernel/irq-msc01.c +4 −4 Original line number Diff line number Diff line Loading @@ -74,7 +74,7 @@ static void disable_msc_irq(unsigned int irq) static void level_mask_and_ack_msc_irq(unsigned int irq) { mask_msc_irq(irq); if (!cpu_has_ei) if (!cpu_has_veic) MSCIC_WRITE(MSC01_IC_EOI, 0); } Loading @@ -84,7 +84,7 @@ static void level_mask_and_ack_msc_irq(unsigned int irq) static void edge_mask_and_ack_msc_irq(unsigned int irq) { mask_msc_irq(irq); if (!cpu_has_ei) if (!cpu_has_veic) MSCIC_WRITE(MSC01_IC_EOI, 0); else { u32 r; Loading Loading @@ -166,14 +166,14 @@ void __init init_msc_irqs(unsigned int base, msc_irqmap_t *imp, int nirq) switch (imp->im_type) { case MSC01_IRQ_EDGE: irq_desc[base+n].handler = &msc_edgeirq_type; if (cpu_has_ei) if (cpu_has_veic) MSCIC_WRITE(MSC01_IC_SUP+n*8, MSC01_IC_SUP_EDGE_BIT); else MSCIC_WRITE(MSC01_IC_SUP+n*8, MSC01_IC_SUP_EDGE_BIT | imp->im_lvl); break; case MSC01_IRQ_LEVEL: irq_desc[base+n].handler = &msc_levelirq_type; if (cpu_has_ei) if (cpu_has_veic) MSCIC_WRITE(MSC01_IC_SUP+n*8, 0); else MSCIC_WRITE(MSC01_IC_SUP+n*8, imp->im_lvl); Loading arch/mips/kernel/rtlx.c 0 → 100644 +341 −0 Original line number Diff line number Diff line /* * Copyright (C) 2005 MIPS Technologies, Inc. All rights reserved. * * 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. * */ #include <linux/kernel.h> #include <linux/module.h> #include <linux/fs.h> #include <linux/init.h> #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> #include <linux/syscalls.h> #include <linux/moduleloader.h> #include <linux/interrupt.h> #include <linux/poll.h> #include <linux/sched.h> #include <linux/wait.h> #include <asm/mipsmtregs.h> #include <asm/cacheflush.h> #include <asm/atomic.h> #include <asm/cpu.h> #include <asm/processor.h> #include <asm/system.h> #include <asm/rtlx.h> #define RTLX_MAJOR 64 #define RTLX_TARG_VPE 1 struct rtlx_info *rtlx; static int major; static char module_name[] = "rtlx"; static inline int spacefree(int read, int write, int size); static struct chan_waitqueues { wait_queue_head_t rt_queue; wait_queue_head_t lx_queue; } channel_wqs[RTLX_CHANNELS]; static struct irqaction irq; static int irq_num; extern void *vpe_get_shared(int index); static void rtlx_dispatch(struct pt_regs *regs) { do_IRQ(MIPSCPU_INT_BASE + MIPS_CPU_RTLX_IRQ, regs); } irqreturn_t rtlx_interrupt(int irq, void *dev_id, struct pt_regs *regs) { irqreturn_t r = IRQ_HANDLED; int i; for (i = 0; i < RTLX_CHANNELS; i++) { struct rtlx_channel *chan = &rtlx->channel[i]; if (chan->lx_read != chan->lx_write) wake_up_interruptible(&channel_wqs[i].lx_queue); } return r; } void dump_rtlx(void) { int i; printk("id 0x%lx state %d\n", rtlx->id, rtlx->state); for (i = 0; i < RTLX_CHANNELS; i++) { struct rtlx_channel *chan = &rtlx->channel[i]; printk(" rt_state %d lx_state %d buffer_size %d\n", chan->rt_state, chan->lx_state, chan->buffer_size); printk(" rt_read %d rt_write %d\n", chan->rt_read, chan->rt_write); 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) { int i; if (rtlxi->id != RTLX_ID) { printk(KERN_WARNING "no valid RTLX id at 0x%p\n", rtlxi); return (-ENOEXEC); } /* 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); } /* set up for interrupt handling */ memset(&irq, 0, sizeof(struct irqaction)); if (cpu_has_vint) { set_vi_handler(MIPS_CPU_RTLX_IRQ, rtlx_dispatch); } irq_num = MIPSCPU_INT_BASE + MIPS_CPU_RTLX_IRQ; irq.handler = rtlx_interrupt; irq.flags = SA_INTERRUPT; irq.name = "RTLX"; irq.dev_id = rtlx; setup_irq(irq_num, &irq); rtlx = rtlxi; return (0); } /* only allow one open process at a time to open each channel */ static int rtlx_open(struct inode *inode, struct file *filp) { int minor, ret; struct rtlx_channel *chan; /* assume only 1 device at the mo. */ minor = MINOR(inode->i_rdev); if (rtlx == NULL) { struct rtlx_info **p; if( (p = vpe_get_shared(RTLX_TARG_VPE)) == NULL) { printk(" vpe_get_shared is NULL. Has an SP program been loaded?\n"); return (-EFAULT); } if (*p == NULL) { printk(" vpe_shared %p %p\n", p, *p); return (-EFAULT); } if ((ret = rtlx_init(*p)) < 0) return (ret); } chan = &rtlx->channel[minor]; /* already open? */ if (chan->lx_state == RTLX_STATE_OPENED) return (-EBUSY); chan->lx_state = RTLX_STATE_OPENED; return (0); } static int rtlx_release(struct inode *inode, struct file *filp) { int minor; minor = MINOR(inode->i_rdev); rtlx->channel[minor].lx_state = RTLX_STATE_UNUSED; return (0); } static unsigned int rtlx_poll(struct file *file, poll_table * wait) { int minor; unsigned int mask = 0; struct rtlx_channel *chan; minor = MINOR(file->f_dentry->d_inode->i_rdev); chan = &rtlx->channel[minor]; poll_wait(file, &channel_wqs[minor].rt_queue, wait); poll_wait(file, &channel_wqs[minor].lx_queue, wait); /* data available to read? */ if (chan->lx_read != chan->lx_write) mask |= POLLIN | POLLRDNORM; /* space to write */ if (spacefree(chan->rt_read, chan->rt_write, chan->buffer_size)) mask |= POLLOUT | POLLWRNORM; return (mask); } static ssize_t rtlx_read(struct file *file, char __user * buffer, size_t count, loff_t * ppos) { size_t fl = 0L; int minor; struct rtlx_channel *lx; DECLARE_WAITQUEUE(wait, current); minor = MINOR(file->f_dentry->d_inode->i_rdev); lx = &rtlx->channel[minor]; /* data available? */ if (lx->lx_write == lx->lx_read) { if (file->f_flags & O_NONBLOCK) return (0); // -EAGAIN makes cat whinge /* go to sleep */ add_wait_queue(&channel_wqs[minor].lx_queue, &wait); set_current_state(TASK_INTERRUPTIBLE); while (lx->lx_write == lx->lx_read) schedule(); set_current_state(TASK_RUNNING); remove_wait_queue(&channel_wqs[minor].lx_queue, &wait); /* back running */ } /* find out how much in total */ count = min( count, (size_t)(lx->lx_write + lx->buffer_size - lx->lx_read) % lx->buffer_size); /* then how much from the read pointer onwards */ fl = min( count, (size_t)lx->buffer_size - lx->lx_read); copy_to_user (buffer, &lx->lx_buffer[lx->lx_read], fl); /* and if there is anything left at the beginning of the buffer */ if ( count - fl ) copy_to_user (buffer + fl, lx->lx_buffer, count - fl); /* update the index */ lx->lx_read += count; lx->lx_read %= lx->buffer_size; return (count); } static inline int spacefree(int read, int write, int size) { 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); } return ((read + size - write) % size) - 1; } static ssize_t rtlx_write(struct file *file, const char __user * buffer, size_t count, loff_t * ppos) { int minor; struct rtlx_channel *rt; size_t fl; DECLARE_WAITQUEUE(wait, current); minor = MINOR(file->f_dentry->d_inode->i_rdev); rt = &rtlx->channel[minor]; /* any space left... */ if (!spacefree(rt->rt_read, rt->rt_write, rt->buffer_size)) { if (file->f_flags & O_NONBLOCK) return (-EAGAIN); add_wait_queue(&channel_wqs[minor].rt_queue, &wait); set_current_state(TASK_INTERRUPTIBLE); while (!spacefree(rt->rt_read, rt->rt_write, rt->buffer_size)) schedule(); set_current_state(TASK_RUNNING); remove_wait_queue(&channel_wqs[minor].rt_queue, &wait); } /* total number of bytes to copy */ count = min( count, (size_t)spacefree(rt->rt_read, rt->rt_write, rt->buffer_size) ); /* first bit from write pointer to the end of the buffer, or count */ fl = min(count, (size_t) rt->buffer_size - rt->rt_write); copy_from_user(&rt->rt_buffer[rt->rt_write], buffer, fl); /* if there's any left copy to the beginning of the buffer */ if( count - fl ) copy_from_user(rt->rt_buffer, buffer + fl, count - fl); rt->rt_write += count; rt->rt_write %= rt->buffer_size; return(count); } static struct file_operations rtlx_fops = { .owner = THIS_MODULE, .open = rtlx_open, .release = rtlx_release, .write = rtlx_write, .read = rtlx_read, .poll = rtlx_poll }; static int rtlx_module_init(void) { if ((major = register_chrdev(RTLX_MAJOR, module_name, &rtlx_fops)) < 0) { printk("rtlx_module_init: unable to register device\n"); return (-EBUSY); } if (major == 0) major = RTLX_MAJOR; return (0); } static void rtlx_module_exit(void) { unregister_chrdev(major, module_name); } module_init(rtlx_module_init); module_exit(rtlx_module_exit); MODULE_DESCRIPTION("MIPS RTLX"); MODULE_AUTHOR("Elizabeth Clarke, MIPS Technologies, Inc"); MODULE_LICENSE("GPL"); Loading
arch/mips/Kconfig +72 −0 Original line number Diff line number Diff line Loading @@ -13,6 +13,22 @@ mainmenu "Linux/MIPS Kernel Configuration" source "init/Kconfig" config CPU_MIPS32 bool default y if CPU_MIPS32_R1 || CPU_MIPS32_R2 config CPU_MIPS64 bool default y if CPU_MIPS64_R1 || CPU_MIPS64_R2 config CPU_MIPSR1 bool default y if CPU_MIPS32_R1 || CPU_MIPS64_R1 config CPU_MIPSR2 bool default y if CPU_MIPS32_R2 || CPU_MIPS64_R2 config SYS_SUPPORTS_32BIT_KERNEL bool config SYS_SUPPORTS_64BIT_KERNEL Loading Loading @@ -233,6 +249,7 @@ config MIPS_EV64120 bool "Support for Galileo EV64120 Evaluation board (EXPERIMENTAL)" depends on EXPERIMENTAL select DMA_NONCOHERENT select IRQ_CPU select HW_HAS_PCI select MIPS_GT64120 select SYS_SUPPORTS_32BIT_KERNEL Loading Loading @@ -344,6 +361,7 @@ config MIPS_MALTA select BOOT_ELF32 select HAVE_STD_PC_SERIAL_PORT select DMA_NONCOHERENT select IRQ_CPU select GENERIC_ISA_DMA select HW_HAS_PCI select I8259 Loading Loading @@ -1277,6 +1295,31 @@ config CPU_HAS_PREFETCH bool "Enable prefetches" if CPU_SB1 && !CPU_SB1_PASS_2 default y if CPU_MIPS32 || CPU_MIPS64 || CPU_RM7000 || CPU_RM9000 || CPU_R10000 config MIPS_MT bool "Enable MIPS MT" config MIPS_VPE_LOADER bool "VPE loader support." depends on MIPS_MT help Includes a loader for loading an elf relocatable object onto another VPE and running it. config MIPS_VPE_LOADER_TOM bool "Load VPE program into memory hidden from linux" depends on MIPS_VPE_LOADER default y help The loader can use memory that is present but has been hidden from Linux using the kernel command line option "mem=xxMB". It's up to you to ensure the amount you put in the option and the space your program requires is less or equal to the amount physically present. # this should possibly be in drivers/char, but it is rather cpu related. Hmmm config MIPS_VPE_APSP_API bool "Enable support for AP/SP API (RTLX)" depends on MIPS_VPE_LOADER config VTAG_ICACHE bool "Support for Virtual Tagged I-cache" if CPU_MIPS64 || CPU_MIPS32 default y if CPU_SB1 Loading Loading @@ -1335,6 +1378,35 @@ config CPU_HAS_WB machines which require flushing of write buffers in software. Saying Y is the safe option; N may result in kernel malfunction and crashes. menu "MIPSR2 Interrupt handling" depends on CPU_MIPSR2 && CPU_ADVANCED config CPU_MIPSR2_IRQ_VI bool "Vectored interrupt mode" help Vectored interrupt mode allowing faster dispatching of interrupts. The board support code needs to be written to take advantage of this mode. Compatibility code is included to allow the kernel to run on a CPU that does not support vectored interrupts. It's safe to say Y here. config CPU_MIPSR2_IRQ_EI bool "External interrupt controller mode" help Extended interrupt mode takes advantage of an external interrupt controller to allow fast dispatching from many possible interrupt sources. Say N unless you know that external interrupt support is required. config CPU_MIPSR2_SRS bool "Make shadow set registers available for interrupt handlers" depends on CPU_MIPSR2_IRQ_VI || CPU_MIPSR2_IRQ_EI help Allow the kernel to use shadow register sets for fast interrupts. Interrupt handlers must be specially written to use shadow sets. Say N unless you know that shadow register set upport is needed. endmenu config CPU_HAS_SYNC bool depends on !CPU_R3000 Loading
arch/mips/kernel/Makefile +4 −0 Original line number Diff line number Diff line Loading @@ -34,12 +34,16 @@ obj-$(CONFIG_CPU_R6000) += r6000_fpu.o r4k_switch.o obj-$(CONFIG_SMP) += smp.o obj-$(CONFIG_MIPS_VPE_LOADER) += vpe.o obj-$(CONFIG_MIPS_VPE_APSP_API) += rtlx.o obj-$(CONFIG_NO_ISA) += dma-no-isa.o obj-$(CONFIG_I8259) += i8259.o obj-$(CONFIG_IRQ_CPU) += irq_cpu.o obj-$(CONFIG_IRQ_CPU_RM7K) += irq-rm7000.o obj-$(CONFIG_IRQ_CPU_RM9K) += irq-rm9000.o obj-$(CONFIG_IRQ_MV64340) += irq-mv6434x.o obj-$(CONFIG_MIPS_BOARDS_GEN) += irq-msc01.o obj-$(CONFIG_32BIT) += scall32-o32.o obj-$(CONFIG_64BIT) += scall64-64.o Loading
arch/mips/kernel/genex.S +32 −0 Original line number Diff line number Diff line Loading @@ -147,6 +147,38 @@ NESTED(except_vec_ejtag_debug, 0, sp) __FINIT /* * Vectored interrupt handler. * This prototype is copied to ebase + n*IntCtl.VS and patched * to invoke the handler */ NESTED(except_vec_vi, 0, sp) SAVE_SOME SAVE_AT .set push .set noreorder EXPORT(except_vec_vi_lui) lui v0, 0 /* Patched */ j except_vec_vi_handler EXPORT(except_vec_vi_ori) ori v0, 0 /* Patched */ .set pop END(except_vec_vi) EXPORT(except_vec_vi_end) /* * Common Vectored Interrupt code * Complete the register saves and invoke the handler which is passed in $v0 */ NESTED(except_vec_vi_handler, 0, sp) SAVE_TEMP SAVE_STATIC CLI move a0, sp jalr v0 j ret_from_irq END(except_vec_vi_handler) /* * EJTAG debug exception handler. */ Loading
arch/mips/kernel/irq-msc01.c +4 −4 Original line number Diff line number Diff line Loading @@ -74,7 +74,7 @@ static void disable_msc_irq(unsigned int irq) static void level_mask_and_ack_msc_irq(unsigned int irq) { mask_msc_irq(irq); if (!cpu_has_ei) if (!cpu_has_veic) MSCIC_WRITE(MSC01_IC_EOI, 0); } Loading @@ -84,7 +84,7 @@ static void level_mask_and_ack_msc_irq(unsigned int irq) static void edge_mask_and_ack_msc_irq(unsigned int irq) { mask_msc_irq(irq); if (!cpu_has_ei) if (!cpu_has_veic) MSCIC_WRITE(MSC01_IC_EOI, 0); else { u32 r; Loading Loading @@ -166,14 +166,14 @@ void __init init_msc_irqs(unsigned int base, msc_irqmap_t *imp, int nirq) switch (imp->im_type) { case MSC01_IRQ_EDGE: irq_desc[base+n].handler = &msc_edgeirq_type; if (cpu_has_ei) if (cpu_has_veic) MSCIC_WRITE(MSC01_IC_SUP+n*8, MSC01_IC_SUP_EDGE_BIT); else MSCIC_WRITE(MSC01_IC_SUP+n*8, MSC01_IC_SUP_EDGE_BIT | imp->im_lvl); break; case MSC01_IRQ_LEVEL: irq_desc[base+n].handler = &msc_levelirq_type; if (cpu_has_ei) if (cpu_has_veic) MSCIC_WRITE(MSC01_IC_SUP+n*8, 0); else MSCIC_WRITE(MSC01_IC_SUP+n*8, imp->im_lvl); Loading
arch/mips/kernel/rtlx.c 0 → 100644 +341 −0 Original line number Diff line number Diff line /* * Copyright (C) 2005 MIPS Technologies, Inc. All rights reserved. * * 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. * */ #include <linux/kernel.h> #include <linux/module.h> #include <linux/fs.h> #include <linux/init.h> #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> #include <linux/syscalls.h> #include <linux/moduleloader.h> #include <linux/interrupt.h> #include <linux/poll.h> #include <linux/sched.h> #include <linux/wait.h> #include <asm/mipsmtregs.h> #include <asm/cacheflush.h> #include <asm/atomic.h> #include <asm/cpu.h> #include <asm/processor.h> #include <asm/system.h> #include <asm/rtlx.h> #define RTLX_MAJOR 64 #define RTLX_TARG_VPE 1 struct rtlx_info *rtlx; static int major; static char module_name[] = "rtlx"; static inline int spacefree(int read, int write, int size); static struct chan_waitqueues { wait_queue_head_t rt_queue; wait_queue_head_t lx_queue; } channel_wqs[RTLX_CHANNELS]; static struct irqaction irq; static int irq_num; extern void *vpe_get_shared(int index); static void rtlx_dispatch(struct pt_regs *regs) { do_IRQ(MIPSCPU_INT_BASE + MIPS_CPU_RTLX_IRQ, regs); } irqreturn_t rtlx_interrupt(int irq, void *dev_id, struct pt_regs *regs) { irqreturn_t r = IRQ_HANDLED; int i; for (i = 0; i < RTLX_CHANNELS; i++) { struct rtlx_channel *chan = &rtlx->channel[i]; if (chan->lx_read != chan->lx_write) wake_up_interruptible(&channel_wqs[i].lx_queue); } return r; } void dump_rtlx(void) { int i; printk("id 0x%lx state %d\n", rtlx->id, rtlx->state); for (i = 0; i < RTLX_CHANNELS; i++) { struct rtlx_channel *chan = &rtlx->channel[i]; printk(" rt_state %d lx_state %d buffer_size %d\n", chan->rt_state, chan->lx_state, chan->buffer_size); printk(" rt_read %d rt_write %d\n", chan->rt_read, chan->rt_write); 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) { int i; if (rtlxi->id != RTLX_ID) { printk(KERN_WARNING "no valid RTLX id at 0x%p\n", rtlxi); return (-ENOEXEC); } /* 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); } /* set up for interrupt handling */ memset(&irq, 0, sizeof(struct irqaction)); if (cpu_has_vint) { set_vi_handler(MIPS_CPU_RTLX_IRQ, rtlx_dispatch); } irq_num = MIPSCPU_INT_BASE + MIPS_CPU_RTLX_IRQ; irq.handler = rtlx_interrupt; irq.flags = SA_INTERRUPT; irq.name = "RTLX"; irq.dev_id = rtlx; setup_irq(irq_num, &irq); rtlx = rtlxi; return (0); } /* only allow one open process at a time to open each channel */ static int rtlx_open(struct inode *inode, struct file *filp) { int minor, ret; struct rtlx_channel *chan; /* assume only 1 device at the mo. */ minor = MINOR(inode->i_rdev); if (rtlx == NULL) { struct rtlx_info **p; if( (p = vpe_get_shared(RTLX_TARG_VPE)) == NULL) { printk(" vpe_get_shared is NULL. Has an SP program been loaded?\n"); return (-EFAULT); } if (*p == NULL) { printk(" vpe_shared %p %p\n", p, *p); return (-EFAULT); } if ((ret = rtlx_init(*p)) < 0) return (ret); } chan = &rtlx->channel[minor]; /* already open? */ if (chan->lx_state == RTLX_STATE_OPENED) return (-EBUSY); chan->lx_state = RTLX_STATE_OPENED; return (0); } static int rtlx_release(struct inode *inode, struct file *filp) { int minor; minor = MINOR(inode->i_rdev); rtlx->channel[minor].lx_state = RTLX_STATE_UNUSED; return (0); } static unsigned int rtlx_poll(struct file *file, poll_table * wait) { int minor; unsigned int mask = 0; struct rtlx_channel *chan; minor = MINOR(file->f_dentry->d_inode->i_rdev); chan = &rtlx->channel[minor]; poll_wait(file, &channel_wqs[minor].rt_queue, wait); poll_wait(file, &channel_wqs[minor].lx_queue, wait); /* data available to read? */ if (chan->lx_read != chan->lx_write) mask |= POLLIN | POLLRDNORM; /* space to write */ if (spacefree(chan->rt_read, chan->rt_write, chan->buffer_size)) mask |= POLLOUT | POLLWRNORM; return (mask); } static ssize_t rtlx_read(struct file *file, char __user * buffer, size_t count, loff_t * ppos) { size_t fl = 0L; int minor; struct rtlx_channel *lx; DECLARE_WAITQUEUE(wait, current); minor = MINOR(file->f_dentry->d_inode->i_rdev); lx = &rtlx->channel[minor]; /* data available? */ if (lx->lx_write == lx->lx_read) { if (file->f_flags & O_NONBLOCK) return (0); // -EAGAIN makes cat whinge /* go to sleep */ add_wait_queue(&channel_wqs[minor].lx_queue, &wait); set_current_state(TASK_INTERRUPTIBLE); while (lx->lx_write == lx->lx_read) schedule(); set_current_state(TASK_RUNNING); remove_wait_queue(&channel_wqs[minor].lx_queue, &wait); /* back running */ } /* find out how much in total */ count = min( count, (size_t)(lx->lx_write + lx->buffer_size - lx->lx_read) % lx->buffer_size); /* then how much from the read pointer onwards */ fl = min( count, (size_t)lx->buffer_size - lx->lx_read); copy_to_user (buffer, &lx->lx_buffer[lx->lx_read], fl); /* and if there is anything left at the beginning of the buffer */ if ( count - fl ) copy_to_user (buffer + fl, lx->lx_buffer, count - fl); /* update the index */ lx->lx_read += count; lx->lx_read %= lx->buffer_size; return (count); } static inline int spacefree(int read, int write, int size) { 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); } return ((read + size - write) % size) - 1; } static ssize_t rtlx_write(struct file *file, const char __user * buffer, size_t count, loff_t * ppos) { int minor; struct rtlx_channel *rt; size_t fl; DECLARE_WAITQUEUE(wait, current); minor = MINOR(file->f_dentry->d_inode->i_rdev); rt = &rtlx->channel[minor]; /* any space left... */ if (!spacefree(rt->rt_read, rt->rt_write, rt->buffer_size)) { if (file->f_flags & O_NONBLOCK) return (-EAGAIN); add_wait_queue(&channel_wqs[minor].rt_queue, &wait); set_current_state(TASK_INTERRUPTIBLE); while (!spacefree(rt->rt_read, rt->rt_write, rt->buffer_size)) schedule(); set_current_state(TASK_RUNNING); remove_wait_queue(&channel_wqs[minor].rt_queue, &wait); } /* total number of bytes to copy */ count = min( count, (size_t)spacefree(rt->rt_read, rt->rt_write, rt->buffer_size) ); /* first bit from write pointer to the end of the buffer, or count */ fl = min(count, (size_t) rt->buffer_size - rt->rt_write); copy_from_user(&rt->rt_buffer[rt->rt_write], buffer, fl); /* if there's any left copy to the beginning of the buffer */ if( count - fl ) copy_from_user(rt->rt_buffer, buffer + fl, count - fl); rt->rt_write += count; rt->rt_write %= rt->buffer_size; return(count); } static struct file_operations rtlx_fops = { .owner = THIS_MODULE, .open = rtlx_open, .release = rtlx_release, .write = rtlx_write, .read = rtlx_read, .poll = rtlx_poll }; static int rtlx_module_init(void) { if ((major = register_chrdev(RTLX_MAJOR, module_name, &rtlx_fops)) < 0) { printk("rtlx_module_init: unable to register device\n"); return (-EBUSY); } if (major == 0) major = RTLX_MAJOR; return (0); } static void rtlx_module_exit(void) { unregister_chrdev(major, module_name); } module_init(rtlx_module_init); module_exit(rtlx_module_exit); MODULE_DESCRIPTION("MIPS RTLX"); MODULE_AUTHOR("Elizabeth Clarke, MIPS Technologies, Inc"); MODULE_LICENSE("GPL");