
/*
 * rk_isr.c: interrupt hooks
 *
 * Copyright (C) 2000 TimeSys Corporation
 *
 * This is free software; you can redistribute it and/or modify it under
 * the terms of the GNU Lesser General Public License as published by the
 * Free Software Foundation; either version 2 of the License, or (at your
 * option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
 *
 * This file is derived from software distributed under the following terms:
 *
 * Real-Time and Multimedia Systems Laboratory
 * Copyright (c) 2000 Carnegie Mellon University
 * All Rights Reserved.
 * 
 * Permission to use, copy, modify and distribute this software and its
 * documentation is hereby granted, provided that both the copyright
 * notice and this permission notice appear in all copies of the
 * software, derivative works or modified versions, and any portions
 * thereof, and that both notices appear in supporting documentation.
 * 
 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
 * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
 * 
 * Carnegie Mellon requests users of this software to return to
 * 
 *  Real-Time and Multimedia Systems Laboratory
 *  Attn: Prof. Raj Rajkumar
 *  Electrical and Computer Engineering, and Computer Science
 *  Carnegie Mellon University
 *  Pittsburgh PA 15213-3890
 *
 *  or via email to raj@ece.cmu.edu
 * 
 * any improvements or extensions that they make and grant Carnegie Mellon
 * the rights to redistribute these changes.
 */
#include <rk/rk_linux.h>
#include <rk/rk_error.h>
#include <rk/rk.h>
#include <asm/system.h>
#include <asm/io.h>

#ifdef	DEBUG_RK
 /**/
#define	DEBUG_RK_ISR
     /**/
#ifdef	DEBUG_RK_ENFORCE
int debug_rk_enforce_flag;
#endif
 /**/
#endif				/*DEBUG_RK */
/*
 * Hooks
 */
void (*rk_kernel_intr_in_hook) (void);	/* arch/i386/kernel/irq.h */
void (*rk_restore_all_hook) (struct pt_regs regs);	/* arch/i386/kernel/entry.S */
int (*rk_ret_with_reschedule_hook) (struct pt_regs regs);

#ifdef __i386__
/*
 * For "rk_kernel_intr_in_hook":
 * void
 * rk_kernel_intr_in(void)
 * {
 * 	if (rk_do_IRQ()) {
 * 		__asm__("pushl $ret_from_intr\n\t"
 * 			"jmp do_IRQ");
 * 	} else {
 * 		__asm__ ("jmp ret_from_intr");
 * 	}
 * }
 *
 * This is called (is jumped to) while interrupt is disabled.
 */
void rk_kernel_intr_in(void);
__asm__("\n" __ALIGN_STR "\n"
	"rk_kernel_intr_in:\n\t"
	"call rk_do_IRQ\n\t"
	"testl %eax,%eax\n\t"
	"je 1f\n\t"
	"pushl $ret_from_intr\n\t"
	"jmp do_IRQ\n\t" ".align 4\n" "1:\n\t" "jmp ret_from_intr\n\t");

extern volatile int rk_in_intr;

int rk_do_IRQ(struct pt_regs regs)
{
    extern int rk_timer_isr(void);
    unsigned int irq;
    int ret = 1;		/* by default for Linux */

    irq = regs.orig_eax & 0xff;
    rk_in_intr = 1;
    if (irq == 0) {
	/*
	 * return 1:    also for Linux 
	 * return 0:    only for RK
	 */
	ret = rk_timer_isr();
	if (!ret) {
	    outb(0x20, 0x20);	/* send EIO to PIC */
	}
    }
    rk_in_intr = 0;
    return ret;
}
#elif defined(__powerpc__)
int rk_timer_interrupt(void)
{
    extern int rk_timer_isr(void);
    int ret;

    /*
     * return 1:    also for Linux 
     * return 0:    only for RK
     */
    ret = rk_timer_isr();
    return ret;
}

#else
#error I do not know how to handle interrupts on this architecture.
#endif

void cpu_reserve_sched_disable(struct rs_proc_list *rs_proc,
			       unsigned int arg);
void cpu_reserve_sched_blocking(struct rs_proc_list *rs_proc,
				unsigned int arg);

/*
 * For "rk_ret_with_reschedule_hook"
 */
asmlinkage int rk_ret_with_reschedule(struct pt_regs regs)
{
    rk_resource_set_t rs;
    rk_reserve_t cpu_rsv;
    void cpu_reserve_sched_timeshare_self(void);

    return 0;

    if ((rs = rk_current_resource_set)) {
	if (rk_resource_set_depleted(rs)) {
#if	defined(DEBUG_RK_ISR) || defined(DEBUG_RK_ENFORCE)
	    printk("rk_ret_with_reschedule: depleted rs(0x%x)\n",
		   (int) rs);
#endif				/* DEBUG_RK_ENFORCE */
	    cpu_rsv = rs->rs_cpu;
	    switch (cpu_rsv->rsv_reserve_param.enf_mode) {
	    case RSV_SOFT:
	    case RSV_FIRM:
		cpu_reserve_sched_timeshare_self();
		break;
	    case RSV_HARD:
	    default:
		/* sleep when hard (or bad) enforcement */
#if 0
		{
		    struct pt_regs *regs =
			(struct pt_regs *) (init_task.tss.esp0 -
					    sizeof(struct pt_regs));

		    if (!(regs->eflags & 0x200)) {
			if (current == &init_task)
			    printk("Current is init\n");

			printk("Idle task has interrupts disabled!\n");
			printk("EAX 0x%x EBX 0x%x ECX 0x%x EDX 0x%x\n",
			       regs->orig_eax, regs->ebx, regs->ecx,
			       regs->edx);
			printk("ESP 0x%x EBP 0x%x ESI 0x%x EDI 0x%x\n",
			       regs->esp, regs->ebp, regs->esi, regs->edi);
			printk("CS 0x%x DS 0x%x EFLAGS 0x%x EIP 0x%x\n",
			       regs->xcs, regs->xds, regs->eflags,
			       regs->eip);


//                                              regs->eflags|=0x200;
		    }
		}
#endif


		rs_proc_list_apply(&cpu_rsv->rsv_rs->rs_proc_list,
				   cpu_reserve_sched_blocking, 0);

		current->need_resched = 1;
	    }
	}
    }
    return 0;
}

/*
 * For "rk_restore_all_hook"
 */
asmlinkage void rk_restore_all(struct pt_regs regs)
{
}

/*
 *
 */
void rk_enable_isr(void)
{
    unsigned long flags;

    /*debug_rk_enforce_flag = 1; */

#if 0
    if (rk_kernel_intr_in_hook == rk_kernel_intr_in
	&& rk_restore_all_hook == rk_restore_all
	&& rk_ret_with_reschedule_hook == rk_ret_with_reschedule)
	return;
#endif

    rk_spin_lock(flags);
    {
	/* install hooks */
#ifdef __i386__
	rk_kernel_intr_in_hook = rk_kernel_intr_in;
	rk_restore_all_hook = rk_restore_all;
	rk_ret_with_reschedule_hook = rk_ret_with_reschedule;
#elif defined(__powerpc__)
	rk_timer_interrupt_hook = rk_timer_interrupt;
#else
#error I do not know the ISR hooks for this architecture.
#endif
    }
    rk_spin_unlock(flags);
#ifdef	DEBUG_RK
    printk("rk_enable_isr: rk kernel in/out hook enabled.\n");
#endif
}

void rk_disable_isr(void)
{
    unsigned long flags;
#if 0
    if (rk_kernel_intr_in_hook == (void *) 0
	&& rk_restore_all_hook == (void *) 0
	&& rk_ret_with_reschedule_hook == (void *) 0)
	return;
#endif
    /* This needs an interrupt, so the lock can't be held. */
    rk_release_hw_timer();

    rk_spin_lock(flags);
    {
	/* remove hooks */
#ifdef __i386__
	rk_kernel_intr_in_hook = NULL;
	rk_restore_all_hook = NULL;
	rk_ret_with_reschedule_hook = NULL;
#elif defined(__powerpc__)
	rk_timer_interrupt_hook = NULL;
#else
#error I do not know the ISR hooks for this architecture.
#endif
    }
    rk_spin_unlock(flags);
#ifdef	DEBUG_RK
    printk("rk_disable_isr: rk kernel in/out hook diabled.\n");
#endif
}


#ifdef	DEBUG_RK_ENFORCE
asmlinkage void rk_ret_from_sys_call_msg(void)
{
    unsigned long flags;
    rk_spin_lock(flags);
    {
	printk("ret_from_sys_call! pid(%d) eflag(0x%x)\n",
	       (int) current->pid, (int) flags);
    }
    rk_spin_unlock(flags);
}

asmlinkage void rk_signal_return_msg(void)
{
    unsigned long flags;
    rk_spin_lock(flags);
    {
	printk("signal_return! eflag(0x%x)\n", (int) flags);
    }
    rk_spin_unlock(flags);
}

asmlinkage void rk_ret_from_intr_msg(void)
{
    unsigned long flags;
    rk_spin_lock(flags);
    {
	printk("ret_from_intr! pid(%d) eflag(0x%x)\n",
	       (int) current->pid, (int) flags);
    }
    rk_spin_unlock(flags);
}

asmlinkage void rk_reschedule_msg(void)
{
    unsigned long flags;
    rk_spin_lock(flags);
    {
	printk("reschedule! pid(%d) eflag(0x%x)\n",
	       (int) current->pid, (int) flags);
    }
    rk_spin_unlock(flags);
}

#endif
