123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522 |
- #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;
- static int hw_tcs, hw_vpes;
- int vpe_run(struct vpe *v)
- {
- unsigned long flags, val, dmt_flag;
- struct vpe_notifications *notifier;
- unsigned int vpeflags;
- struct tc *t;
-
- 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);
-
- set_c0_mvpcontrol(MVPCONTROL_VPC);
- settc(t->index);
-
- 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_tc_c0_tcrestart((unsigned long)v->__start);
- write_tc_c0_tccontext((unsigned long)0);
-
- 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);
-
- mttgpr(6, v->ntcs);
- mttgpr(7, physical_memsize);
-
-
- 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();
-
- write_vpe_c0_vpeconf0((read_vpe_c0_vpeconf0() & ~(VPECONF0_XTC))
- | (t->index << VPECONF0_XTC_SHIFT));
- back_to_back_c0_hazard();
-
- write_vpe_c0_vpeconf0(read_vpe_c0_vpeconf0() | VPECONF0_VPA);
-
- write_vpe_c0_status(0);
- write_vpe_c0_cause(0);
-
- clear_c0_mvpcontrol(MVPCONTROL_VPC);
-
- #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();
-
- set_c0_mvpcontrol(MVPCONTROL_VPC);
- settc(tc->index);
- tmp = read_tc_c0_tcstatus();
-
- tmp &= ~(TCSTATUS_A | TCSTATUS_DA);
- tmp |= TCSTATUS_IXMT;
- 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);
- }
- void *vpe_alloc(void)
- {
- int i;
- struct vpe *v;
-
- 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);
- int vpe_start(void *vpe, unsigned long start)
- {
- struct vpe *v = vpe;
- v->__start = start;
- return vpe_run(v);
- }
- EXPORT_SYMBOL(vpe_start);
- 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);
- 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();
-
- set_c0_mvpcontrol(MVPCONTROL_VPC);
- settc(t->index);
- write_vpe_c0_vpeconf0(read_vpe_c0_vpeconf0() & ~VPECONF0_VPA);
-
- write_tc_c0_tchalt(TCHALT_H);
- mips_ihb();
-
- 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();
-
- 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++) {
-
- 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);
-
- 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();
-
- list_add(&t->tc, &v->tc);
-
- if (tc >= aprp_cpu_index()) {
- unsigned long tmp = read_vpe_c0_vpeconf0();
- tmp &= ~VPECONF0_VPA;
-
- tmp |= VPECONF0_MVP;
- write_vpe_c0_vpeconf0(tmp);
- }
-
- write_vpe_c0_vpecontrol(read_vpe_c0_vpecontrol() &
- ~VPECONTROL_TE);
- if (tc >= vpelimit) {
-
- write_vpe_c0_config(read_c0_config());
- }
- }
-
- t->pvpe = v;
- if (tc >= aprp_cpu_index()) {
- unsigned long tmp;
- settc(tc);
-
- tmp = read_tc_c0_tcbind();
- if (tmp & TCBIND_CURVPE) {
-
- write_tc_c0_tcbind(tmp & ~TCBIND_CURVPE);
- t->pvpe = get_vpe(0);
- }
-
- write_tc_c0_tchalt(TCHALT_H);
- mips_ihb();
- tmp = read_tc_c0_tcstatus();
-
- tmp &= ~(TCSTATUS_A | TCSTATUS_DA);
- tmp |= TCSTATUS_IXMT;
- write_tc_c0_tcstatus(tmp);
- }
- }
- out_reenable:
-
- 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);
-
- list_for_each_entry_safe(v, n, &vpecontrol.vpe_list, list) {
- if (v->state != VPE_STATE_UNUSED)
- release_vpe(v);
- }
- }
|