diff options
Diffstat (limited to 'arch/blackfin/kernel')
-rw-r--r-- | arch/blackfin/kernel/Makefile | 2 | ||||
-rw-r--r-- | arch/blackfin/kernel/bfin_gpio.c | 100 | ||||
-rw-r--r-- | arch/blackfin/kernel/cplb-mpu/cplbmgr.c | 8 | ||||
-rw-r--r-- | arch/blackfin/kernel/entry.S | 4 | ||||
-rw-r--r-- | arch/blackfin/kernel/ipipe.c | 428 | ||||
-rw-r--r-- | arch/blackfin/kernel/irqchip.c | 5 | ||||
-rw-r--r-- | arch/blackfin/kernel/mcount.S | 70 | ||||
-rw-r--r-- | arch/blackfin/kernel/process.c | 7 | ||||
-rw-r--r-- | arch/blackfin/kernel/time.c | 15 | ||||
-rw-r--r-- | arch/blackfin/kernel/traps.c | 13 |
10 files changed, 586 insertions, 66 deletions
diff --git a/arch/blackfin/kernel/Makefile b/arch/blackfin/kernel/Makefile index f0902c120dc..38a233374f0 100644 --- a/arch/blackfin/kernel/Makefile +++ b/arch/blackfin/kernel/Makefile @@ -15,6 +15,8 @@ else obj-y += time.o endif +obj-$(CONFIG_IPIPE) += ipipe.o +obj-$(CONFIG_IPIPE_TRACE_MCOUNT) += mcount.o obj-$(CONFIG_BFIN_GPTIMERS) += gptimers.o obj-$(CONFIG_CPLB_INFO) += cplbinfo.o obj-$(CONFIG_MODULES) += module.o diff --git a/arch/blackfin/kernel/bfin_gpio.c b/arch/blackfin/kernel/bfin_gpio.c index 2c72b15b71b..4c14331978f 100644 --- a/arch/blackfin/kernel/bfin_gpio.c +++ b/arch/blackfin/kernel/bfin_gpio.c @@ -422,13 +422,13 @@ arch_initcall(bfin_gpio_init); void set_gpio_ ## name(unsigned gpio, unsigned short arg) \ { \ unsigned long flags; \ - local_irq_save(flags); \ + local_irq_save_hw(flags); \ if (arg) \ gpio_bankb[gpio_bank(gpio)]->name |= gpio_bit(gpio); \ else \ gpio_bankb[gpio_bank(gpio)]->name &= ~gpio_bit(gpio); \ AWA_DUMMY_READ(name); \ - local_irq_restore(flags); \ + local_irq_restore_hw(flags); \ } \ EXPORT_SYMBOL(set_gpio_ ## name); @@ -444,13 +444,13 @@ SET_GPIO(both) void set_gpio_ ## name(unsigned gpio, unsigned short arg) \ { \ unsigned long flags; \ - local_irq_save(flags); \ + local_irq_save_hw(flags); \ if (arg) \ gpio_bankb[gpio_bank(gpio)]->name ## _set = gpio_bit(gpio); \ else \ gpio_bankb[gpio_bank(gpio)]->name ## _clear = gpio_bit(gpio); \ AWA_DUMMY_READ(name); \ - local_irq_restore(flags); \ + local_irq_restore_hw(flags); \ } \ EXPORT_SYMBOL(set_gpio_ ## name); #else @@ -473,10 +473,10 @@ SET_GPIO_SC(data) void set_gpio_toggle(unsigned gpio) { unsigned long flags; - local_irq_save(flags); + local_irq_save_hw(flags); gpio_bankb[gpio_bank(gpio)]->toggle = gpio_bit(gpio); AWA_DUMMY_READ(toggle); - local_irq_restore(flags); + local_irq_restore_hw(flags); } #else void set_gpio_toggle(unsigned gpio) @@ -494,10 +494,10 @@ EXPORT_SYMBOL(set_gpio_toggle); void set_gpiop_ ## name(unsigned gpio, unsigned short arg) \ { \ unsigned long flags; \ - local_irq_save(flags); \ + local_irq_save_hw(flags); \ gpio_bankb[gpio_bank(gpio)]->name = arg; \ AWA_DUMMY_READ(name); \ - local_irq_restore(flags); \ + local_irq_restore_hw(flags); \ } \ EXPORT_SYMBOL(set_gpiop_ ## name); #else @@ -525,10 +525,10 @@ unsigned short get_gpio_ ## name(unsigned gpio) \ { \ unsigned long flags; \ unsigned short ret; \ - local_irq_save(flags); \ + local_irq_save_hw(flags); \ ret = 0x01 & (gpio_bankb[gpio_bank(gpio)]->name >> gpio_sub_n(gpio)); \ AWA_DUMMY_READ(name); \ - local_irq_restore(flags); \ + local_irq_restore_hw(flags); \ return ret; \ } \ EXPORT_SYMBOL(get_gpio_ ## name); @@ -558,10 +558,10 @@ unsigned short get_gpiop_ ## name(unsigned gpio) \ { \ unsigned long flags; \ unsigned short ret; \ - local_irq_save(flags); \ + local_irq_save_hw(flags); \ ret = (gpio_bankb[gpio_bank(gpio)]->name); \ AWA_DUMMY_READ(name); \ - local_irq_restore(flags); \ + local_irq_restore_hw(flags); \ return ret; \ } \ EXPORT_SYMBOL(get_gpiop_ ## name); @@ -611,10 +611,10 @@ int gpio_pm_wakeup_request(unsigned gpio, unsigned char type) if ((check_gpio(gpio) < 0) || !type) return -EINVAL; - local_irq_save(flags); + local_irq_save_hw(flags); wakeup_map[gpio_bank(gpio)] |= gpio_bit(gpio); wakeup_flags_map[gpio] = type; - local_irq_restore(flags); + local_irq_restore_hw(flags); return 0; } @@ -627,11 +627,11 @@ void gpio_pm_wakeup_free(unsigned gpio) if (check_gpio(gpio) < 0) return; - local_irq_save(flags); + local_irq_save_hw(flags); wakeup_map[gpio_bank(gpio)] &= ~gpio_bit(gpio); - local_irq_restore(flags); + local_irq_restore_hw(flags); } EXPORT_SYMBOL(gpio_pm_wakeup_free); @@ -882,7 +882,7 @@ int peripheral_request(unsigned short per, const char *label) if (!(per & P_DEFINED)) return -ENODEV; - local_irq_save(flags); + local_irq_save_hw(flags); /* If a pin can be muxed as either GPIO or peripheral, make * sure it is not already a GPIO pin when we request it. @@ -893,7 +893,7 @@ int peripheral_request(unsigned short per, const char *label) printk(KERN_ERR "%s: Peripheral %d is already reserved as GPIO by %s !\n", __func__, ident, get_label(ident)); - local_irq_restore(flags); + local_irq_restore_hw(flags); return -EBUSY; } @@ -923,7 +923,7 @@ int peripheral_request(unsigned short per, const char *label) printk(KERN_ERR "%s: Peripheral %d function %d is already reserved by %s !\n", __func__, ident, P_FUNCT2MUX(per), get_label(ident)); - local_irq_restore(flags); + local_irq_restore_hw(flags); return -EBUSY; } } @@ -938,7 +938,7 @@ int peripheral_request(unsigned short per, const char *label) #endif port_setup(ident, PERIPHERAL_USAGE); - local_irq_restore(flags); + local_irq_restore_hw(flags); set_label(ident, label); return 0; @@ -980,10 +980,10 @@ void peripheral_free(unsigned short per) if (check_gpio(ident) < 0) return; - local_irq_save(flags); + local_irq_save_hw(flags); if (unlikely(!(reserved_peri_map[gpio_bank(ident)] & gpio_bit(ident)))) { - local_irq_restore(flags); + local_irq_restore_hw(flags); return; } @@ -994,7 +994,7 @@ void peripheral_free(unsigned short per) set_label(ident, "free"); - local_irq_restore(flags); + local_irq_restore_hw(flags); } EXPORT_SYMBOL(peripheral_free); @@ -1028,7 +1028,7 @@ int bfin_gpio_request(unsigned gpio, const char *label) if (check_gpio(gpio) < 0) return -EINVAL; - local_irq_save(flags); + local_irq_save_hw(flags); /* * Allow that the identical GPIO can @@ -1037,7 +1037,7 @@ int bfin_gpio_request(unsigned gpio, const char *label) */ if (cmp_label(gpio, label) == 0) { - local_irq_restore(flags); + local_irq_restore_hw(flags); return 0; } @@ -1045,7 +1045,7 @@ int bfin_gpio_request(unsigned gpio, const char *label) dump_stack(); printk(KERN_ERR "bfin-gpio: GPIO %d is already reserved by %s !\n", gpio, get_label(gpio)); - local_irq_restore(flags); + local_irq_restore_hw(flags); return -EBUSY; } if (unlikely(reserved_peri_map[gpio_bank(gpio)] & gpio_bit(gpio))) { @@ -1053,7 +1053,7 @@ int bfin_gpio_request(unsigned gpio, const char *label) printk(KERN_ERR "bfin-gpio: GPIO %d is already reserved as Peripheral by %s !\n", gpio, get_label(gpio)); - local_irq_restore(flags); + local_irq_restore_hw(flags); return -EBUSY; } if (unlikely(reserved_gpio_irq_map[gpio_bank(gpio)] & gpio_bit(gpio))) @@ -1063,7 +1063,7 @@ int bfin_gpio_request(unsigned gpio, const char *label) reserved_gpio_map[gpio_bank(gpio)] |= gpio_bit(gpio); set_label(gpio, label); - local_irq_restore(flags); + local_irq_restore_hw(flags); port_setup(gpio, GPIO_USAGE); @@ -1078,12 +1078,12 @@ void bfin_gpio_free(unsigned gpio) if (check_gpio(gpio) < 0) return; - local_irq_save(flags); + local_irq_save_hw(flags); if (unlikely(!(reserved_gpio_map[gpio_bank(gpio)] & gpio_bit(gpio)))) { dump_stack(); gpio_error(gpio); - local_irq_restore(flags); + local_irq_restore_hw(flags); return; } @@ -1091,7 +1091,7 @@ void bfin_gpio_free(unsigned gpio) set_label(gpio, "free"); - local_irq_restore(flags); + local_irq_restore_hw(flags); } EXPORT_SYMBOL(bfin_gpio_free); @@ -1102,14 +1102,14 @@ int bfin_gpio_irq_request(unsigned gpio, const char *label) if (check_gpio(gpio) < 0) return -EINVAL; - local_irq_save(flags); + local_irq_save_hw(flags); if (unlikely(reserved_gpio_irq_map[gpio_bank(gpio)] & gpio_bit(gpio))) { dump_stack(); printk(KERN_ERR "bfin-gpio: GPIO %d is already reserved as gpio-irq !\n", gpio); - local_irq_restore(flags); + local_irq_restore_hw(flags); return -EBUSY; } if (unlikely(reserved_peri_map[gpio_bank(gpio)] & gpio_bit(gpio))) { @@ -1117,7 +1117,7 @@ int bfin_gpio_irq_request(unsigned gpio, const char *label) printk(KERN_ERR "bfin-gpio: GPIO %d is already reserved as Peripheral by %s !\n", gpio, get_label(gpio)); - local_irq_restore(flags); + local_irq_restore_hw(flags); return -EBUSY; } if (unlikely(reserved_gpio_map[gpio_bank(gpio)] & gpio_bit(gpio))) @@ -1128,7 +1128,7 @@ int bfin_gpio_irq_request(unsigned gpio, const char *label) reserved_gpio_irq_map[gpio_bank(gpio)] |= gpio_bit(gpio); set_label(gpio, label); - local_irq_restore(flags); + local_irq_restore_hw(flags); port_setup(gpio, GPIO_USAGE); @@ -1142,12 +1142,12 @@ void bfin_gpio_irq_free(unsigned gpio) if (check_gpio(gpio) < 0) return; - local_irq_save(flags); + local_irq_save_hw(flags); if (unlikely(!(reserved_gpio_irq_map[gpio_bank(gpio)] & gpio_bit(gpio)))) { dump_stack(); gpio_error(gpio); - local_irq_restore(flags); + local_irq_restore_hw(flags); return; } @@ -1155,7 +1155,7 @@ void bfin_gpio_irq_free(unsigned gpio) set_label(gpio, "free"); - local_irq_restore(flags); + local_irq_restore_hw(flags); } @@ -1169,10 +1169,10 @@ int bfin_gpio_direction_input(unsigned gpio) return -EINVAL; } - local_irq_save(flags); + local_irq_save_hw(flags); gpio_array[gpio_bank(gpio)]->port_dir_clear = gpio_bit(gpio); gpio_array[gpio_bank(gpio)]->port_inen |= gpio_bit(gpio); - local_irq_restore(flags); + local_irq_restore_hw(flags); return 0; } @@ -1187,11 +1187,11 @@ int bfin_gpio_direction_output(unsigned gpio, int value) return -EINVAL; } - local_irq_save(flags); + local_irq_save_hw(flags); gpio_array[gpio_bank(gpio)]->port_inen &= ~gpio_bit(gpio); gpio_set_value(gpio, value); gpio_array[gpio_bank(gpio)]->port_dir_set = gpio_bit(gpio); - local_irq_restore(flags); + local_irq_restore_hw(flags); return 0; } @@ -1218,10 +1218,10 @@ void bfin_gpio_irq_prepare(unsigned gpio) port_setup(gpio, GPIO_USAGE); - local_irq_save(flags); + local_irq_save_hw(flags); gpio_array[gpio_bank(gpio)]->port_dir_clear = gpio_bit(gpio); gpio_array[gpio_bank(gpio)]->port_inen |= gpio_bit(gpio); - local_irq_restore(flags); + local_irq_restore_hw(flags); } #else @@ -1232,11 +1232,11 @@ int bfin_gpio_get_value(unsigned gpio) int ret; if (unlikely(get_gpio_edge(gpio))) { - local_irq_save(flags); + local_irq_save_hw(flags); set_gpio_edge(gpio, 0); ret = get_gpio_data(gpio); set_gpio_edge(gpio, 1); - local_irq_restore(flags); + local_irq_restore_hw(flags); return ret; } else @@ -1254,11 +1254,11 @@ int bfin_gpio_direction_input(unsigned gpio) return -EINVAL; } - local_irq_save(flags); + local_irq_save_hw(flags); gpio_bankb[gpio_bank(gpio)]->dir &= ~gpio_bit(gpio); gpio_bankb[gpio_bank(gpio)]->inen |= gpio_bit(gpio); AWA_DUMMY_READ(inen); - local_irq_restore(flags); + local_irq_restore_hw(flags); return 0; } @@ -1273,7 +1273,7 @@ int bfin_gpio_direction_output(unsigned gpio, int value) return -EINVAL; } - local_irq_save(flags); + local_irq_save_hw(flags); gpio_bankb[gpio_bank(gpio)]->inen &= ~gpio_bit(gpio); if (value) @@ -1283,7 +1283,7 @@ int bfin_gpio_direction_output(unsigned gpio, int value) gpio_bankb[gpio_bank(gpio)]->dir |= gpio_bit(gpio); AWA_DUMMY_READ(dir); - local_irq_restore(flags); + local_irq_restore_hw(flags); return 0; } diff --git a/arch/blackfin/kernel/cplb-mpu/cplbmgr.c b/arch/blackfin/kernel/cplb-mpu/cplbmgr.c index 5ef5d1a787f..87463ce87f5 100644 --- a/arch/blackfin/kernel/cplb-mpu/cplbmgr.c +++ b/arch/blackfin/kernel/cplb-mpu/cplbmgr.c @@ -332,7 +332,7 @@ void flush_switched_cplbs(unsigned int cpu) nr_cplb_flush[cpu]++; - local_irq_save(flags); + local_irq_save_hw(flags); disable_icplb(); for (i = first_switched_icplb; i < MAX_CPLBS; i++) { icplb_tbl[cpu][i].data = 0; @@ -346,7 +346,7 @@ void flush_switched_cplbs(unsigned int cpu) bfin_write32(DCPLB_DATA0 + i * 4, 0); } enable_dcplb(); - local_irq_restore(flags); + local_irq_restore_hw(flags); } @@ -362,7 +362,7 @@ void set_mask_dcplbs(unsigned long *masks, unsigned int cpu) return; } - local_irq_save(flags); + local_irq_save_hw(flags); current_rwx_mask[cpu] = masks; d_data = CPLB_SUPV_WR | CPLB_VALID | CPLB_DIRTY | PAGE_SIZE_4KB; @@ -382,5 +382,5 @@ void set_mask_dcplbs(unsigned long *masks, unsigned int cpu) addr += PAGE_SIZE; } enable_dcplb(); - local_irq_restore(flags); + local_irq_restore_hw(flags); } diff --git a/arch/blackfin/kernel/entry.S b/arch/blackfin/kernel/entry.S index c0c3fe81122..a9cfba9946b 100644 --- a/arch/blackfin/kernel/entry.S +++ b/arch/blackfin/kernel/entry.S @@ -42,6 +42,10 @@ #endif ENTRY(_ret_from_fork) +#ifdef CONFIG_IPIPE + [--sp] = reti; /* IRQs on. */ + SP += 4; +#endif /* CONFIG_IPIPE */ SP += -12; call _schedule_tail; SP += 12; diff --git a/arch/blackfin/kernel/ipipe.c b/arch/blackfin/kernel/ipipe.c new file mode 100644 index 00000000000..339be5a3ae6 --- /dev/null +++ b/arch/blackfin/kernel/ipipe.c @@ -0,0 +1,428 @@ +/* -*- linux-c -*- + * linux/arch/blackfin/kernel/ipipe.c + * + * Copyright (C) 2005-2007 Philippe Gerum. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, Inc., 675 Mass Ave, Cambridge MA 02139, + * USA; either version 2 of the License, or (at your option) any later + * version. + * + * This program 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 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. + * + * Architecture-dependent I-pipe support for the Blackfin. + */ + +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/module.h> +#include <linux/interrupt.h> +#include <linux/percpu.h> +#include <linux/bitops.h> +#include <linux/slab.h> +#include <linux/errno.h> +#include <linux/kthread.h> +#include <asm/unistd.h> +#include <asm/system.h> +#include <asm/atomic.h> +#include <asm/io.h> + +static int create_irq_threads; + +DEFINE_PER_CPU(struct pt_regs, __ipipe_tick_regs); + +static DEFINE_PER_CPU(unsigned long, pending_irqthread_mask); + +static DEFINE_PER_CPU(int [IVG13 + 1], pending_irq_count); + +asmlinkage void asm_do_IRQ(unsigned int irq, struct pt_regs *regs); + +static void __ipipe_no_irqtail(void); + +unsigned long __ipipe_irq_tail_hook = (unsigned long)&__ipipe_no_irqtail; +EXPORT_SYMBOL(__ipipe_irq_tail_hook); + +unsigned long __ipipe_core_clock; +EXPORT_SYMBOL(__ipipe_core_clock); + +unsigned long __ipipe_freq_scale; +EXPORT_SYMBOL(__ipipe_freq_scale); + +atomic_t __ipipe_irq_lvdepth[IVG15 + 1]; + +unsigned long __ipipe_irq_lvmask = __all_masked_irq_flags; +EXPORT_SYMBOL(__ipipe_irq_lvmask); + +static void __ipipe_ack_irq(unsigned irq, struct irq_desc *desc) +{ + desc->ipipe_ack(irq, desc); +} + +/* + * __ipipe_enable_pipeline() -- We are running on the boot CPU, hw + * interrupts are off, and secondary CPUs are still lost in space. + */ +void __ipipe_enable_pipeline(void) +{ + unsigned irq; + + __ipipe_core_clock = get_cclk(); /* Fetch this once. */ + __ipipe_freq_scale = 1000000000UL / __ipipe_core_clock; + + for (irq = 0; irq < NR_IRQS; ++irq) + ipipe_virtualize_irq(ipipe_root_domain, + irq, + (ipipe_irq_handler_t)&asm_do_IRQ, + NULL, + &__ipipe_ack_irq, + IPIPE_HANDLE_MASK | IPIPE_PASS_MASK); +} + +/* + * __ipipe_handle_irq() -- IPIPE's generic IRQ handler. An optimistic + * interrupt protection log is maintained here for each domain. Hw + * interrupts are masked on entry. + */ +void __ipipe_handle_irq(unsigned irq, struct pt_regs *regs) +{ + struct ipipe_domain *this_domain, *next_domain; + struct list_head *head, *pos; + int m_ack, s = -1; + + /* + * Software-triggered IRQs do not need any ack. The contents + * of the register frame should only be used when processing + * the timer interrupt, but not for handling any other + * interrupt. + */ + m_ack = (regs == NULL || irq == IRQ_SYSTMR || irq == IRQ_CORETMR); + + this_domain = ipipe_current_domain; + + if (unlikely(test_bit(IPIPE_STICKY_FLAG, &this_domain->irqs[irq].control))) + head = &this_domain->p_link; + else { + head = __ipipe_pipeline.next; + next_domain = list_entry(head, struct ipipe_domain, p_link); + if (likely(test_bit(IPIPE_WIRED_FLAG, &next_domain->irqs[irq].control))) { + if (!m_ack && next_domain->irqs[irq].acknowledge != NULL) + next_domain->irqs[irq].acknowledge(irq, irq_desc + irq); + if (test_bit(IPIPE_ROOTLOCK_FLAG, &ipipe_root_domain->flags)) + s = __test_and_set_bit(IPIPE_STALL_FLAG, + &ipipe_root_cpudom_var(status)); + __ipipe_dispatch_wired(next_domain, irq); + goto finalize; + return; + } + } + + /* Ack the interrupt. */ + + pos = head; + + while (pos != &__ipipe_pipeline) { + next_domain = list_entry(pos, struct ipipe_domain, p_link); + /* + * For each domain handling the incoming IRQ, mark it + * as pending in its log. + */ + if (test_bit(IPIPE_HANDLE_FLAG, &next_domain->irqs[irq].control)) { + /* + * Domains that handle this IRQ are polled for + * acknowledging it by decreasing priority + * order. The interrupt must be made pending + * _first_ in the domain's status flags before + * the PIC is unlocked. + */ + __ipipe_set_irq_pending(next_domain, irq); + + if (!m_ack && next_domain->irqs[irq].acknowledge != NULL) { + next_domain->irqs[irq].acknowledge(irq, irq_desc + irq); + m_ack = 1; + } + } + + /* + * If the domain does not want the IRQ to be passed + * down the interrupt pipe, exit the loop now. + */ + if (!test_bit(IPIPE_PASS_FLAG, &next_domain->irqs[irq].control)) + break; + + pos = next_domain->p_link.next; + } + + /* + * Now walk the pipeline, yielding control to the highest + * priority domain that has pending interrupt(s) or + * immediately to the current domain if the interrupt has been + * marked as 'sticky'. This search does not go beyond the + * current domain in the pipeline. We also enforce the + * additional root stage lock (blackfin-specific). */ + + if (test_bit(IPIPE_ROOTLOCK_FLAG, &ipipe_root_domain->flags)) + s = __test_and_set_bit(IPIPE_STALL_FLAG, + &ipipe_root_cpudom_var(status)); +finalize: + + __ipipe_walk_pipeline(head); + + if (!s) + __clear_bit(IPIPE_STALL_FLAG, + &ipipe_root_cpudom_var(status)); +} + +int __ipipe_check_root(void) +{ + return ipipe_root_domain_p; +} + +void __ipipe_enable_irqdesc(struct ipipe_domain *ipd, unsigned irq) +{ + struct irq_desc *desc = irq_desc + irq; + int prio = desc->ic_prio; + + desc->depth = 0; + if (ipd != &ipipe_root && + atomic_inc_return(&__ipipe_irq_lvdepth[prio]) == 1) + __set_bit(prio, &__ipipe_irq_lvmask); +} +EXPORT_SYMBOL(__ipipe_enable_irqdesc); + +void __ipipe_disable_irqdesc(struct ipipe_domain *ipd, unsigned irq) +{ + struct irq_desc *desc = irq_desc + irq; + int prio = desc->ic_prio; + + if (ipd != &ipipe_root && + atomic_dec_and_test(&__ipipe_irq_lvdepth[prio])) + __clear_bit(prio, &__ipipe_irq_lvmask); +} +EXPORT_SYMBOL(__ipipe_disable_irqdesc); + +void __ipipe_stall_root_raw(void) +{ + /* + * This code is called by the ins{bwl} routines (see + * arch/blackfin/lib/ins.S), which are heavily used by the + * network stack. It masks all interrupts but those handled by + * non-root domains, so that we keep decent network transfer + * rates for Linux without inducing pathological jitter for + * the real-time domain. + */ + __asm__ __volatile__ ("sti %0;" : : "d"(__ipipe_irq_lvmask)); + + __set_bit(IPIPE_STALL_FLAG, + &ipipe_root_cpudom_var(status)); +} + +void __ipipe_unstall_root_raw(void) +{ + __clear_bit(IPIPE_STALL_FLAG, + &ipipe_root_cpudom_var(status)); + + __asm__ __volatile__ ("sti %0;" : : "d"(bfin_irq_flags)); +} + +int __ipipe_syscall_root(struct pt_regs *regs) +{ + unsigned long flags; + + /* We need to run the IRQ tail hook whenever we don't + * propagate a syscall to higher domains, because we know that + * important operations might be pending there (e.g. Xenomai + * deferred rescheduling). */ + + if (!__ipipe_syscall_watched_p(current, regs->orig_p0)) { + void (*hook)(void) = (void (*)(void))__ipipe_irq_tail_hook; + hook(); + return 0; + } + + /* + * This routine either returns: + * 0 -- if the syscall is to be passed to Linux; + * 1 -- if the syscall should not be passed to Linux, and no + * tail work should be performed; + * -1 -- if the syscall should not be passed to Linux but the + * tail work has to be performed (for handling signals etc). + */ + + if (__ipipe_event_monitored_p(IPIPE_EVENT_SYSCALL) && + __ipipe_dispatch_event(IPIPE_EVENT_SYSCALL, regs) > 0) { + if (ipipe_root_domain_p && !in_atomic()) { + /* + * Sync pending VIRQs before _TIF_NEED_RESCHED + * is tested. + */ + local_irq_save_hw(flags); + if ((ipipe_root_cpudom_var(irqpend_himask) & IPIPE_IRQMASK_VIRT) != 0) + __ipipe_sync_pipeline(IPIPE_IRQMASK_VIRT); + local_irq_restore_hw(flags); + return -1; + } + return 1; + } + + return 0; +} + +unsigned long ipipe_critical_enter(void (*syncfn) (void)) +{ + unsigned long flags; + + local_irq_save_hw(flags); + + return flags; +} + +void ipipe_critical_exit(unsigned long flags) +{ + local_irq_restore_hw(flags); +} + +static void __ipipe_no_irqtail(void) +{ +} + +int ipipe_get_sysinfo(struct ipipe_sysinfo *info) +{ + info->ncpus = num_online_cpus(); + info->cpufreq = ipipe_cpu_freq(); + info->archdep.tmirq = IPIPE_TIMER_IRQ; + info->archdep.tmfreq = info->cpufreq; + + return 0; +} + +/* + * ipipe_trigger_irq() -- Push the interrupt at front of the pipeline + * just like if it has been actually received from a hw source. Also + * works for virtual interrupts. + */ +int ipipe_trigger_irq(unsigned irq) +{ + unsigned long flags; + + if (irq >= IPIPE_NR_IRQS || + (ipipe_virtual_irq_p(irq) + && !test_bit(irq - IPIPE_VIRQ_BASE, &__ipipe_virtual_irq_map))) + return -EINVAL; + + local_irq_save_hw(flags); + + __ipipe_handle_irq(irq, NULL); + + local_irq_restore_hw(flags); + + return 1; +} + +/* Move Linux IRQ to threads. */ + +static int do_irqd(void *__desc) +{ + struct irq_desc *desc = __desc; + unsigned irq = desc - irq_desc; + int thrprio = desc->thr_prio; + int thrmask = 1 << thrprio; + int cpu = smp_processor_id(); + cpumask_t cpumask; + + sigfillset(¤t->blocked); + current->flags |= PF_NOFREEZE; + cpumask = cpumask_of_cpu(cpu); + set_cpus_allowed(current, cpumask); + ipipe_setscheduler_root(current, SCHED_FIFO, 50 + thrprio); + + while (!kthread_should_stop()) { + local_irq_disable(); + if (!(desc->status & IRQ_SCHEDULED)) { + set_current_state(TASK_INTERRUPTIBLE); +resched: + local_irq_enable(); + schedule(); + local_irq_disable(); + } + __set_current_state(TASK_RUNNING); + /* + * If higher priority interrupt servers are ready to + * run, reschedule immediately. We need this for the + * GPIO demux IRQ handler to unmask the interrupt line + * _last_, after all GPIO IRQs have run. + */ + if (per_cpu(pending_irqthread_mask, cpu) & ~(thrmask|(thrmask-1))) + goto resched; + if (--per_cpu(pending_irq_count[thrprio], cpu) == 0) + per_cpu(pending_irqthread_mask, cpu) &= ~thrmask; + desc->status &= ~IRQ_SCHEDULED; + desc->thr_handler(irq, &__raw_get_cpu_var(__ipipe_tick_regs)); + local_irq_enable(); + } + __set_current_state(TASK_RUNNING); + return 0; +} + +static void kick_irqd(unsigned irq, void *cookie) +{ + struct irq_desc *desc = irq_desc + irq; + int thrprio = desc->thr_prio; + int thrmask = 1 << thrprio; + int cpu = smp_processor_id(); + + if (!(desc->status & IRQ_SCHEDULED)) { + desc->status |= IRQ_SCHEDULED; + per_cpu(pending_irqthread_mask, cpu) |= thrmask; + ++per_cpu(pending_irq_count[thrprio], cpu); + wake_up_process(desc->thread); + } +} + +int ipipe_start_irq_thread(unsigned irq, struct irq_desc *desc) +{ + if (desc->thread || !create_irq_threads) + return 0; + + desc->thread = kthread_create(do_irqd, desc, "IRQ %d", irq); + if (desc->thread == NULL) { + printk(KERN_ERR "irqd: could not create IRQ thread %d!\n", irq); + return -ENOMEM; + } + + wake_up_process(desc->thread); + + desc->thr_handler = ipipe_root_domain->irqs[irq].handler; + ipipe_root_domain->irqs[irq].handler = &kick_irqd; + + return 0; +} + +void __init ipipe_init_irq_threads(void) +{ + unsigned irq; + struct irq_desc *desc; + + create_irq_threads = 1; + + for (irq = 0; irq < NR_IRQS; irq++) { + desc = irq_desc + irq; + if (desc->action != NULL || + (desc->status & IRQ_NOREQUEST) != 0) + ipipe_start_irq_thread(irq, desc); + } +} + +EXPORT_SYMBOL(show_stack); + +#ifdef CONFIG_IPIPE_TRACE_MCOUNT +void notrace _mcount(void); +EXPORT_SYMBOL(_mcount); +#endif /* CONFIG_IPIPE_TRACE_MCOUNT */ diff --git a/arch/blackfin/kernel/irqchip.c b/arch/blackfin/kernel/irqchip.c index 1624e112968..ab8209cbbad 100644 --- a/arch/blackfin/kernel/irqchip.c +++ b/arch/blackfin/kernel/irqchip.c @@ -108,8 +108,9 @@ asmlinkage void asm_do_IRQ(unsigned int irq, struct pt_regs *regs) { struct pt_regs *old_regs; struct irq_desc *desc = irq_desc + irq; +#ifndef CONFIG_IPIPE unsigned short pending, other_ints; - +#endif old_regs = set_irq_regs(regs); /* @@ -137,6 +138,7 @@ asmlinkage void asm_do_IRQ(unsigned int irq, struct pt_regs *regs) #endif generic_handle_irq(irq); +#ifndef CONFIG_IPIPE /* Useless and bugous over the I-pipe: IRQs are threaded. */ /* If we're the only interrupt running (ignoring IRQ15 which is for syscalls), lower our priority to IRQ14 so that softirqs run at that level. If there's another, lower-level interrupt, irq_exit @@ -146,6 +148,7 @@ asmlinkage void asm_do_IRQ(unsigned int irq, struct pt_regs *regs) other_ints = pending & (pending - 1); if (other_ints == 0) lower_to_irq14(); +#endif /* !CONFIG_IPIPE */ irq_exit(); set_irq_regs(old_regs); diff --git a/arch/blackfin/kernel/mcount.S b/arch/blackfin/kernel/mcount.S new file mode 100644 index 00000000000..edcfb3865f4 --- /dev/null +++ b/arch/blackfin/kernel/mcount.S @@ -0,0 +1,70 @@ +/* + * linux/arch/blackfin/mcount.S + * + * Copyright (C) 2006 Analog Devices Inc. + * + * 2007/04/12 Save index, length, modify and base registers. --rpm + */ + +#include <linux/linkage.h> +#include <asm/blackfin.h> + +.text + +.align 4 /* just in case */ + +ENTRY(__mcount) + [--sp] = i0; + [--sp] = i1; + [--sp] = i2; + [--sp] = i3; + [--sp] = l0; + [--sp] = l1; + [--sp] = l2; + [--sp] = l3; + [--sp] = m0; + [--sp] = m1; + [--sp] = m2; + [--sp] = m3; + [--sp] = b0; + [--sp] = b1; + [--sp] = b2; + [--sp] = b3; + [--sp] = ( r7:0, p5:0 ); + [--sp] = ASTAT; + + p1.L = _ipipe_trace_enable; + p1.H = _ipipe_trace_enable; + r7 = [p1]; + CC = r7 == 0; + if CC jump out; + link 0x10; + r0 = 0x0; + [sp + 0xc] = r0; /* v */ + r0 = 0x0; /* type: IPIPE_TRACE_FN */ + r1 = rets; + p0 = [fp]; /* p0: Prior FP */ + r2 = [p0 + 4]; /* r2: Prior RETS */ + call ___ipipe_trace; + unlink; +out: + ASTAT = [sp++]; + ( r7:0, p5:0 ) = [sp++]; + b3 = [sp++]; + b2 = [sp++]; + b1 = [sp++]; + b0 = [sp++]; + m3 = [sp++]; + m2 = [sp++]; + m1 = [sp++]; + m0 = [sp++]; + l3 = [sp++]; + l2 = [sp++]; + l1 = [sp++]; + l0 = [sp++]; + i3 = [sp++]; + i2 = [sp++]; + i1 = [sp++]; + i0 = [sp++]; + rts; +ENDPROC(__mcount) diff --git a/arch/blackfin/kernel/process.c b/arch/blackfin/kernel/process.c index 1ec0faa8c68..33e2e8993f7 100644 --- a/arch/blackfin/kernel/process.c +++ b/arch/blackfin/kernel/process.c @@ -82,11 +82,14 @@ void cpu_idle(void)__attribute__((l1_text)); */ static void default_idle(void) { - local_irq_disable(); +#ifdef CONFIG_IPIPE + ipipe_suspend_domain(); +#endif + local_irq_disable_hw(); if (!need_resched()) idle_with_irq_disabled(); - local_irq_enable(); + local_irq_enable_hw(); } /* diff --git a/arch/blackfin/kernel/time.c b/arch/blackfin/kernel/time.c index ec4dfa38eb0..172b4c58846 100644 --- a/arch/blackfin/kernel/time.c +++ b/arch/blackfin/kernel/time.c @@ -31,7 +31,7 @@ static struct irqaction bfin_timer_irq = { #endif }; -#ifdef CONFIG_TICK_SOURCE_SYSTMR0 +#if defined(CONFIG_TICK_SOURCE_SYSTMR0) || defined(CONFIG_IPIPE) void __init setup_system_timer0(void) { /* Power down the core timer, just to play safe. */ @@ -74,7 +74,7 @@ void __init setup_core_timer(void) static void __init time_sched_init(irqreturn_t(*timer_routine) (int, void *)) { -#ifdef CONFIG_TICK_SOURCE_SYSTMR0 +#if defined(CONFIG_TICK_SOURCE_SYSTMR0) || defined(CONFIG_IPIPE) setup_system_timer0(); bfin_timer_irq.handler = timer_routine; setup_irq(IRQ_TIMER0, &bfin_timer_irq); @@ -94,7 +94,7 @@ static unsigned long gettimeoffset(void) unsigned long offset; unsigned long clocks_per_jiffy; -#ifdef CONFIG_TICK_SOURCE_SYSTMR0 +#if defined(CONFIG_TICK_SOURCE_SYSTMR0) || defined(CONFIG_IPIPE) clocks_per_jiffy = bfin_read_TIMER0_PERIOD(); offset = bfin_read_TIMER0_COUNTER() / \ (((clocks_per_jiffy + 1) * HZ) / USEC_PER_SEC); @@ -133,7 +133,8 @@ irqreturn_t timer_interrupt(int irq, void *dummy) static long last_rtc_update; write_seqlock(&xtime_lock); -#ifdef CONFIG_TICK_SOURCE_SYSTMR0 +#if defined(CONFIG_TICK_SOURCE_SYSTMR0) && !defined(CONFIG_IPIPE) +/* FIXME: Here TIMIL0 is not set when IPIPE enabled, why? */ if (get_gptimer_status(0) & TIMER_STATUS_TIMIL0) { #endif do_timer(1); @@ -155,13 +156,17 @@ irqreturn_t timer_interrupt(int irq, void *dummy) /* Do it again in 60s. */ last_rtc_update = xtime.tv_sec - 600; } -#ifdef CONFIG_TICK_SOURCE_SYSTMR0 +#if defined(CONFIG_TICK_SOURCE_SYSTMR0) && !defined(CONFIG_IPIPE) set_gptimer_status(0, TIMER_STATUS_TIMIL0); } #endif write_sequnlock(&xtime_lock); +#ifdef CONFIG_IPIPE + update_root_process_times(get_irq_regs()); +#else update_process_times(user_mode(get_irq_regs())); +#endif profile_tick(CPU_PROFILING); return IRQ_HANDLED; diff --git a/arch/blackfin/kernel/traps.c b/arch/blackfin/kernel/traps.c index 950cc822fb7..956aefb8468 100644 --- a/arch/blackfin/kernel/traps.c +++ b/arch/blackfin/kernel/traps.c @@ -577,10 +577,15 @@ asmlinkage void trap_c(struct pt_regs *fp) } } - info.si_signo = sig; - info.si_errno = 0; - info.si_addr = (void __user *)fp->pc; - force_sig_info(sig, &info, current); +#ifdef CONFIG_IPIPE + if (!ipipe_trap_notify(fp->seqstat & 0x3f, fp)) +#endif + { + info.si_signo = sig; + info.si_errno = 0; + info.si_addr = (void __user *)fp->pc; + force_sig_info(sig, &info, current); + } trace_buffer_restore(j); return; |