📄 rtai.c
字号:
/*COPYRIGHT (C) 2000 Paolo Mantegazza (mantegazza@aero.polimi.it) 2001 David Schleef <ds@schleef.org> 2001 Lineo, Inc. <ds@lineo.com> 2002 Wolfgang Grandegger (wg@denx.de)This program is free software; you can redistribute it and/or modifyit under the terms of version 2 of the GNU General Public License aspublished by the Free Software Foundation.This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; without even the implied warranty ofMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See theGNU General Public License for more details.You should have received a copy of the GNU General Public Licensealong with this program; if not, write to the Free SoftwareFoundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA*//* ACKNOWLEDGMENTS (LIKELY JUST A PRELIMINARY DRAFT): - Steve Papacharalambous (stevep@zentropix.com) has contributed an informative proc filesystem procedure.*/#include <linux/module.h>#include <linux/version.h>#include <linux/sched.h>#include <linux/interrupt.h>#include <linux/irq.h>#ifdef CONFIG_SMP#include <linux/openpic.h>#endif#include <asm/system.h>#include <asm/hw_irq.h>#include <asm/smp.h>#include <asm/io.h>#include <asm/bitops.h>#include <asm/atomic.h>#ifdef CONFIG_PROC_FS#include <linux/stat.h>#include <linux/proc_fs.h>#include "rtai_proc_fs.h"#include "rtai_version.h"#endif#include <asm/rtai.h>#include <asm/rtai_srq.h>MODULE_LICENSE("GPL");#ifndef CONFIG_RTAI_MOUNT_ON_LOAD#define CONFIG_RTAI_MOUNT_ON_LOAD y#endif#ifdef CONFIG_MPC8260ADSvoid mpc8260ads_set_led(int led,int state);#endif#include <rtai_trace.h>// proc filesystem additions.static int rtai_proc_register(void);static void rtai_proc_unregister(void);// End of proc filesystem additions./* Some defines */#define BITS_PER_INT 32 #ifdef CONFIG_SMP/* The SMP code untested. We don't care about it for the moment */#define NR_RTAI_IRQS (2 * BITS_PER_INT)#define LAST_GLOBAL_RTAI_IRQ (BITS_PER_INT - 1)#define HARD_LOCK_IPI 62#else#if NR_IRQS < BITS_PER_INT#define NR_RTAI_IRQS NR_IRQS#else#define NR_RTAI_IRQS BITS_PER_INT#endif#define LAST_GLOBAL_RTAI_IRQ (NR_RTAI_IRQS - 1)#endif#define NR_SYSRQS 32#define TIMER_IRQ 31#define IRQ_DESC irq_desc/* * This allows LINUX to handle IRQs requested with the function * rt_request_global_irq(). By default it's off for compatibility * with the i386 port. */#undef GLOBAL_PEND_LINUX_IRQ/* Debugging for events that change the interrupt flag * * If you want to use this, you need to define the functions * blink() or blink_out(). blink() is called with 0 or 1 * to indicate whether interrupts are disabled or enabled. * blink_out() is called with the return address of the code * that triggered the interrupt flag change, with the low * bit set/unset as in blink. (Since ppc code is always * word aligned, we can freely use the bottom 2 bits.) * * I use blink() to turn on/off LEDs and blink_out() to * write the return address serially over digital I/O lines * to a host system. -ds */#if defined(DEBUG_INTR_BLINK)#define set_intr_flag(a,b) do{ \ (a)=(b); \ blink((b)&1); \ }while(0)#define set_intr_flag_wasset(b) do{ \ blink((b)&1); \ }while(0)#elif defined(DEBUG_INTR_SERIAL)#define set_intr_flag(a,b) do{ \ (a)=(b); \ blink_out(((unsigned int)__builtin_return_address(0))|((b)&1)); \ }while(0)#define set_intr_flag_wasset(b) do{ \ blink_out(((unsigned int)__builtin_return_address(0))|((b)&1)); \ }while(0)#else#define set_intr_flag(a,b) do{ (a)=(b); }while(0)#define set_intr_flag_wasset(b)#endif/* Most of our data */#define RTAI_IRQ_UNMAPPED 0#define RTAI_IRQ_MAPPED 1#define RTAI_IRQ_MAPPED_TEMP 2static struct irq_handling { int ppc_irq; int mapped;#ifdef CONFIG_SMP volatile unsigned long dest_status; #endif volatile int ext; unsigned long data; void (*handler)(unsigned int irq); unsigned int irq_count;} global_irq[NR_RTAI_IRQS];static int rtai_irq[NR_IRQS];static struct hw_interrupt_type *linux_irq_desc_handler[NR_IRQS];static int map_ppc_irq(int irq, int rtai_irq_from, int rtai_irq_to){ int rirq; for (rirq = rtai_irq_from; rirq <= rtai_irq_to; rirq++) { if (global_irq[rirq].mapped == RTAI_IRQ_UNMAPPED) { global_irq[rirq].mapped = RTAI_IRQ_MAPPED; global_irq[rirq].ppc_irq = irq; rtai_irq[irq] = rirq; return rirq; } } printk("map_ppc_irq: no more slot available for IRQ %d!\n", irq); return -1;}static inline int map_global_ppc_irq(int irq){ return map_ppc_irq(irq, 0, LAST_GLOBAL_RTAI_IRQ);}#ifdef CONFIG_SMPstatic inline void map_cpu_own_ppc_irq(int irq){ return map_ppc_irq(irq, LAST_GLOBAL_RTAI_IRQ + 1, NR_RTAI_IRQS - 1);}#endifstatic inline void unmap_ppc_irq(int irq){ int rirq; if ((rirq = rtai_irq[irq]) >= 0) { if (global_irq[rirq].mapped == RTAI_IRQ_MAPPED_TEMP) { global_irq[rirq].mapped = RTAI_IRQ_UNMAPPED; global_irq[rirq].ppc_irq = 0; rtai_irq[irq] = -1; } else { printk("unmap_ppc_irq: oops, conistency error!\n"); } } else { printk("unmap_ppc_irq: IRQ %d is not mapped!\n", irq); }}static struct sysrq_t { unsigned int label; void (*rtai_handler)(void); long long (*user_handler)(unsigned int whatever);} sysrq[NR_SYSRQS];// The main items to be saved-restored to make Linux our humble slaveextern void ppc_irq_dispatch_handler(struct pt_regs *regs, int irq);#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,4,3)struct int_control_struct ppc_int_control;#endifstatic int (*ppc_get_irq)(struct pt_regs *regs);unsigned long ppc_irq_dispatcher, ppc_timer_handler;extern int (*rtai_srq_bckdr)(struct pt_regs *regs);/* Hook in the interrupt return path */extern void (*rtai_soft_sti)(void);static struct pt_regs rtai_regs; // Dummy registers.static struct global_rt_status { volatile unsigned int pending_irqs; volatile unsigned int activ_irqs; volatile unsigned int pending_srqs; volatile unsigned int activ_srqs; volatile unsigned int cpu_in_sti; volatile unsigned int used_by_linux; volatile unsigned int locked_cpus;#ifdef CONFIG_SMP volatile unsigned int hard_nesting; volatile unsigned int hard_lock_all_service; spinlock_t hard_lock;#endif spinlock_t data_lock; spinlock_t ic_lock;} global;volatile unsigned int *locked_cpus = &global.locked_cpus;static struct cpu_own_status { volatile unsigned int intr_flag; volatile unsigned int linux_intr_flag; volatile unsigned int pending_irqs; volatile unsigned int activ_irqs; void (*rt_timer_handler)(void); void (*trailing_irq_handler)(int irq, void *dev_id, struct pt_regs *regs);} processor[NR_RT_CPUS]; #ifdef CONFIG_SMP/* Our interprocessor messaging */ void send_ipi_shorthand(unsigned int shorthand, int irq){ int cpuid; unsigned long flags; cpuid = hard_cpu_id(); hard_save_flags(flags); hard_cli(); if (shorthand == APIC_DEST_ALLINC) { openpic_cause_IPI(cpuid, irq, 0xFFFFFFFF); } else { openpic_cause_IPI(cpuid, irq, 0xFFFFFFFF & ~(1 << cpuid)); } hard_restore_flags(flags);}void send_ipi_logical(unsigned long dest, int irq){ unsigned long flags; if ((dest &= cpu_online_map)) { hard_save_flags(flags); openpic_cause_IPI(hard_cpu_id(), irq, dest); hard_cli(); hard_restore_flags(flags); }}static void hard_lock_all_handler(void){ int cpuid; set_bit(cpuid = hard_cpu_id(), &global_irq[HARD_LOCK_IPI].dest_status); rt_spin_lock(&(global.hard_lock));// No service at the moment. rt_spin_unlock(&(global.hard_lock)); clear_bit(cpuid, &globa_irql[HARD_LOCK_IPI].dest_status);}static inline int hard_lock_all(void){ unsigned long flags; flags = rt_global_save_flags_and_cli(); if (!global.hard_nesting++) { global.hard_lock_all_service = 0; rt_spin_lock(&(global.hard_lock)); send_ipi_shorthand(APIC_DEST_ALLBUT, HARD_LOCK_IPI); while (global_irq[HARD_LOCK_IPI].dest_status != (cpu_online_map & ~global.locked_cpus)); } return flags;}static inline void hard_unlock_all(unsigned long flags){ if (global.hard_nesting > 0) { if (!(--global.hard_nesting)) { rt_spin_unlock(&(global.hard_lock)); while (global_irq[HARD_LOCK_IPI].dest_status); } } rt_global_restore_flags(flags);}#elsevoid send_ipi_shorthand(unsigned int shorthand, int irq) { }void send_ipi_logical(unsigned long dest, int irq) { }#if 0static void hard_lock_all_handler(void) { }#endifstatic inline unsigned long hard_lock_all(void){ unsigned long flags; hard_save_flags_and_cli(flags); return flags;}#define hard_unlock_all(flags) hard_restore_flags((flags))#endifstatic void linux_cli(void){ set_intr_flag(processor[hard_cpu_id()].intr_flag,0);}#if 0static void linux_soft_sti(void){ unsigned long cpuid; struct cpu_own_status *cpu; cpu = processor + (cpuid = hard_cpu_id()); set_intr_flag(cpu->intr_flag,(1 << IFLAG) | (1 << cpuid));}#endifstatic void run_pending_irqs(void){ unsigned long irq, cpuid; struct cpu_own_status *cpu; cpuid = hard_cpu_id(); if (!test_and_set_bit(cpuid, &global.cpu_in_sti)) { cpu = processor + cpuid; while (global.pending_irqs | cpu->pending_irqs | global.pending_srqs) { hard_cli(); if ((irq = cpu->pending_irqs & ~(cpu->activ_irqs))) { irq = ffnz(irq); set_bit(irq, &cpu->activ_irqs); clear_bit(irq, &cpu->pending_irqs); hard_sti(); set_intr_flag(cpu->intr_flag,0); if (irq == TIMER_IRQ) { timer_interrupt(&rtai_regs); if (cpu->trailing_irq_handler) { (cpu->trailing_irq_handler)(TIMER_IRQ, 0, &rtai_regs); } } else { printk("WHY HERE? AT THE MOMENT IT IS JUST UP, SO NO LOCAL IRQs.\n"); } clear_bit(irq, &cpu->activ_irqs); } else { hard_sti(); } rt_spin_lock_irq(&(global.data_lock)); if ((irq = global.pending_srqs & ~global.activ_srqs)) { irq = ffnz(irq); set_bit(irq, &global.activ_srqs); clear_bit(irq, &global.pending_srqs); rt_spin_unlock_irq(&(global.data_lock)); if (sysrq[irq].rtai_handler) { sysrq[irq].rtai_handler(); } clear_bit(irq, &global.activ_srqs); } else { rt_spin_unlock_irq(&(global.data_lock)); } rt_spin_lock_irq(&(global.data_lock)); if ((irq = global.pending_irqs & ~global.activ_irqs)) { irq = ffnz(irq); set_bit(irq, &global.activ_irqs); clear_bit(irq, &global.pending_irqs); rt_spin_unlock_irq(&(global.data_lock)); set_intr_flag(cpu->intr_flag,0); /* from Linux do_IRQ */ hardirq_enter(cpuid); ppc_irq_dispatch_handler(&rtai_regs, global_irq[irq].ppc_irq); hardirq_exit(cpuid);#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,10) /* ? */ if (softirq_pending(cpuid)) { do_softirq(); }#endif clear_bit(irq, &global.activ_irqs); } else { rt_spin_unlock_irq(&(global.data_lock)); } } clear_bit(cpuid, &global.cpu_in_sti); } /* We _should_ do this, but it doesn't work correctly */ //if(atomic_read(&ppc_n_lost_interrupts))do_lost_interrupts(MSR_EE);}static void linux_sti(void){ run_pending_irqs(); set_intr_flag(processor[hard_cpu_id()].intr_flag, (1 << IFLAG) | (1 << hard_cpu_id()));}static void linux_save_flags(unsigned long *flags){ *flags = processor[hard_cpu_id()].intr_flag;}static void linux_restore_flags(unsigned long flags){ if (flags) { linux_sti(); } else { set_intr_flag(processor[hard_cpu_id()].intr_flag,0); }}unsigned int linux_save_flags_and_cli(void){ unsigned int ret; ret = xchg_u32((void *)(&(processor[hard_cpu_id()].intr_flag)), 0); set_intr_flag_wasset(0); return ret;}unsigned int linux_save_flags_and_cli_cpuid(int cpuid) // LXRT specific{ return xchg_u32((void *)&(processor[cpuid].intr_flag), 0);}void rtai_just_copy_back(unsigned long flags, int cpuid){ set_intr_flag(processor[cpuid].intr_flag,flags);}static void (*ic_ack_irq[NR_IRQS]) (unsigned int irq);static void do_nothing_picfun(unsigned int irq) { };unsigned int rt_startup_irq(unsigned int irq){ unsigned long flags, retval; struct hw_interrupt_type *irq_desc; if ((irq_desc = linux_irq_desc_handler[irq]) && irq_desc->startup) { flags = rt_spin_lock_irqsave(&global.ic_lock); retval = irq_desc->startup(irq); rt_spin_unlock_irqrestore(flags, &global.ic_lock); return retval; } return 0;}void rt_shutdown_irq(unsigned int irq){ unsigned int flags; struct hw_interrupt_type *irq_desc; if ((irq_desc = linux_irq_desc_handler[irq]) && irq_desc->shutdown) { flags = rt_spin_lock_irqsave(&global.ic_lock); irq_desc->shutdown(irq); rt_spin_unlock_irqrestore(flags, &global.ic_lock); }}void rt_enable_irq(unsigned int irq){ unsigned int flags; struct hw_interrupt_type *irq_desc; if ((irq_desc = linux_irq_desc_handler[irq]) && irq_desc->enable) { flags = rt_spin_lock_irqsave(&global.ic_lock); irq_desc->enable(irq); rt_spin_unlock_irqrestore(flags, &global.ic_lock); }}void rt_disable_irq(unsigned int irq){ unsigned int flags; struct hw_interrupt_type *irq_desc; if ((irq_desc = linux_irq_desc_handler[irq]) && irq_desc->disable) { flags = rt_spin_lock_irqsave(&global.ic_lock); irq_desc->disable(irq); rt_spin_unlock_irqrestore(flags, &global.ic_lock); }}void rt_ack_irq(unsigned int irq){ unsigned int flags; flags = rt_spin_lock_irqsave(&global.ic_lock); if(ic_ack_irq[irq]) ic_ack_irq[irq](irq); rt_spin_unlock_irqrestore(flags, &global.ic_lock);}void rt_unmask_irq(unsigned int irq){ unsigned int flags; struct hw_interrupt_type *irq_desc; if ((irq_desc = linux_irq_desc_handler[irq])) { flags = rt_spin_lock_irqsave(&global.ic_lock); if(linux_irq_desc_handler[irq]->end) linux_irq_desc_handler[irq]->end(irq); else if(linux_irq_desc_handler[irq]->enable) linux_irq_desc_handler[irq]->enable(irq); rt_spin_unlock_irqrestore(flags, &global.ic_lock); }}static int dispatch_irq(struct pt_regs *regs, int isfake){ int irq, rirq; rt_spin_lock(&global.ic_lock); if ((irq = ppc_get_irq(regs)) >= 0) { if(ic_ack_irq[irq]) ic_ack_irq[irq](irq); // Any umasking must be done in the irq handler. rt_spin_unlock(&global.ic_lock);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -