Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e01402b1 authored by Ralf Baechle's avatar Ralf Baechle
Browse files

More AP / SP bits for the 34K, the Malta bits and things. Still wants


a little polishing.

Signed-off-by: default avatarRalf Baechle <ralf@linux-mips.org>
parent 86071b63
Loading
Loading
Loading
Loading
+72 −0
Original line number Diff line number Diff line
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
+4 −0
Original line number Diff line number Diff line
@@ -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
+32 −0
Original line number Diff line number Diff line
@@ -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.
 */
+4 −4
Original line number Diff line number Diff line
@@ -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);
}

@@ -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;
@@ -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);
+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