xref: /openbmc/linux/arch/mips/kernel/vpe-mt.c (revision 10a03c36)
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 	/*
95*91dc288fSRandy Dunlap 	 * We don't pass the memsize here, so VPE programs need to be
96*91dc288fSRandy Dunlap 	 * compiled with DFLT_STACK_SIZE and DFLT_HEAP_SIZE defined.
971a2a6d7eSDeng-Cheng Zhu 	 */
98*91dc288fSRandy Dunlap 	mttgpr(7, 0);
991a2a6d7eSDeng-Cheng Zhu 	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