diff options
author | Ralf Baechle <ralf@linux-mips.org> | 2007-07-27 20:31:10 +0200 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 2007-07-31 22:35:24 +0200 |
commit | 07cc0c9e65d3e262f871ea357dd77b41950b1ca5 (patch) | |
tree | fa797fa236da6e03c7b778cdc364ac1c3e03b52f /arch/mips/kernel/vpe.c | |
parent | [MIPS] SMTC: Safety net for i8259A interrupts. (diff) | |
download | linux-07cc0c9e65d3e262f871ea357dd77b41950b1ca5.tar.xz linux-07cc0c9e65d3e262f871ea357dd77b41950b1ca5.zip |
[MIPS] MT: Enable coexistence of AP/SP with VSMP and SMTC.
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
Diffstat (limited to 'arch/mips/kernel/vpe.c')
-rw-r--r-- | arch/mips/kernel/vpe.c | 263 |
1 files changed, 137 insertions, 126 deletions
diff --git a/arch/mips/kernel/vpe.c b/arch/mips/kernel/vpe.c index a2bee10f04cf..c726c47cd2c3 100644 --- a/arch/mips/kernel/vpe.c +++ b/arch/mips/kernel/vpe.c @@ -27,7 +27,6 @@ * To load and run, simply cat a SP 'program file' to /dev/vpe1. * i.e cat spapp >/dev/vpe1. */ - #include <linux/kernel.h> #include <linux/device.h> #include <linux/module.h> @@ -54,6 +53,7 @@ #include <asm/system.h> #include <asm/vpe.h> #include <asm/kspd.h> +#include <asm/mips_mt.h> typedef void *vpe_handle; @@ -132,14 +132,9 @@ struct tc { enum tc_state state; int index; - /* parent VPE */ - struct vpe *pvpe; - - /* The list of TC's with this VPE */ - struct list_head tc; - - /* The global list of tc's */ - struct list_head list; + struct vpe *pvpe; /* parent VPE */ + struct list_head tc; /* The list of TC's with this VPE */ + struct list_head list; /* The global list of tc's */ }; struct { @@ -217,18 +212,17 @@ struct vpe *alloc_vpe(int minor) /* allocate a tc. At startup only tc0 is running, all other can be halted. */ struct tc *alloc_tc(int index) { - struct tc *t; - - if ((t = kzalloc(sizeof(struct tc), GFP_KERNEL)) == NULL) { - return NULL; - } + struct tc *tc; - INIT_LIST_HEAD(&t->tc); - list_add_tail(&t->list, &vpecontrol.tc_list); + if ((tc = kzalloc(sizeof(struct tc), GFP_KERNEL)) == NULL) + goto out; - t->index = index; + INIT_LIST_HEAD(&tc->tc); + tc->index = index; + list_add_tail(&tc->list, &vpecontrol.tc_list); - return t; +out: + return tc; } /* clean up and free everything */ @@ -663,66 +657,48 @@ static void dump_elfsymbols(Elf_Shdr * sechdrs, unsigned int symindex, } #endif -static void dump_tc(struct tc *t) -{ - unsigned long val; - - settc(t->index); - printk(KERN_DEBUG "VPE loader: TC index %d targtc %ld " - "TCStatus 0x%lx halt 0x%lx\n", - t->index, read_c0_vpecontrol() & VPECONTROL_TARGTC, - read_tc_c0_tcstatus(), read_tc_c0_tchalt()); - - printk(KERN_DEBUG " tcrestart 0x%lx\n", read_tc_c0_tcrestart()); - printk(KERN_DEBUG " tcbind 0x%lx\n", read_tc_c0_tcbind()); - - val = read_c0_vpeconf0(); - printk(KERN_DEBUG " VPEConf0 0x%lx MVP %ld\n", val, - (val & VPECONF0_MVP) >> VPECONF0_MVP_SHIFT); - - printk(KERN_DEBUG " c0 status 0x%lx\n", read_vpe_c0_status()); - printk(KERN_DEBUG " c0 cause 0x%lx\n", read_vpe_c0_cause()); - - printk(KERN_DEBUG " c0 badvaddr 0x%lx\n", read_vpe_c0_badvaddr()); - printk(KERN_DEBUG " c0 epc 0x%lx\n", read_vpe_c0_epc()); -} - -static void dump_tclist(void) -{ - struct tc *t; - - list_for_each_entry(t, &vpecontrol.tc_list, list) { - dump_tc(t); - } -} - /* We are prepared so configure and start the VPE... */ static int vpe_run(struct vpe * v) { + unsigned long flags, val, dmt_flag; struct vpe_notifications *n; - unsigned long val, dmt_flag; + unsigned int vpeflags; struct tc *t; /* check we are the Master VPE */ + local_irq_save(flags); val = read_c0_vpeconf0(); if (!(val & VPECONF0_MVP)) { printk(KERN_WARNING "VPE loader: only Master VPE's are allowed to configure MT\n"); + local_irq_restore(flags); + return -1; } - /* disable MT (using dvpe) */ - dvpe(); + dmt_flag = dmt(); + vpeflags = dvpe(); if (!list_empty(&v->tc)) { if ((t = list_entry(v->tc.next, struct tc, tc)) == NULL) { - printk(KERN_WARNING "VPE loader: TC %d is already in use.\n", - t->index); + evpe(vpeflags); + emt(dmt_flag); + local_irq_restore(flags); + + printk(KERN_WARNING + "VPE loader: TC %d is already in use.\n", + t->index); return -ENOEXEC; } } else { - printk(KERN_WARNING "VPE loader: No TC's associated with VPE %d\n", + evpe(vpeflags); + emt(dmt_flag); + local_irq_restore(flags); + + printk(KERN_WARNING + "VPE loader: No TC's associated with VPE %d\n", v->minor); + return -ENOEXEC; } @@ -733,21 +709,20 @@ static int vpe_run(struct vpe * v) /* should check it is halted, and not activated */ if ((read_tc_c0_tcstatus() & TCSTATUS_A) || !(read_tc_c0_tchalt() & TCHALT_H)) { - printk(KERN_WARNING "VPE loader: TC %d is already doing something!\n", + evpe(vpeflags); + emt(dmt_flag); + local_irq_restore(flags); + + printk(KERN_WARNING "VPE loader: TC %d is already active!\n", t->index); - dump_tclist(); + return -ENOEXEC; } - /* - * Disable multi-threaded execution whilst we activate, clear the - * halt bit and bound the tc to the other VPE... - */ - dmt_flag = dmt(); - /* Write the address we want it to start running from in the TCPC register. */ write_tc_c0_tcrestart((unsigned long)v->__start); write_tc_c0_tccontext((unsigned long)0); + /* * Mark the TC as activated, not interrupt exempt and not dynamically * allocatable @@ -763,15 +738,14 @@ static int vpe_run(struct vpe * v) * here... Or set $a3 to zero and define DFLT_STACK_SIZE and * DFLT_HEAP_SIZE when you compile your program */ - mttgpr(7, physical_memsize); - + mttgpr(7, physical_memsize); /* set up VPE1 */ /* * bind the TC to VPE 1 as late as possible so we only have the final * VPE registers to set up, and so an EJTAG probe can trigger on it */ - write_tc_c0_tcbind((read_tc_c0_tcbind() & ~TCBIND_CURVPE) | v->minor); + write_tc_c0_tcbind((read_tc_c0_tcbind() & ~TCBIND_CURVPE) | 1); write_vpe_c0_vpeconf0(read_vpe_c0_vpeconf0() & ~(VPECONF0_VPA)); @@ -793,15 +767,16 @@ static int vpe_run(struct vpe * v) /* take system out of configuration state */ clear_c0_mvpcontrol(MVPCONTROL_VPC); - /* now safe to re-enable multi-threading */ - emt(dmt_flag); - - /* set it running */ +#ifdef CONFIG_SMP evpe(EVPE_ENABLE); +#else + evpe(vpeflags); +#endif + emt(dmt_flag); + local_irq_restore(flags); - list_for_each_entry(n, &v->notify, list) { - n->start(v->minor); - } + list_for_each_entry(n, &v->notify, list) + n->start(minor); return 0; } @@ -1023,23 +998,15 @@ static int vpe_elfload(struct vpe * v) return 0; } -void __used dump_vpe(struct vpe * v) -{ - struct tc *t; - - settc(v->minor); - - printk(KERN_DEBUG "VPEControl 0x%lx\n", read_vpe_c0_vpecontrol()); - printk(KERN_DEBUG "VPEConf0 0x%lx\n", read_vpe_c0_vpeconf0()); - - list_for_each_entry(t, &vpecontrol.tc_list, list) - dump_tc(t); -} - static void cleanup_tc(struct tc *tc) { + unsigned long flags; + unsigned int mtflags, vpflags; int tmp; + local_irq_save(flags); + mtflags = dmt(); + vpflags = dvpe(); /* Put MVPE's into 'configuration state' */ set_c0_mvpcontrol(MVPCONTROL_VPC); @@ -1054,9 +1021,12 @@ static void cleanup_tc(struct tc *tc) write_tc_c0_tchalt(TCHALT_H); /* bind it to anything other than VPE1 */ - write_tc_c0_tcbind(read_tc_c0_tcbind() & ~TCBIND_CURVPE); // | TCBIND_CURVPE +// write_tc_c0_tcbind(read_tc_c0_tcbind() & ~TCBIND_CURVPE); // | TCBIND_CURVPE clear_c0_mvpcontrol(MVPCONTROL_VPC); + evpe(vpflags); + emt(mtflags); + local_irq_restore(flags); } static int getcwd(char *buff, int size) @@ -1077,36 +1047,32 @@ static int getcwd(char *buff, int size) /* checks VPE is unused and gets ready to load program */ static int vpe_open(struct inode *inode, struct file *filp) { - int minor, ret; enum vpe_state state; - struct vpe *v; struct vpe_notifications *not; + struct vpe *v; + int ret; - /* assume only 1 device at the mo. */ - if ((minor = iminor(inode)) != 1) { + if (minor != iminor(inode)) { + /* assume only 1 device at the moment. */ printk(KERN_WARNING "VPE loader: only vpe1 is supported\n"); return -ENODEV; } - if ((v = get_vpe(minor)) == NULL) { + if ((v = get_vpe(tclimit)) == NULL) { printk(KERN_WARNING "VPE loader: unable to get vpe\n"); return -ENODEV; } state = xchg(&v->state, VPE_STATE_INUSE); if (state != VPE_STATE_UNUSED) { - dvpe(); - printk(KERN_DEBUG "VPE loader: tc in use dumping regs\n"); - dump_tc(get_tc(minor)); - list_for_each_entry(not, &v->notify, list) { - not->stop(minor); + not->stop(tclimit); } release_progmem(v->load_addr); - cleanup_tc(get_tc(minor)); + cleanup_tc(get_tc(tclimit)); } /* this of-course trashes what was there before... */ @@ -1133,26 +1099,25 @@ static int vpe_open(struct inode *inode, struct file *filp) v->shared_ptr = NULL; v->__start = 0; + return 0; } static int vpe_release(struct inode *inode, struct file *filp) { - int minor, ret = 0; struct vpe *v; Elf_Ehdr *hdr; + int ret = 0; - minor = iminor(inode); - if ((v = get_vpe(minor)) == NULL) + v = get_vpe(tclimit); + if (v == NULL) return -ENODEV; - // simple case of fire and forget, so tell the VPE to run... - hdr = (Elf_Ehdr *) v->pbuffer; if (memcmp(hdr->e_ident, ELFMAG, 4) == 0) { - if (vpe_elfload(v) >= 0) + if (vpe_elfload(v) >= 0) { vpe_run(v); - else { + } else { printk(KERN_WARNING "VPE loader: ELF load failed.\n"); ret = -ENOEXEC; } @@ -1179,12 +1144,14 @@ static int vpe_release(struct inode *inode, struct file *filp) static ssize_t vpe_write(struct file *file, const char __user * buffer, size_t count, loff_t * ppos) { - int minor; size_t ret = count; struct vpe *v; - minor = iminor(file->f_path.dentry->d_inode); - if ((v = get_vpe(minor)) == NULL) + if (iminor(file->f_path.dentry->d_inode) != minor) + return -ENODEV; + + v = get_vpe(tclimit); + if (v == NULL) return -ENODEV; if (v->pbuffer == NULL) { @@ -1370,17 +1337,34 @@ static struct device *vpe_dev; static int __init vpe_module_init(void) { + unsigned int mtflags, vpflags; + int hw_tcs, hw_vpes, tc, err = 0; + unsigned long flags, val; struct vpe *v = NULL; struct device *dev; struct tc *t; - unsigned long val; - int i, err; if (!cpu_has_mipsmt) { printk("VPE loader: not a MIPS MT capable processor\n"); return -ENODEV; } + if (vpelimit == 0) { + printk(KERN_WARNING "No VPEs reserved for AP/SP, not " + "initializing VPE loader.\nPass maxvpes=<n> argument as " + "kernel argument\n"); + + return -ENODEV; + } + + if (tclimit == 0) { + printk(KERN_WARNING "No TCs reserved for AP/SP, not " + "initializing VPE loader.\nPass maxtcs=<n> argument as " + "kernel argument\n"); + + return -ENODEV; + } + major = register_chrdev(0, module_name, &vpe_fops); if (major < 0) { printk("VPE loader: unable to register character device\n"); @@ -1388,40 +1372,61 @@ static int __init vpe_module_init(void) } dev = device_create(mt_class, NULL, MKDEV(major, minor), - "tc%d", minor); + "vpe%d", minor); if (IS_ERR(dev)) { err = PTR_ERR(dev); goto out_chrdev; } vpe_dev = dev; - dmt(); - dvpe(); + local_irq_save(flags); + mtflags = dmt(); + vpflags = dvpe(); /* Put MVPE's into 'configuration state' */ set_c0_mvpcontrol(MVPCONTROL_VPC); /* dump_mtregs(); */ - val = read_c0_mvpconf0(); - for (i = 0; i < ((val & MVPCONF0_PTC) + 1); i++) { - t = alloc_tc(i); + hw_tcs = (val & MVPCONF0_PTC) + 1; + hw_vpes = ((val & MVPCONF0_PVPE) >> MVPCONF0_PVPE_SHIFT) + 1; + + for (tc = tclimit; tc < hw_tcs; tc++) { + /* + * Must re-enable multithreading temporarily or in case we + * reschedule send IPIs or similar we might hang. + */ + clear_c0_mvpcontrol(MVPCONTROL_VPC); + evpe(vpflags); + emt(mtflags); + local_irq_restore(flags); + t = alloc_tc(tc); + if (!t) { + err = -ENOMEM; + goto out; + } + + local_irq_save(flags); + mtflags = dmt(); + vpflags = dvpe(); + set_c0_mvpcontrol(MVPCONTROL_VPC); /* VPE's */ - if (i < ((val & MVPCONF0_PVPE) >> MVPCONF0_PVPE_SHIFT) + 1) { - settc(i); + if (tc < hw_tcs) { + settc(tc); - if ((v = alloc_vpe(i)) == NULL) { + if ((v = alloc_vpe(tc)) == NULL) { printk(KERN_WARNING "VPE: unable to allocate VPE\n"); - return -ENODEV; + + goto out_reenable; } /* add the tc to the list of this vpe's tc's. */ list_add(&t->tc, &v->tc); /* deactivate all but vpe0 */ - if (i != 0) { + if (tc >= tclimit) { unsigned long tmp = read_vpe_c0_vpeconf0(); tmp &= ~VPECONF0_VPA; @@ -1434,7 +1439,7 @@ static int __init vpe_module_init(void) /* disable multi-threading with TC's */ write_vpe_c0_vpecontrol(read_vpe_c0_vpecontrol() & ~VPECONTROL_TE); - if (i != 0) { + if (tc >= vpelimit) { /* * Set config to be the same as vpe0, * particularly kseg0 coherency alg @@ -1446,10 +1451,10 @@ static int __init vpe_module_init(void) /* TC's */ t->pvpe = v; /* set the parent vpe */ - if (i != 0) { + if (tc >= tclimit) { unsigned long tmp; - settc(i); + settc(tc); /* Any TC that is bound to VPE0 gets left as is - in case we are running SMTC on VPE0. A TC that is bound to any @@ -1479,9 +1484,14 @@ static int __init vpe_module_init(void) } } +out_reenable: /* release config state */ clear_c0_mvpcontrol(MVPCONTROL_VPC); + evpe(vpflags); + emt(mtflags); + local_irq_restore(flags); + #ifdef CONFIG_MIPS_APSP_KSPD kspd_events.kspd_sp_exit = kspd_sp_exit; #endif @@ -1490,6 +1500,7 @@ static int __init vpe_module_init(void) out_chrdev: unregister_chrdev(major, module_name); +out: return err; } |