123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522 |
- /*
- * This file is subject to the terms and conditions of the GNU General Public
- * License. See the file "COPYING" in the main directory of this archive
- * for more details.
- *
- * Copyright (C) 2004, 2005 MIPS Technologies, Inc. All rights reserved.
- * Copyright (C) 2013 Imagination Technologies Ltd.
- */
- #include <linux/kernel.h>
- #include <linux/device.h>
- #include <linux/fs.h>
- #include <linux/slab.h>
- #include <linux/export.h>
- #include <asm/mipsregs.h>
- #include <asm/mipsmtregs.h>
- #include <asm/mips_mt.h>
- #include <asm/vpe.h>
- static int major;
- /* The number of TCs and VPEs physically available on the core */
- static int hw_tcs, hw_vpes;
- /* We are prepared so configure and start the VPE... */
- int vpe_run(struct vpe *v)
- {
- unsigned long flags, val, dmt_flag;
- struct vpe_notifications *notifier;
- unsigned int vpeflags;
- struct tc *t;
- /* check we are the Master VPE */
- local_irq_save(flags);
- val = read_c0_vpeconf0();
- if (!(val & VPECONF0_MVP)) {
- pr_warn("VPE loader: only Master VPE's are able to config MT\n");
- local_irq_restore(flags);
- return -1;
- }
- dmt_flag = dmt();
- vpeflags = dvpe();
- if (list_empty(&v->tc)) {
- evpe(vpeflags);
- emt(dmt_flag);
- local_irq_restore(flags);
- pr_warn("VPE loader: No TC's associated with VPE %d\n",
- v->minor);
- return -ENOEXEC;
- }
- t = list_first_entry(&v->tc, struct tc, tc);
- /* Put MVPE's into 'configuration state' */
- set_c0_mvpcontrol(MVPCONTROL_VPC);
- settc(t->index);
- /* should check it is halted, and not activated */
- if ((read_tc_c0_tcstatus() & TCSTATUS_A) ||
- !(read_tc_c0_tchalt() & TCHALT_H)) {
- evpe(vpeflags);
- emt(dmt_flag);
- local_irq_restore(flags);
- pr_warn("VPE loader: TC %d is already active!\n",
- t->index);
- return -ENOEXEC;
- }
- /*
- * 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
- */
- val = read_tc_c0_tcstatus();
- val = (val & ~(TCSTATUS_DA | TCSTATUS_IXMT)) | TCSTATUS_A;
- write_tc_c0_tcstatus(val);
- write_tc_c0_tchalt(read_tc_c0_tchalt() & ~TCHALT_H);
- /*
- * The sde-kit passes 'memsize' to __start in $a3, so set something
- * here... Or set $a3 to zero and define DFLT_STACK_SIZE and
- * DFLT_HEAP_SIZE when you compile your program
- */
- mttgpr(6, v->ntcs);
- 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) | 1);
- write_vpe_c0_vpeconf0(read_vpe_c0_vpeconf0() & ~(VPECONF0_VPA));
- back_to_back_c0_hazard();
- /* Set up the XTC bit in vpeconf0 to point at our tc */
- write_vpe_c0_vpeconf0((read_vpe_c0_vpeconf0() & ~(VPECONF0_XTC))
- | (t->index << VPECONF0_XTC_SHIFT));
- back_to_back_c0_hazard();
- /* enable this VPE */
- write_vpe_c0_vpeconf0(read_vpe_c0_vpeconf0() | VPECONF0_VPA);
- /* clear out any left overs from a previous program */
- write_vpe_c0_status(0);
- write_vpe_c0_cause(0);
- /* take system out of configuration state */
- clear_c0_mvpcontrol(MVPCONTROL_VPC);
- /*
- * SMVP kernels manage VPE enable independently, but uniprocessor
- * kernels need to turn it on, even if that wasn't the pre-dvpe() state.
- */
- #ifdef CONFIG_SMP
- evpe(vpeflags);
- #else
- evpe(EVPE_ENABLE);
- #endif
- emt(dmt_flag);
- local_irq_restore(flags);
- list_for_each_entry(notifier, &v->notify, list)
- notifier->start(VPE_MODULE_MINOR);
- return 0;
- }
- 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);
- settc(tc->index);
- tmp = read_tc_c0_tcstatus();
- /* mark not allocated and not dynamically allocatable */
- tmp &= ~(TCSTATUS_A | TCSTATUS_DA);
- tmp |= TCSTATUS_IXMT; /* interrupt exempt */
- write_tc_c0_tcstatus(tmp);
- write_tc_c0_tchalt(TCHALT_H);
- mips_ihb();
- clear_c0_mvpcontrol(MVPCONTROL_VPC);
- evpe(vpflags);
- emt(mtflags);
- local_irq_restore(flags);
- }
- /* module wrapper entry points */
- /* give me a vpe */
- void *vpe_alloc(void)
- {
- int i;
- struct vpe *v;
- /* find a vpe */
- for (i = 1; i < MAX_VPES; i++) {
- v = get_vpe(i);
- if (v != NULL) {
- v->state = VPE_STATE_INUSE;
- return v;
- }
- }
- return NULL;
- }
- EXPORT_SYMBOL(vpe_alloc);
- /* start running from here */
- int vpe_start(void *vpe, unsigned long start)
- {
- struct vpe *v = vpe;
- v->__start = start;
- return vpe_run(v);
- }
- EXPORT_SYMBOL(vpe_start);
- /* halt it for now */
- int vpe_stop(void *vpe)
- {
- struct vpe *v = vpe;
- struct tc *t;
- unsigned int evpe_flags;
- evpe_flags = dvpe();
- t = list_entry(v->tc.next, struct tc, tc);
- if (t != NULL) {
- settc(t->index);
- write_vpe_c0_vpeconf0(read_vpe_c0_vpeconf0() & ~VPECONF0_VPA);
- }
- evpe(evpe_flags);
- return 0;
- }
- EXPORT_SYMBOL(vpe_stop);
- /* I've done with it thank you */
- int vpe_free(void *vpe)
- {
- struct vpe *v = vpe;
- struct tc *t;
- unsigned int evpe_flags;
- t = list_entry(v->tc.next, struct tc, tc);
- if (t == NULL)
- return -ENOEXEC;
- evpe_flags = dvpe();
- /* Put MVPE's into 'configuration state' */
- set_c0_mvpcontrol(MVPCONTROL_VPC);
- settc(t->index);
- write_vpe_c0_vpeconf0(read_vpe_c0_vpeconf0() & ~VPECONF0_VPA);
- /* halt the TC */
- write_tc_c0_tchalt(TCHALT_H);
- mips_ihb();
- /* mark the TC unallocated */
- write_tc_c0_tcstatus(read_tc_c0_tcstatus() & ~TCSTATUS_A);
- v->state = VPE_STATE_UNUSED;
- clear_c0_mvpcontrol(MVPCONTROL_VPC);
- evpe(evpe_flags);
- return 0;
- }
- EXPORT_SYMBOL(vpe_free);
- static ssize_t store_kill(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t len)
- {
- struct vpe *vpe = get_vpe(aprp_cpu_index());
- struct vpe_notifications *notifier;
- list_for_each_entry(notifier, &vpe->notify, list)
- notifier->stop(aprp_cpu_index());
- release_progmem(vpe->load_addr);
- cleanup_tc(get_tc(aprp_cpu_index()));
- vpe_stop(vpe);
- vpe_free(vpe);
- return len;
- }
- static DEVICE_ATTR(kill, S_IWUSR, NULL, store_kill);
- static ssize_t ntcs_show(struct device *cd, struct device_attribute *attr,
- char *buf)
- {
- struct vpe *vpe = get_vpe(aprp_cpu_index());
- return sprintf(buf, "%d\n", vpe->ntcs);
- }
- static ssize_t ntcs_store(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t len)
- {
- struct vpe *vpe = get_vpe(aprp_cpu_index());
- unsigned long new;
- int ret;
- ret = kstrtoul(buf, 0, &new);
- if (ret < 0)
- return ret;
- if (new == 0 || new > (hw_tcs - aprp_cpu_index()))
- return -EINVAL;
- vpe->ntcs = new;
- return len;
- }
- static DEVICE_ATTR_RW(ntcs);
- static struct attribute *vpe_attrs[] = {
- &dev_attr_kill.attr,
- &dev_attr_ntcs.attr,
- NULL,
- };
- ATTRIBUTE_GROUPS(vpe);
- static void vpe_device_release(struct device *cd)
- {
- kfree(cd);
- }
- static struct class vpe_class = {
- .name = "vpe",
- .owner = THIS_MODULE,
- .dev_release = vpe_device_release,
- .dev_groups = vpe_groups,
- };
- static struct device vpe_device;
- int __init vpe_module_init(void)
- {
- unsigned int mtflags, vpflags;
- unsigned long flags, val;
- struct vpe *v = NULL;
- struct tc *t;
- int tc, err;
- if (!cpu_has_mipsmt) {
- pr_warn("VPE loader: not a MIPS MT capable processor\n");
- return -ENODEV;
- }
- if (vpelimit == 0) {
- pr_warn("No VPEs reserved for AP/SP, not initialize VPE loader\n"
- "Pass maxvpes=<n> argument as kernel argument\n");
- return -ENODEV;
- }
- if (aprp_cpu_index() == 0) {
- pr_warn("No TCs reserved for AP/SP, not initialize VPE loader\n"
- "Pass maxtcs=<n> argument as kernel argument\n");
- return -ENODEV;
- }
- major = register_chrdev(0, VPE_MODULE_NAME, &vpe_fops);
- if (major < 0) {
- pr_warn("VPE loader: unable to register character device\n");
- return major;
- }
- err = class_register(&vpe_class);
- if (err) {
- pr_err("vpe_class registration failed\n");
- goto out_chrdev;
- }
- device_initialize(&vpe_device);
- vpe_device.class = &vpe_class,
- vpe_device.parent = NULL,
- dev_set_name(&vpe_device, "vpe1");
- vpe_device.devt = MKDEV(major, VPE_MODULE_MINOR);
- err = device_add(&vpe_device);
- if (err) {
- pr_err("Adding vpe_device failed\n");
- goto out_class;
- }
- local_irq_save(flags);
- mtflags = dmt();
- vpflags = dvpe();
- /* Put MVPE's into 'configuration state' */
- set_c0_mvpcontrol(MVPCONTROL_VPC);
- val = read_c0_mvpconf0();
- hw_tcs = (val & MVPCONF0_PTC) + 1;
- hw_vpes = ((val & MVPCONF0_PVPE) >> MVPCONF0_PVPE_SHIFT) + 1;
- for (tc = aprp_cpu_index(); 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_dev;
- }
- local_irq_save(flags);
- mtflags = dmt();
- vpflags = dvpe();
- set_c0_mvpcontrol(MVPCONTROL_VPC);
- /* VPE's */
- if (tc < hw_tcs) {
- settc(tc);
- v = alloc_vpe(tc);
- if (v == NULL) {
- pr_warn("VPE: unable to allocate VPE\n");
- goto out_reenable;
- }
- v->ntcs = hw_tcs - aprp_cpu_index();
- /* add the tc to the list of this vpe's tc's. */
- list_add(&t->tc, &v->tc);
- /* deactivate all but vpe0 */
- if (tc >= aprp_cpu_index()) {
- unsigned long tmp = read_vpe_c0_vpeconf0();
- tmp &= ~VPECONF0_VPA;
- /* master VPE */
- tmp |= VPECONF0_MVP;
- write_vpe_c0_vpeconf0(tmp);
- }
- /* disable multi-threading with TC's */
- write_vpe_c0_vpecontrol(read_vpe_c0_vpecontrol() &
- ~VPECONTROL_TE);
- if (tc >= vpelimit) {
- /*
- * Set config to be the same as vpe0,
- * particularly kseg0 coherency alg
- */
- write_vpe_c0_config(read_c0_config());
- }
- }
- /* TC's */
- t->pvpe = v; /* set the parent vpe */
- if (tc >= aprp_cpu_index()) {
- unsigned long tmp;
- settc(tc);
- /*
- * A TC that is bound to any other VPE gets bound to
- * VPE0, ideally I'd like to make it homeless but it
- * doesn't appear to let me bind a TC to a non-existent
- * VPE. Which is perfectly reasonable.
- *
- * The (un)bound state is visible to an EJTAG probe so
- * may notify GDB...
- */
- tmp = read_tc_c0_tcbind();
- if (tmp & TCBIND_CURVPE) {
- /* tc is bound >vpe0 */
- write_tc_c0_tcbind(tmp & ~TCBIND_CURVPE);
- t->pvpe = get_vpe(0); /* set the parent vpe */
- }
- /* halt the TC */
- write_tc_c0_tchalt(TCHALT_H);
- mips_ihb();
- tmp = read_tc_c0_tcstatus();
- /* mark not activated and not dynamically allocatable */
- tmp &= ~(TCSTATUS_A | TCSTATUS_DA);
- tmp |= TCSTATUS_IXMT; /* interrupt exempt */
- write_tc_c0_tcstatus(tmp);
- }
- }
- out_reenable:
- /* release config state */
- clear_c0_mvpcontrol(MVPCONTROL_VPC);
- evpe(vpflags);
- emt(mtflags);
- local_irq_restore(flags);
- return 0;
- out_dev:
- device_del(&vpe_device);
- out_class:
- class_unregister(&vpe_class);
- out_chrdev:
- unregister_chrdev(major, VPE_MODULE_NAME);
- return err;
- }
- void __exit vpe_module_exit(void)
- {
- struct vpe *v, *n;
- device_del(&vpe_device);
- class_unregister(&vpe_class);
- unregister_chrdev(major, VPE_MODULE_NAME);
- /* No locking needed here */
- list_for_each_entry_safe(v, n, &vpecontrol.vpe_list, list) {
- if (v->state != VPE_STATE_UNUSED)
- release_vpe(v);
- }
- }
|