11a2a6d7eSDeng-Cheng Zhu /*
21a2a6d7eSDeng-Cheng Zhu * This file is subject to the terms and conditions of the GNU General Public
31a2a6d7eSDeng-Cheng Zhu * License. See the file "COPYING" in the main directory of this archive
41a2a6d7eSDeng-Cheng Zhu * for more details.
51a2a6d7eSDeng-Cheng Zhu *
61a2a6d7eSDeng-Cheng Zhu * Copyright (C) 2004, 2005 MIPS Technologies, Inc. All rights reserved.
71a2a6d7eSDeng-Cheng Zhu * Copyright (C) 2013 Imagination Technologies Ltd.
81a2a6d7eSDeng-Cheng Zhu */
91a2a6d7eSDeng-Cheng Zhu #include <linux/kernel.h>
101a2a6d7eSDeng-Cheng Zhu #include <linux/device.h>
111a2a6d7eSDeng-Cheng Zhu #include <linux/fs.h>
121a2a6d7eSDeng-Cheng Zhu #include <linux/slab.h>
131a2a6d7eSDeng-Cheng Zhu #include <linux/export.h>
141a2a6d7eSDeng-Cheng Zhu
151a2a6d7eSDeng-Cheng Zhu #include <asm/mipsregs.h>
161a2a6d7eSDeng-Cheng Zhu #include <asm/mipsmtregs.h>
171a2a6d7eSDeng-Cheng Zhu #include <asm/mips_mt.h>
181a2a6d7eSDeng-Cheng Zhu #include <asm/vpe.h>
191a2a6d7eSDeng-Cheng Zhu
201a2a6d7eSDeng-Cheng Zhu static int major;
211a2a6d7eSDeng-Cheng Zhu
221a2a6d7eSDeng-Cheng Zhu /* The number of TCs and VPEs physically available on the core */
231a2a6d7eSDeng-Cheng Zhu static int hw_tcs, hw_vpes;
241a2a6d7eSDeng-Cheng Zhu
251a2a6d7eSDeng-Cheng Zhu /* We are prepared so configure and start the VPE... */
vpe_run(struct vpe * v)261a2a6d7eSDeng-Cheng Zhu int vpe_run(struct vpe *v)
271a2a6d7eSDeng-Cheng Zhu {
281a2a6d7eSDeng-Cheng Zhu unsigned long flags, val, dmt_flag;
295792bf64SSteven J. Hill struct vpe_notifications *notifier;
301a2a6d7eSDeng-Cheng Zhu unsigned int vpeflags;
311a2a6d7eSDeng-Cheng Zhu struct tc *t;
321a2a6d7eSDeng-Cheng Zhu
331a2a6d7eSDeng-Cheng Zhu /* check we are the Master VPE */
341a2a6d7eSDeng-Cheng Zhu local_irq_save(flags);
351a2a6d7eSDeng-Cheng Zhu val = read_c0_vpeconf0();
361a2a6d7eSDeng-Cheng Zhu if (!(val & VPECONF0_MVP)) {
371a2a6d7eSDeng-Cheng Zhu pr_warn("VPE loader: only Master VPE's are able to config MT\n");
381a2a6d7eSDeng-Cheng Zhu local_irq_restore(flags);
391a2a6d7eSDeng-Cheng Zhu
401a2a6d7eSDeng-Cheng Zhu return -1;
411a2a6d7eSDeng-Cheng Zhu }
421a2a6d7eSDeng-Cheng Zhu
431a2a6d7eSDeng-Cheng Zhu dmt_flag = dmt();
441a2a6d7eSDeng-Cheng Zhu vpeflags = dvpe();
451a2a6d7eSDeng-Cheng Zhu
461a2a6d7eSDeng-Cheng Zhu if (list_empty(&v->tc)) {
471a2a6d7eSDeng-Cheng Zhu evpe(vpeflags);
481a2a6d7eSDeng-Cheng Zhu emt(dmt_flag);
491a2a6d7eSDeng-Cheng Zhu local_irq_restore(flags);
501a2a6d7eSDeng-Cheng Zhu
511a2a6d7eSDeng-Cheng Zhu pr_warn("VPE loader: No TC's associated with VPE %d\n",
521a2a6d7eSDeng-Cheng Zhu v->minor);
531a2a6d7eSDeng-Cheng Zhu
541a2a6d7eSDeng-Cheng Zhu return -ENOEXEC;
551a2a6d7eSDeng-Cheng Zhu }
561a2a6d7eSDeng-Cheng Zhu
571a2a6d7eSDeng-Cheng Zhu t = list_first_entry(&v->tc, struct tc, tc);
581a2a6d7eSDeng-Cheng Zhu
591a2a6d7eSDeng-Cheng Zhu /* Put MVPE's into 'configuration state' */
601a2a6d7eSDeng-Cheng Zhu set_c0_mvpcontrol(MVPCONTROL_VPC);
611a2a6d7eSDeng-Cheng Zhu
621a2a6d7eSDeng-Cheng Zhu settc(t->index);
631a2a6d7eSDeng-Cheng Zhu
641a2a6d7eSDeng-Cheng Zhu /* should check it is halted, and not activated */
651a2a6d7eSDeng-Cheng Zhu if ((read_tc_c0_tcstatus() & TCSTATUS_A) ||
661a2a6d7eSDeng-Cheng Zhu !(read_tc_c0_tchalt() & TCHALT_H)) {
671a2a6d7eSDeng-Cheng Zhu evpe(vpeflags);
681a2a6d7eSDeng-Cheng Zhu emt(dmt_flag);
691a2a6d7eSDeng-Cheng Zhu local_irq_restore(flags);
701a2a6d7eSDeng-Cheng Zhu
711a2a6d7eSDeng-Cheng Zhu pr_warn("VPE loader: TC %d is already active!\n",
721a2a6d7eSDeng-Cheng Zhu t->index);
731a2a6d7eSDeng-Cheng Zhu
741a2a6d7eSDeng-Cheng Zhu return -ENOEXEC;
751a2a6d7eSDeng-Cheng Zhu }
761a2a6d7eSDeng-Cheng Zhu
771a2a6d7eSDeng-Cheng Zhu /*
781a2a6d7eSDeng-Cheng Zhu * Write the address we want it to start running from in the TCPC
791a2a6d7eSDeng-Cheng Zhu * register.
801a2a6d7eSDeng-Cheng Zhu */
811a2a6d7eSDeng-Cheng Zhu write_tc_c0_tcrestart((unsigned long)v->__start);
821a2a6d7eSDeng-Cheng Zhu write_tc_c0_tccontext((unsigned long)0);
831a2a6d7eSDeng-Cheng Zhu
841a2a6d7eSDeng-Cheng Zhu /*
851a2a6d7eSDeng-Cheng Zhu * Mark the TC as activated, not interrupt exempt and not dynamically
861a2a6d7eSDeng-Cheng Zhu * allocatable
871a2a6d7eSDeng-Cheng Zhu */
881a2a6d7eSDeng-Cheng Zhu val = read_tc_c0_tcstatus();
891a2a6d7eSDeng-Cheng Zhu val = (val & ~(TCSTATUS_DA | TCSTATUS_IXMT)) | TCSTATUS_A;
901a2a6d7eSDeng-Cheng Zhu write_tc_c0_tcstatus(val);
911a2a6d7eSDeng-Cheng Zhu
921a2a6d7eSDeng-Cheng Zhu write_tc_c0_tchalt(read_tc_c0_tchalt() & ~TCHALT_H);
931a2a6d7eSDeng-Cheng Zhu
941a2a6d7eSDeng-Cheng Zhu /*
9591dc288fSRandy Dunlap * We don't pass the memsize here, so VPE programs need to be
9691dc288fSRandy Dunlap * compiled with DFLT_STACK_SIZE and DFLT_HEAP_SIZE defined.
971a2a6d7eSDeng-Cheng Zhu */
98*dd6d29a6SJiaxun Yang mttgpr($7, 0);
99*dd6d29a6SJiaxun Yang mttgpr($6, v->ntcs);
1001a2a6d7eSDeng-Cheng Zhu
1011a2a6d7eSDeng-Cheng Zhu /* set up VPE1 */
1021a2a6d7eSDeng-Cheng Zhu /*
1031a2a6d7eSDeng-Cheng Zhu * bind the TC to VPE 1 as late as possible so we only have the final
1041a2a6d7eSDeng-Cheng Zhu * VPE registers to set up, and so an EJTAG probe can trigger on it
1051a2a6d7eSDeng-Cheng Zhu */
1061a2a6d7eSDeng-Cheng Zhu write_tc_c0_tcbind((read_tc_c0_tcbind() & ~TCBIND_CURVPE) | 1);
1071a2a6d7eSDeng-Cheng Zhu
1081a2a6d7eSDeng-Cheng Zhu write_vpe_c0_vpeconf0(read_vpe_c0_vpeconf0() & ~(VPECONF0_VPA));
1091a2a6d7eSDeng-Cheng Zhu
1101a2a6d7eSDeng-Cheng Zhu back_to_back_c0_hazard();
1111a2a6d7eSDeng-Cheng Zhu
1121a2a6d7eSDeng-Cheng Zhu /* Set up the XTC bit in vpeconf0 to point at our tc */
1131a2a6d7eSDeng-Cheng Zhu write_vpe_c0_vpeconf0((read_vpe_c0_vpeconf0() & ~(VPECONF0_XTC))
1141a2a6d7eSDeng-Cheng Zhu | (t->index << VPECONF0_XTC_SHIFT));
1151a2a6d7eSDeng-Cheng Zhu
1161a2a6d7eSDeng-Cheng Zhu back_to_back_c0_hazard();
1171a2a6d7eSDeng-Cheng Zhu
1181a2a6d7eSDeng-Cheng Zhu /* enable this VPE */
1191a2a6d7eSDeng-Cheng Zhu write_vpe_c0_vpeconf0(read_vpe_c0_vpeconf0() | VPECONF0_VPA);
1201a2a6d7eSDeng-Cheng Zhu
1211a2a6d7eSDeng-Cheng Zhu /* clear out any left overs from a previous program */
1221a2a6d7eSDeng-Cheng Zhu write_vpe_c0_status(0);
1231a2a6d7eSDeng-Cheng Zhu write_vpe_c0_cause(0);
1241a2a6d7eSDeng-Cheng Zhu
1251a2a6d7eSDeng-Cheng Zhu /* take system out of configuration state */
1261a2a6d7eSDeng-Cheng Zhu clear_c0_mvpcontrol(MVPCONTROL_VPC);
1271a2a6d7eSDeng-Cheng Zhu
1281a2a6d7eSDeng-Cheng Zhu /*
129b633648cSRalf Baechle * SMVP kernels manage VPE enable independently, but uniprocessor
130b633648cSRalf Baechle * kernels need to turn it on, even if that wasn't the pre-dvpe() state.
1311a2a6d7eSDeng-Cheng Zhu */
1321a2a6d7eSDeng-Cheng Zhu #ifdef CONFIG_SMP
1331a2a6d7eSDeng-Cheng Zhu evpe(vpeflags);
1341a2a6d7eSDeng-Cheng Zhu #else
1351a2a6d7eSDeng-Cheng Zhu evpe(EVPE_ENABLE);
1361a2a6d7eSDeng-Cheng Zhu #endif
1371a2a6d7eSDeng-Cheng Zhu emt(dmt_flag);
1381a2a6d7eSDeng-Cheng Zhu local_irq_restore(flags);
1391a2a6d7eSDeng-Cheng Zhu
1405792bf64SSteven J. Hill list_for_each_entry(notifier, &v->notify, list)
1415792bf64SSteven J. Hill notifier->start(VPE_MODULE_MINOR);
1421a2a6d7eSDeng-Cheng Zhu
1431a2a6d7eSDeng-Cheng Zhu return 0;
1441a2a6d7eSDeng-Cheng Zhu }
1451a2a6d7eSDeng-Cheng Zhu
cleanup_tc(struct tc * tc)1461a2a6d7eSDeng-Cheng Zhu void cleanup_tc(struct tc *tc)
1471a2a6d7eSDeng-Cheng Zhu {
1481a2a6d7eSDeng-Cheng Zhu unsigned long flags;
1491a2a6d7eSDeng-Cheng Zhu unsigned int mtflags, vpflags;
1501a2a6d7eSDeng-Cheng Zhu int tmp;
1511a2a6d7eSDeng-Cheng Zhu
1521a2a6d7eSDeng-Cheng Zhu local_irq_save(flags);
1531a2a6d7eSDeng-Cheng Zhu mtflags = dmt();
1541a2a6d7eSDeng-Cheng Zhu vpflags = dvpe();
1551a2a6d7eSDeng-Cheng Zhu /* Put MVPE's into 'configuration state' */
1561a2a6d7eSDeng-Cheng Zhu set_c0_mvpcontrol(MVPCONTROL_VPC);
1571a2a6d7eSDeng-Cheng Zhu
1581a2a6d7eSDeng-Cheng Zhu settc(tc->index);
1591a2a6d7eSDeng-Cheng Zhu tmp = read_tc_c0_tcstatus();
1601a2a6d7eSDeng-Cheng Zhu
1611a2a6d7eSDeng-Cheng Zhu /* mark not allocated and not dynamically allocatable */
1621a2a6d7eSDeng-Cheng Zhu tmp &= ~(TCSTATUS_A | TCSTATUS_DA);
1631a2a6d7eSDeng-Cheng Zhu tmp |= TCSTATUS_IXMT; /* interrupt exempt */
1641a2a6d7eSDeng-Cheng Zhu write_tc_c0_tcstatus(tmp);
1651a2a6d7eSDeng-Cheng Zhu
1661a2a6d7eSDeng-Cheng Zhu write_tc_c0_tchalt(TCHALT_H);
1671a2a6d7eSDeng-Cheng Zhu mips_ihb();
1681a2a6d7eSDeng-Cheng Zhu
1691a2a6d7eSDeng-Cheng Zhu clear_c0_mvpcontrol(MVPCONTROL_VPC);
1701a2a6d7eSDeng-Cheng Zhu evpe(vpflags);
1711a2a6d7eSDeng-Cheng Zhu emt(mtflags);
1721a2a6d7eSDeng-Cheng Zhu local_irq_restore(flags);
1731a2a6d7eSDeng-Cheng Zhu }
1741a2a6d7eSDeng-Cheng Zhu
1751a2a6d7eSDeng-Cheng Zhu /* module wrapper entry points */
1761a2a6d7eSDeng-Cheng Zhu /* give me a vpe */
vpe_alloc(void)1771a2a6d7eSDeng-Cheng Zhu void *vpe_alloc(void)
1781a2a6d7eSDeng-Cheng Zhu {
1791a2a6d7eSDeng-Cheng Zhu int i;
1801a2a6d7eSDeng-Cheng Zhu struct vpe *v;
1811a2a6d7eSDeng-Cheng Zhu
1821a2a6d7eSDeng-Cheng Zhu /* find a vpe */
1831a2a6d7eSDeng-Cheng Zhu for (i = 1; i < MAX_VPES; i++) {
1841a2a6d7eSDeng-Cheng Zhu v = get_vpe(i);
1851a2a6d7eSDeng-Cheng Zhu if (v != NULL) {
1861a2a6d7eSDeng-Cheng Zhu v->state = VPE_STATE_INUSE;
1871a2a6d7eSDeng-Cheng Zhu return v;
1881a2a6d7eSDeng-Cheng Zhu }
1891a2a6d7eSDeng-Cheng Zhu }
1901a2a6d7eSDeng-Cheng Zhu return NULL;
1911a2a6d7eSDeng-Cheng Zhu }
1921a2a6d7eSDeng-Cheng Zhu EXPORT_SYMBOL(vpe_alloc);
1931a2a6d7eSDeng-Cheng Zhu
1941a2a6d7eSDeng-Cheng Zhu /* start running from here */
vpe_start(void * vpe,unsigned long start)1951a2a6d7eSDeng-Cheng Zhu int vpe_start(void *vpe, unsigned long start)
1961a2a6d7eSDeng-Cheng Zhu {
1971a2a6d7eSDeng-Cheng Zhu struct vpe *v = vpe;
1981a2a6d7eSDeng-Cheng Zhu
1991a2a6d7eSDeng-Cheng Zhu v->__start = start;
2001a2a6d7eSDeng-Cheng Zhu return vpe_run(v);
2011a2a6d7eSDeng-Cheng Zhu }
2021a2a6d7eSDeng-Cheng Zhu EXPORT_SYMBOL(vpe_start);
2031a2a6d7eSDeng-Cheng Zhu
2041a2a6d7eSDeng-Cheng Zhu /* halt it for now */
vpe_stop(void * vpe)2051a2a6d7eSDeng-Cheng Zhu int vpe_stop(void *vpe)
2061a2a6d7eSDeng-Cheng Zhu {
2071a2a6d7eSDeng-Cheng Zhu struct vpe *v = vpe;
2081a2a6d7eSDeng-Cheng Zhu struct tc *t;
2091a2a6d7eSDeng-Cheng Zhu unsigned int evpe_flags;
2101a2a6d7eSDeng-Cheng Zhu
2111a2a6d7eSDeng-Cheng Zhu evpe_flags = dvpe();
2121a2a6d7eSDeng-Cheng Zhu
2131a2a6d7eSDeng-Cheng Zhu t = list_entry(v->tc.next, struct tc, tc);
2141a2a6d7eSDeng-Cheng Zhu if (t != NULL) {
2151a2a6d7eSDeng-Cheng Zhu settc(t->index);
2161a2a6d7eSDeng-Cheng Zhu write_vpe_c0_vpeconf0(read_vpe_c0_vpeconf0() & ~VPECONF0_VPA);
2171a2a6d7eSDeng-Cheng Zhu }
2181a2a6d7eSDeng-Cheng Zhu
2191a2a6d7eSDeng-Cheng Zhu evpe(evpe_flags);
2201a2a6d7eSDeng-Cheng Zhu
2211a2a6d7eSDeng-Cheng Zhu return 0;
2221a2a6d7eSDeng-Cheng Zhu }
2231a2a6d7eSDeng-Cheng Zhu EXPORT_SYMBOL(vpe_stop);
2241a2a6d7eSDeng-Cheng Zhu
2251a2a6d7eSDeng-Cheng Zhu /* I've done with it thank you */
vpe_free(void * vpe)2261a2a6d7eSDeng-Cheng Zhu int vpe_free(void *vpe)
2271a2a6d7eSDeng-Cheng Zhu {
2281a2a6d7eSDeng-Cheng Zhu struct vpe *v = vpe;
2291a2a6d7eSDeng-Cheng Zhu struct tc *t;
2301a2a6d7eSDeng-Cheng Zhu unsigned int evpe_flags;
2311a2a6d7eSDeng-Cheng Zhu
2321a2a6d7eSDeng-Cheng Zhu t = list_entry(v->tc.next, struct tc, tc);
2331a2a6d7eSDeng-Cheng Zhu if (t == NULL)
2341a2a6d7eSDeng-Cheng Zhu return -ENOEXEC;
2351a2a6d7eSDeng-Cheng Zhu
2361a2a6d7eSDeng-Cheng Zhu evpe_flags = dvpe();
2371a2a6d7eSDeng-Cheng Zhu
2381a2a6d7eSDeng-Cheng Zhu /* Put MVPE's into 'configuration state' */
2391a2a6d7eSDeng-Cheng Zhu set_c0_mvpcontrol(MVPCONTROL_VPC);
2401a2a6d7eSDeng-Cheng Zhu
2411a2a6d7eSDeng-Cheng Zhu settc(t->index);
2421a2a6d7eSDeng-Cheng Zhu write_vpe_c0_vpeconf0(read_vpe_c0_vpeconf0() & ~VPECONF0_VPA);
2431a2a6d7eSDeng-Cheng Zhu
2441a2a6d7eSDeng-Cheng Zhu /* halt the TC */
2451a2a6d7eSDeng-Cheng Zhu write_tc_c0_tchalt(TCHALT_H);
2461a2a6d7eSDeng-Cheng Zhu mips_ihb();
2471a2a6d7eSDeng-Cheng Zhu
2481a2a6d7eSDeng-Cheng Zhu /* mark the TC unallocated */
2491a2a6d7eSDeng-Cheng Zhu write_tc_c0_tcstatus(read_tc_c0_tcstatus() & ~TCSTATUS_A);
2501a2a6d7eSDeng-Cheng Zhu
2511a2a6d7eSDeng-Cheng Zhu v->state = VPE_STATE_UNUSED;
2521a2a6d7eSDeng-Cheng Zhu
2531a2a6d7eSDeng-Cheng Zhu clear_c0_mvpcontrol(MVPCONTROL_VPC);
2541a2a6d7eSDeng-Cheng Zhu evpe(evpe_flags);
2551a2a6d7eSDeng-Cheng Zhu
2561a2a6d7eSDeng-Cheng Zhu return 0;
2571a2a6d7eSDeng-Cheng Zhu }
2581a2a6d7eSDeng-Cheng Zhu EXPORT_SYMBOL(vpe_free);
2591a2a6d7eSDeng-Cheng Zhu
store_kill(struct device * dev,struct device_attribute * attr,const char * buf,size_t len)2601a2a6d7eSDeng-Cheng Zhu static ssize_t store_kill(struct device *dev, struct device_attribute *attr,
2611a2a6d7eSDeng-Cheng Zhu const char *buf, size_t len)
2621a2a6d7eSDeng-Cheng Zhu {
2631a2a6d7eSDeng-Cheng Zhu struct vpe *vpe = get_vpe(aprp_cpu_index());
2641a2a6d7eSDeng-Cheng Zhu struct vpe_notifications *notifier;
2651a2a6d7eSDeng-Cheng Zhu
2661a2a6d7eSDeng-Cheng Zhu list_for_each_entry(notifier, &vpe->notify, list)
2671a2a6d7eSDeng-Cheng Zhu notifier->stop(aprp_cpu_index());
2681a2a6d7eSDeng-Cheng Zhu
2691a2a6d7eSDeng-Cheng Zhu release_progmem(vpe->load_addr);
2701a2a6d7eSDeng-Cheng Zhu cleanup_tc(get_tc(aprp_cpu_index()));
2711a2a6d7eSDeng-Cheng Zhu vpe_stop(vpe);
2721a2a6d7eSDeng-Cheng Zhu vpe_free(vpe);
2731a2a6d7eSDeng-Cheng Zhu
2741a2a6d7eSDeng-Cheng Zhu return len;
2751a2a6d7eSDeng-Cheng Zhu }
2761a2a6d7eSDeng-Cheng Zhu static DEVICE_ATTR(kill, S_IWUSR, NULL, store_kill);
2771a2a6d7eSDeng-Cheng Zhu
ntcs_show(struct device * cd,struct device_attribute * attr,char * buf)2781a2a6d7eSDeng-Cheng Zhu static ssize_t ntcs_show(struct device *cd, struct device_attribute *attr,
2791a2a6d7eSDeng-Cheng Zhu char *buf)
2801a2a6d7eSDeng-Cheng Zhu {
2811a2a6d7eSDeng-Cheng Zhu struct vpe *vpe = get_vpe(aprp_cpu_index());
2821a2a6d7eSDeng-Cheng Zhu
2831a2a6d7eSDeng-Cheng Zhu return sprintf(buf, "%d\n", vpe->ntcs);
2841a2a6d7eSDeng-Cheng Zhu }
2851a2a6d7eSDeng-Cheng Zhu
ntcs_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t len)2861a2a6d7eSDeng-Cheng Zhu static ssize_t ntcs_store(struct device *dev, struct device_attribute *attr,
2871a2a6d7eSDeng-Cheng Zhu const char *buf, size_t len)
2881a2a6d7eSDeng-Cheng Zhu {
2891a2a6d7eSDeng-Cheng Zhu struct vpe *vpe = get_vpe(aprp_cpu_index());
2901a2a6d7eSDeng-Cheng Zhu unsigned long new;
2911a2a6d7eSDeng-Cheng Zhu int ret;
2921a2a6d7eSDeng-Cheng Zhu
2931a2a6d7eSDeng-Cheng Zhu ret = kstrtoul(buf, 0, &new);
2941a2a6d7eSDeng-Cheng Zhu if (ret < 0)
2951a2a6d7eSDeng-Cheng Zhu return ret;
2961a2a6d7eSDeng-Cheng Zhu
2971a2a6d7eSDeng-Cheng Zhu if (new == 0 || new > (hw_tcs - aprp_cpu_index()))
2981a2a6d7eSDeng-Cheng Zhu return -EINVAL;
2991a2a6d7eSDeng-Cheng Zhu
3001a2a6d7eSDeng-Cheng Zhu vpe->ntcs = new;
3011a2a6d7eSDeng-Cheng Zhu
3021a2a6d7eSDeng-Cheng Zhu return len;
3031a2a6d7eSDeng-Cheng Zhu }
3041a2a6d7eSDeng-Cheng Zhu static DEVICE_ATTR_RW(ntcs);
3051a2a6d7eSDeng-Cheng Zhu
3061a2a6d7eSDeng-Cheng Zhu static struct attribute *vpe_attrs[] = {
3071a2a6d7eSDeng-Cheng Zhu &dev_attr_kill.attr,
3081a2a6d7eSDeng-Cheng Zhu &dev_attr_ntcs.attr,
3091a2a6d7eSDeng-Cheng Zhu NULL,
3101a2a6d7eSDeng-Cheng Zhu };
3111a2a6d7eSDeng-Cheng Zhu ATTRIBUTE_GROUPS(vpe);
3121a2a6d7eSDeng-Cheng Zhu
vpe_device_release(struct device * cd)3131a2a6d7eSDeng-Cheng Zhu static void vpe_device_release(struct device *cd)
3141a2a6d7eSDeng-Cheng Zhu {
3151a2a6d7eSDeng-Cheng Zhu }
3161a2a6d7eSDeng-Cheng Zhu
3171a2a6d7eSDeng-Cheng Zhu static struct class vpe_class = {
3181a2a6d7eSDeng-Cheng Zhu .name = "vpe",
3191a2a6d7eSDeng-Cheng Zhu .dev_release = vpe_device_release,
3201a2a6d7eSDeng-Cheng Zhu .dev_groups = vpe_groups,
3211a2a6d7eSDeng-Cheng Zhu };
3221a2a6d7eSDeng-Cheng Zhu
3231a2a6d7eSDeng-Cheng Zhu static struct device vpe_device;
3241a2a6d7eSDeng-Cheng Zhu
vpe_module_init(void)3251a2a6d7eSDeng-Cheng Zhu int __init vpe_module_init(void)
3261a2a6d7eSDeng-Cheng Zhu {
3271a2a6d7eSDeng-Cheng Zhu unsigned int mtflags, vpflags;
3281a2a6d7eSDeng-Cheng Zhu unsigned long flags, val;
3291a2a6d7eSDeng-Cheng Zhu struct vpe *v = NULL;
3301a2a6d7eSDeng-Cheng Zhu struct tc *t;
3311a2a6d7eSDeng-Cheng Zhu int tc, err;
3321a2a6d7eSDeng-Cheng Zhu
3331a2a6d7eSDeng-Cheng Zhu if (!cpu_has_mipsmt) {
3341a2a6d7eSDeng-Cheng Zhu pr_warn("VPE loader: not a MIPS MT capable processor\n");
3351a2a6d7eSDeng-Cheng Zhu return -ENODEV;
3361a2a6d7eSDeng-Cheng Zhu }
3371a2a6d7eSDeng-Cheng Zhu
3381a2a6d7eSDeng-Cheng Zhu if (vpelimit == 0) {
3391a2a6d7eSDeng-Cheng Zhu pr_warn("No VPEs reserved for AP/SP, not initialize VPE loader\n"
3401a2a6d7eSDeng-Cheng Zhu "Pass maxvpes=<n> argument as kernel argument\n");
3411a2a6d7eSDeng-Cheng Zhu
3421a2a6d7eSDeng-Cheng Zhu return -ENODEV;
3431a2a6d7eSDeng-Cheng Zhu }
3441a2a6d7eSDeng-Cheng Zhu
3451a2a6d7eSDeng-Cheng Zhu if (aprp_cpu_index() == 0) {
3461a2a6d7eSDeng-Cheng Zhu pr_warn("No TCs reserved for AP/SP, not initialize VPE loader\n"
3471a2a6d7eSDeng-Cheng Zhu "Pass maxtcs=<n> argument as kernel argument\n");
3481a2a6d7eSDeng-Cheng Zhu
3491a2a6d7eSDeng-Cheng Zhu return -ENODEV;
3501a2a6d7eSDeng-Cheng Zhu }
3511a2a6d7eSDeng-Cheng Zhu
3521a2a6d7eSDeng-Cheng Zhu major = register_chrdev(0, VPE_MODULE_NAME, &vpe_fops);
3531a2a6d7eSDeng-Cheng Zhu if (major < 0) {
3541a2a6d7eSDeng-Cheng Zhu pr_warn("VPE loader: unable to register character device\n");
3551a2a6d7eSDeng-Cheng Zhu return major;
3561a2a6d7eSDeng-Cheng Zhu }
3571a2a6d7eSDeng-Cheng Zhu
3581a2a6d7eSDeng-Cheng Zhu err = class_register(&vpe_class);
3591a2a6d7eSDeng-Cheng Zhu if (err) {
3601a2a6d7eSDeng-Cheng Zhu pr_err("vpe_class registration failed\n");
3611a2a6d7eSDeng-Cheng Zhu goto out_chrdev;
3621a2a6d7eSDeng-Cheng Zhu }
3631a2a6d7eSDeng-Cheng Zhu
3641a2a6d7eSDeng-Cheng Zhu device_initialize(&vpe_device);
3653a845b30SZheng Yongjun vpe_device.class = &vpe_class;
3663a845b30SZheng Yongjun vpe_device.parent = NULL;
3671a2a6d7eSDeng-Cheng Zhu dev_set_name(&vpe_device, "vpe1");
3681a2a6d7eSDeng-Cheng Zhu vpe_device.devt = MKDEV(major, VPE_MODULE_MINOR);
3691a2a6d7eSDeng-Cheng Zhu err = device_add(&vpe_device);
3701a2a6d7eSDeng-Cheng Zhu if (err) {
3711a2a6d7eSDeng-Cheng Zhu pr_err("Adding vpe_device failed\n");
3721a2a6d7eSDeng-Cheng Zhu goto out_class;
3731a2a6d7eSDeng-Cheng Zhu }
3741a2a6d7eSDeng-Cheng Zhu
3751a2a6d7eSDeng-Cheng Zhu local_irq_save(flags);
3761a2a6d7eSDeng-Cheng Zhu mtflags = dmt();
3771a2a6d7eSDeng-Cheng Zhu vpflags = dvpe();
3781a2a6d7eSDeng-Cheng Zhu
3791a2a6d7eSDeng-Cheng Zhu /* Put MVPE's into 'configuration state' */
3801a2a6d7eSDeng-Cheng Zhu set_c0_mvpcontrol(MVPCONTROL_VPC);
3811a2a6d7eSDeng-Cheng Zhu
3821a2a6d7eSDeng-Cheng Zhu val = read_c0_mvpconf0();
3831a2a6d7eSDeng-Cheng Zhu hw_tcs = (val & MVPCONF0_PTC) + 1;
3841a2a6d7eSDeng-Cheng Zhu hw_vpes = ((val & MVPCONF0_PVPE) >> MVPCONF0_PVPE_SHIFT) + 1;
3851a2a6d7eSDeng-Cheng Zhu
3861a2a6d7eSDeng-Cheng Zhu for (tc = aprp_cpu_index(); tc < hw_tcs; tc++) {
3871a2a6d7eSDeng-Cheng Zhu /*
3881a2a6d7eSDeng-Cheng Zhu * Must re-enable multithreading temporarily or in case we
3891a2a6d7eSDeng-Cheng Zhu * reschedule send IPIs or similar we might hang.
3901a2a6d7eSDeng-Cheng Zhu */
3911a2a6d7eSDeng-Cheng Zhu clear_c0_mvpcontrol(MVPCONTROL_VPC);
3921a2a6d7eSDeng-Cheng Zhu evpe(vpflags);
3931a2a6d7eSDeng-Cheng Zhu emt(mtflags);
3941a2a6d7eSDeng-Cheng Zhu local_irq_restore(flags);
3951a2a6d7eSDeng-Cheng Zhu t = alloc_tc(tc);
3961a2a6d7eSDeng-Cheng Zhu if (!t) {
3971a2a6d7eSDeng-Cheng Zhu err = -ENOMEM;
3981a2a6d7eSDeng-Cheng Zhu goto out_dev;
3991a2a6d7eSDeng-Cheng Zhu }
4001a2a6d7eSDeng-Cheng Zhu
4011a2a6d7eSDeng-Cheng Zhu local_irq_save(flags);
4021a2a6d7eSDeng-Cheng Zhu mtflags = dmt();
4031a2a6d7eSDeng-Cheng Zhu vpflags = dvpe();
4041a2a6d7eSDeng-Cheng Zhu set_c0_mvpcontrol(MVPCONTROL_VPC);
4051a2a6d7eSDeng-Cheng Zhu
4061a2a6d7eSDeng-Cheng Zhu /* VPE's */
4071a2a6d7eSDeng-Cheng Zhu if (tc < hw_tcs) {
4081a2a6d7eSDeng-Cheng Zhu settc(tc);
4091a2a6d7eSDeng-Cheng Zhu
4101a2a6d7eSDeng-Cheng Zhu v = alloc_vpe(tc);
4111a2a6d7eSDeng-Cheng Zhu if (v == NULL) {
4121a2a6d7eSDeng-Cheng Zhu pr_warn("VPE: unable to allocate VPE\n");
4131a2a6d7eSDeng-Cheng Zhu goto out_reenable;
4141a2a6d7eSDeng-Cheng Zhu }
4151a2a6d7eSDeng-Cheng Zhu
4161a2a6d7eSDeng-Cheng Zhu v->ntcs = hw_tcs - aprp_cpu_index();
4171a2a6d7eSDeng-Cheng Zhu
4181a2a6d7eSDeng-Cheng Zhu /* add the tc to the list of this vpe's tc's. */
4191a2a6d7eSDeng-Cheng Zhu list_add(&t->tc, &v->tc);
4201a2a6d7eSDeng-Cheng Zhu
4211a2a6d7eSDeng-Cheng Zhu /* deactivate all but vpe0 */
4221a2a6d7eSDeng-Cheng Zhu if (tc >= aprp_cpu_index()) {
4231a2a6d7eSDeng-Cheng Zhu unsigned long tmp = read_vpe_c0_vpeconf0();
4241a2a6d7eSDeng-Cheng Zhu
4251a2a6d7eSDeng-Cheng Zhu tmp &= ~VPECONF0_VPA;
4261a2a6d7eSDeng-Cheng Zhu
4271a2a6d7eSDeng-Cheng Zhu /* master VPE */
4281a2a6d7eSDeng-Cheng Zhu tmp |= VPECONF0_MVP;
4291a2a6d7eSDeng-Cheng Zhu write_vpe_c0_vpeconf0(tmp);
4301a2a6d7eSDeng-Cheng Zhu }
4311a2a6d7eSDeng-Cheng Zhu
4321a2a6d7eSDeng-Cheng Zhu /* disable multi-threading with TC's */
4331a2a6d7eSDeng-Cheng Zhu write_vpe_c0_vpecontrol(read_vpe_c0_vpecontrol() &
4341a2a6d7eSDeng-Cheng Zhu ~VPECONTROL_TE);
4351a2a6d7eSDeng-Cheng Zhu
4361a2a6d7eSDeng-Cheng Zhu if (tc >= vpelimit) {
4371a2a6d7eSDeng-Cheng Zhu /*
4381a2a6d7eSDeng-Cheng Zhu * Set config to be the same as vpe0,
4391a2a6d7eSDeng-Cheng Zhu * particularly kseg0 coherency alg
4401a2a6d7eSDeng-Cheng Zhu */
4411a2a6d7eSDeng-Cheng Zhu write_vpe_c0_config(read_c0_config());
4421a2a6d7eSDeng-Cheng Zhu }
4431a2a6d7eSDeng-Cheng Zhu }
4441a2a6d7eSDeng-Cheng Zhu
4451a2a6d7eSDeng-Cheng Zhu /* TC's */
4461a2a6d7eSDeng-Cheng Zhu t->pvpe = v; /* set the parent vpe */
4471a2a6d7eSDeng-Cheng Zhu
4481a2a6d7eSDeng-Cheng Zhu if (tc >= aprp_cpu_index()) {
4491a2a6d7eSDeng-Cheng Zhu unsigned long tmp;
4501a2a6d7eSDeng-Cheng Zhu
4511a2a6d7eSDeng-Cheng Zhu settc(tc);
4521a2a6d7eSDeng-Cheng Zhu
453b633648cSRalf Baechle /*
454b633648cSRalf Baechle * A TC that is bound to any other VPE gets bound to
455b633648cSRalf Baechle * VPE0, ideally I'd like to make it homeless but it
456b633648cSRalf Baechle * doesn't appear to let me bind a TC to a non-existent
457b633648cSRalf Baechle * VPE. Which is perfectly reasonable.
4581a2a6d7eSDeng-Cheng Zhu *
4591a2a6d7eSDeng-Cheng Zhu * The (un)bound state is visible to an EJTAG probe so
4601a2a6d7eSDeng-Cheng Zhu * may notify GDB...
4611a2a6d7eSDeng-Cheng Zhu */
4621a2a6d7eSDeng-Cheng Zhu tmp = read_tc_c0_tcbind();
4631a2a6d7eSDeng-Cheng Zhu if (tmp & TCBIND_CURVPE) {
4641a2a6d7eSDeng-Cheng Zhu /* tc is bound >vpe0 */
4651a2a6d7eSDeng-Cheng Zhu write_tc_c0_tcbind(tmp & ~TCBIND_CURVPE);
4661a2a6d7eSDeng-Cheng Zhu
4671a2a6d7eSDeng-Cheng Zhu t->pvpe = get_vpe(0); /* set the parent vpe */
4681a2a6d7eSDeng-Cheng Zhu }
4691a2a6d7eSDeng-Cheng Zhu
4701a2a6d7eSDeng-Cheng Zhu /* halt the TC */
4711a2a6d7eSDeng-Cheng Zhu write_tc_c0_tchalt(TCHALT_H);
4721a2a6d7eSDeng-Cheng Zhu mips_ihb();
4731a2a6d7eSDeng-Cheng Zhu
4741a2a6d7eSDeng-Cheng Zhu tmp = read_tc_c0_tcstatus();
4751a2a6d7eSDeng-Cheng Zhu
4761a2a6d7eSDeng-Cheng Zhu /* mark not activated and not dynamically allocatable */
4771a2a6d7eSDeng-Cheng Zhu tmp &= ~(TCSTATUS_A | TCSTATUS_DA);
4781a2a6d7eSDeng-Cheng Zhu tmp |= TCSTATUS_IXMT; /* interrupt exempt */
4791a2a6d7eSDeng-Cheng Zhu write_tc_c0_tcstatus(tmp);
4801a2a6d7eSDeng-Cheng Zhu }
4811a2a6d7eSDeng-Cheng Zhu }
4821a2a6d7eSDeng-Cheng Zhu
4831a2a6d7eSDeng-Cheng Zhu out_reenable:
4841a2a6d7eSDeng-Cheng Zhu /* release config state */
4851a2a6d7eSDeng-Cheng Zhu clear_c0_mvpcontrol(MVPCONTROL_VPC);
4861a2a6d7eSDeng-Cheng Zhu
4871a2a6d7eSDeng-Cheng Zhu evpe(vpflags);
4881a2a6d7eSDeng-Cheng Zhu emt(mtflags);
4891a2a6d7eSDeng-Cheng Zhu local_irq_restore(flags);
4901a2a6d7eSDeng-Cheng Zhu
4911a2a6d7eSDeng-Cheng Zhu return 0;
4921a2a6d7eSDeng-Cheng Zhu
4931a2a6d7eSDeng-Cheng Zhu out_dev:
4941a2a6d7eSDeng-Cheng Zhu device_del(&vpe_device);
4951a2a6d7eSDeng-Cheng Zhu
4961a2a6d7eSDeng-Cheng Zhu out_class:
4975822e8ccSYang Yingliang put_device(&vpe_device);
4981a2a6d7eSDeng-Cheng Zhu class_unregister(&vpe_class);
4991a2a6d7eSDeng-Cheng Zhu
5001a2a6d7eSDeng-Cheng Zhu out_chrdev:
5011a2a6d7eSDeng-Cheng Zhu unregister_chrdev(major, VPE_MODULE_NAME);
5021a2a6d7eSDeng-Cheng Zhu
5031a2a6d7eSDeng-Cheng Zhu return err;
5041a2a6d7eSDeng-Cheng Zhu }
5051a2a6d7eSDeng-Cheng Zhu
vpe_module_exit(void)5061a2a6d7eSDeng-Cheng Zhu void __exit vpe_module_exit(void)
5071a2a6d7eSDeng-Cheng Zhu {
5081a2a6d7eSDeng-Cheng Zhu struct vpe *v, *n;
5091a2a6d7eSDeng-Cheng Zhu
5105822e8ccSYang Yingliang device_unregister(&vpe_device);
5111a2a6d7eSDeng-Cheng Zhu class_unregister(&vpe_class);
5121a2a6d7eSDeng-Cheng Zhu unregister_chrdev(major, VPE_MODULE_NAME);
5131a2a6d7eSDeng-Cheng Zhu
5141a2a6d7eSDeng-Cheng Zhu /* No locking needed here */
5151a2a6d7eSDeng-Cheng Zhu list_for_each_entry_safe(v, n, &vpecontrol.vpe_list, list) {
5161a2a6d7eSDeng-Cheng Zhu if (v->state != VPE_STATE_UNUSED)
5171a2a6d7eSDeng-Cheng Zhu release_vpe(v);
5181a2a6d7eSDeng-Cheng Zhu }
5191a2a6d7eSDeng-Cheng Zhu }
520