xref: /openbmc/linux/arch/mips/kernel/vpe-mt.c (revision 1a2a6d7e)
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... */
261a2a6d7eSDeng-Cheng Zhu int vpe_run(struct vpe *v)
271a2a6d7eSDeng-Cheng Zhu {
281a2a6d7eSDeng-Cheng Zhu 	unsigned long flags, val, dmt_flag;
291a2a6d7eSDeng-Cheng Zhu 	struct vpe_notifications *n;
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 	/*
951a2a6d7eSDeng-Cheng Zhu 	 * The sde-kit passes 'memsize' to __start in $a3, so set something
961a2a6d7eSDeng-Cheng Zhu 	 * here...  Or set $a3 to zero and define DFLT_STACK_SIZE and
971a2a6d7eSDeng-Cheng Zhu 	 * DFLT_HEAP_SIZE when you compile your program
981a2a6d7eSDeng-Cheng Zhu 	 */
991a2a6d7eSDeng-Cheng Zhu 	mttgpr(6, v->ntcs);
1001a2a6d7eSDeng-Cheng Zhu 	mttgpr(7, physical_memsize);
1011a2a6d7eSDeng-Cheng Zhu 
1021a2a6d7eSDeng-Cheng Zhu 	/* set up VPE1 */
1031a2a6d7eSDeng-Cheng Zhu 	/*
1041a2a6d7eSDeng-Cheng Zhu 	 * bind the TC to VPE 1 as late as possible so we only have the final
1051a2a6d7eSDeng-Cheng Zhu 	 * VPE registers to set up, and so an EJTAG probe can trigger on it
1061a2a6d7eSDeng-Cheng Zhu 	 */
1071a2a6d7eSDeng-Cheng Zhu 	write_tc_c0_tcbind((read_tc_c0_tcbind() & ~TCBIND_CURVPE) | 1);
1081a2a6d7eSDeng-Cheng Zhu 
1091a2a6d7eSDeng-Cheng Zhu 	write_vpe_c0_vpeconf0(read_vpe_c0_vpeconf0() & ~(VPECONF0_VPA));
1101a2a6d7eSDeng-Cheng Zhu 
1111a2a6d7eSDeng-Cheng Zhu 	back_to_back_c0_hazard();
1121a2a6d7eSDeng-Cheng Zhu 
1131a2a6d7eSDeng-Cheng Zhu 	/* Set up the XTC bit in vpeconf0 to point at our tc */
1141a2a6d7eSDeng-Cheng Zhu 	write_vpe_c0_vpeconf0((read_vpe_c0_vpeconf0() & ~(VPECONF0_XTC))
1151a2a6d7eSDeng-Cheng Zhu 			      | (t->index << VPECONF0_XTC_SHIFT));
1161a2a6d7eSDeng-Cheng Zhu 
1171a2a6d7eSDeng-Cheng Zhu 	back_to_back_c0_hazard();
1181a2a6d7eSDeng-Cheng Zhu 
1191a2a6d7eSDeng-Cheng Zhu 	/* enable this VPE */
1201a2a6d7eSDeng-Cheng Zhu 	write_vpe_c0_vpeconf0(read_vpe_c0_vpeconf0() | VPECONF0_VPA);
1211a2a6d7eSDeng-Cheng Zhu 
1221a2a6d7eSDeng-Cheng Zhu 	/* clear out any left overs from a previous program */
1231a2a6d7eSDeng-Cheng Zhu 	write_vpe_c0_status(0);
1241a2a6d7eSDeng-Cheng Zhu 	write_vpe_c0_cause(0);
1251a2a6d7eSDeng-Cheng Zhu 
1261a2a6d7eSDeng-Cheng Zhu 	/* take system out of configuration state */
1271a2a6d7eSDeng-Cheng Zhu 	clear_c0_mvpcontrol(MVPCONTROL_VPC);
1281a2a6d7eSDeng-Cheng Zhu 
1291a2a6d7eSDeng-Cheng Zhu 	/*
1301a2a6d7eSDeng-Cheng Zhu 	 * SMTC/SMVP kernels manage VPE enable independently,
1311a2a6d7eSDeng-Cheng Zhu 	 * but uniprocessor kernels need to turn it on, even
1321a2a6d7eSDeng-Cheng Zhu 	 * if that wasn't the pre-dvpe() state.
1331a2a6d7eSDeng-Cheng Zhu 	 */
1341a2a6d7eSDeng-Cheng Zhu #ifdef CONFIG_SMP
1351a2a6d7eSDeng-Cheng Zhu 	evpe(vpeflags);
1361a2a6d7eSDeng-Cheng Zhu #else
1371a2a6d7eSDeng-Cheng Zhu 	evpe(EVPE_ENABLE);
1381a2a6d7eSDeng-Cheng Zhu #endif
1391a2a6d7eSDeng-Cheng Zhu 	emt(dmt_flag);
1401a2a6d7eSDeng-Cheng Zhu 	local_irq_restore(flags);
1411a2a6d7eSDeng-Cheng Zhu 
1421a2a6d7eSDeng-Cheng Zhu 	list_for_each_entry(n, &v->notify, list)
1431a2a6d7eSDeng-Cheng Zhu 		n->start(VPE_MODULE_MINOR);
1441a2a6d7eSDeng-Cheng Zhu 
1451a2a6d7eSDeng-Cheng Zhu 	return 0;
1461a2a6d7eSDeng-Cheng Zhu }
1471a2a6d7eSDeng-Cheng Zhu 
1481a2a6d7eSDeng-Cheng Zhu void cleanup_tc(struct tc *tc)
1491a2a6d7eSDeng-Cheng Zhu {
1501a2a6d7eSDeng-Cheng Zhu 	unsigned long flags;
1511a2a6d7eSDeng-Cheng Zhu 	unsigned int mtflags, vpflags;
1521a2a6d7eSDeng-Cheng Zhu 	int tmp;
1531a2a6d7eSDeng-Cheng Zhu 
1541a2a6d7eSDeng-Cheng Zhu 	local_irq_save(flags);
1551a2a6d7eSDeng-Cheng Zhu 	mtflags = dmt();
1561a2a6d7eSDeng-Cheng Zhu 	vpflags = dvpe();
1571a2a6d7eSDeng-Cheng Zhu 	/* Put MVPE's into 'configuration state' */
1581a2a6d7eSDeng-Cheng Zhu 	set_c0_mvpcontrol(MVPCONTROL_VPC);
1591a2a6d7eSDeng-Cheng Zhu 
1601a2a6d7eSDeng-Cheng Zhu 	settc(tc->index);
1611a2a6d7eSDeng-Cheng Zhu 	tmp = read_tc_c0_tcstatus();
1621a2a6d7eSDeng-Cheng Zhu 
1631a2a6d7eSDeng-Cheng Zhu 	/* mark not allocated and not dynamically allocatable */
1641a2a6d7eSDeng-Cheng Zhu 	tmp &= ~(TCSTATUS_A | TCSTATUS_DA);
1651a2a6d7eSDeng-Cheng Zhu 	tmp |= TCSTATUS_IXMT;	/* interrupt exempt */
1661a2a6d7eSDeng-Cheng Zhu 	write_tc_c0_tcstatus(tmp);
1671a2a6d7eSDeng-Cheng Zhu 
1681a2a6d7eSDeng-Cheng Zhu 	write_tc_c0_tchalt(TCHALT_H);
1691a2a6d7eSDeng-Cheng Zhu 	mips_ihb();
1701a2a6d7eSDeng-Cheng Zhu 
1711a2a6d7eSDeng-Cheng Zhu 	clear_c0_mvpcontrol(MVPCONTROL_VPC);
1721a2a6d7eSDeng-Cheng Zhu 	evpe(vpflags);
1731a2a6d7eSDeng-Cheng Zhu 	emt(mtflags);
1741a2a6d7eSDeng-Cheng Zhu 	local_irq_restore(flags);
1751a2a6d7eSDeng-Cheng Zhu }
1761a2a6d7eSDeng-Cheng Zhu 
1771a2a6d7eSDeng-Cheng Zhu /* module wrapper entry points */
1781a2a6d7eSDeng-Cheng Zhu /* give me a vpe */
1791a2a6d7eSDeng-Cheng Zhu void *vpe_alloc(void)
1801a2a6d7eSDeng-Cheng Zhu {
1811a2a6d7eSDeng-Cheng Zhu 	int i;
1821a2a6d7eSDeng-Cheng Zhu 	struct vpe *v;
1831a2a6d7eSDeng-Cheng Zhu 
1841a2a6d7eSDeng-Cheng Zhu 	/* find a vpe */
1851a2a6d7eSDeng-Cheng Zhu 	for (i = 1; i < MAX_VPES; i++) {
1861a2a6d7eSDeng-Cheng Zhu 		v = get_vpe(i);
1871a2a6d7eSDeng-Cheng Zhu 		if (v != NULL) {
1881a2a6d7eSDeng-Cheng Zhu 			v->state = VPE_STATE_INUSE;
1891a2a6d7eSDeng-Cheng Zhu 			return v;
1901a2a6d7eSDeng-Cheng Zhu 		}
1911a2a6d7eSDeng-Cheng Zhu 	}
1921a2a6d7eSDeng-Cheng Zhu 	return NULL;
1931a2a6d7eSDeng-Cheng Zhu }
1941a2a6d7eSDeng-Cheng Zhu EXPORT_SYMBOL(vpe_alloc);
1951a2a6d7eSDeng-Cheng Zhu 
1961a2a6d7eSDeng-Cheng Zhu /* start running from here */
1971a2a6d7eSDeng-Cheng Zhu int vpe_start(void *vpe, unsigned long start)
1981a2a6d7eSDeng-Cheng Zhu {
1991a2a6d7eSDeng-Cheng Zhu 	struct vpe *v = vpe;
2001a2a6d7eSDeng-Cheng Zhu 
2011a2a6d7eSDeng-Cheng Zhu 	v->__start = start;
2021a2a6d7eSDeng-Cheng Zhu 	return vpe_run(v);
2031a2a6d7eSDeng-Cheng Zhu }
2041a2a6d7eSDeng-Cheng Zhu EXPORT_SYMBOL(vpe_start);
2051a2a6d7eSDeng-Cheng Zhu 
2061a2a6d7eSDeng-Cheng Zhu /* halt it for now */
2071a2a6d7eSDeng-Cheng Zhu int vpe_stop(void *vpe)
2081a2a6d7eSDeng-Cheng Zhu {
2091a2a6d7eSDeng-Cheng Zhu 	struct vpe *v = vpe;
2101a2a6d7eSDeng-Cheng Zhu 	struct tc *t;
2111a2a6d7eSDeng-Cheng Zhu 	unsigned int evpe_flags;
2121a2a6d7eSDeng-Cheng Zhu 
2131a2a6d7eSDeng-Cheng Zhu 	evpe_flags = dvpe();
2141a2a6d7eSDeng-Cheng Zhu 
2151a2a6d7eSDeng-Cheng Zhu 	t = list_entry(v->tc.next, struct tc, tc);
2161a2a6d7eSDeng-Cheng Zhu 	if (t != NULL) {
2171a2a6d7eSDeng-Cheng Zhu 		settc(t->index);
2181a2a6d7eSDeng-Cheng Zhu 		write_vpe_c0_vpeconf0(read_vpe_c0_vpeconf0() & ~VPECONF0_VPA);
2191a2a6d7eSDeng-Cheng Zhu 	}
2201a2a6d7eSDeng-Cheng Zhu 
2211a2a6d7eSDeng-Cheng Zhu 	evpe(evpe_flags);
2221a2a6d7eSDeng-Cheng Zhu 
2231a2a6d7eSDeng-Cheng Zhu 	return 0;
2241a2a6d7eSDeng-Cheng Zhu }
2251a2a6d7eSDeng-Cheng Zhu EXPORT_SYMBOL(vpe_stop);
2261a2a6d7eSDeng-Cheng Zhu 
2271a2a6d7eSDeng-Cheng Zhu /* I've done with it thank you */
2281a2a6d7eSDeng-Cheng Zhu int vpe_free(void *vpe)
2291a2a6d7eSDeng-Cheng Zhu {
2301a2a6d7eSDeng-Cheng Zhu 	struct vpe *v = vpe;
2311a2a6d7eSDeng-Cheng Zhu 	struct tc *t;
2321a2a6d7eSDeng-Cheng Zhu 	unsigned int evpe_flags;
2331a2a6d7eSDeng-Cheng Zhu 
2341a2a6d7eSDeng-Cheng Zhu 	t = list_entry(v->tc.next, struct tc, tc);
2351a2a6d7eSDeng-Cheng Zhu 	if (t == NULL)
2361a2a6d7eSDeng-Cheng Zhu 		return -ENOEXEC;
2371a2a6d7eSDeng-Cheng Zhu 
2381a2a6d7eSDeng-Cheng Zhu 	evpe_flags = dvpe();
2391a2a6d7eSDeng-Cheng Zhu 
2401a2a6d7eSDeng-Cheng Zhu 	/* Put MVPE's into 'configuration state' */
2411a2a6d7eSDeng-Cheng Zhu 	set_c0_mvpcontrol(MVPCONTROL_VPC);
2421a2a6d7eSDeng-Cheng Zhu 
2431a2a6d7eSDeng-Cheng Zhu 	settc(t->index);
2441a2a6d7eSDeng-Cheng Zhu 	write_vpe_c0_vpeconf0(read_vpe_c0_vpeconf0() & ~VPECONF0_VPA);
2451a2a6d7eSDeng-Cheng Zhu 
2461a2a6d7eSDeng-Cheng Zhu 	/* halt the TC */
2471a2a6d7eSDeng-Cheng Zhu 	write_tc_c0_tchalt(TCHALT_H);
2481a2a6d7eSDeng-Cheng Zhu 	mips_ihb();
2491a2a6d7eSDeng-Cheng Zhu 
2501a2a6d7eSDeng-Cheng Zhu 	/* mark the TC unallocated */
2511a2a6d7eSDeng-Cheng Zhu 	write_tc_c0_tcstatus(read_tc_c0_tcstatus() & ~TCSTATUS_A);
2521a2a6d7eSDeng-Cheng Zhu 
2531a2a6d7eSDeng-Cheng Zhu 	v->state = VPE_STATE_UNUSED;
2541a2a6d7eSDeng-Cheng Zhu 
2551a2a6d7eSDeng-Cheng Zhu 	clear_c0_mvpcontrol(MVPCONTROL_VPC);
2561a2a6d7eSDeng-Cheng Zhu 	evpe(evpe_flags);
2571a2a6d7eSDeng-Cheng Zhu 
2581a2a6d7eSDeng-Cheng Zhu 	return 0;
2591a2a6d7eSDeng-Cheng Zhu }
2601a2a6d7eSDeng-Cheng Zhu EXPORT_SYMBOL(vpe_free);
2611a2a6d7eSDeng-Cheng Zhu 
2621a2a6d7eSDeng-Cheng Zhu static ssize_t store_kill(struct device *dev, struct device_attribute *attr,
2631a2a6d7eSDeng-Cheng Zhu 			  const char *buf, size_t len)
2641a2a6d7eSDeng-Cheng Zhu {
2651a2a6d7eSDeng-Cheng Zhu 	struct vpe *vpe = get_vpe(aprp_cpu_index());
2661a2a6d7eSDeng-Cheng Zhu 	struct vpe_notifications *notifier;
2671a2a6d7eSDeng-Cheng Zhu 
2681a2a6d7eSDeng-Cheng Zhu 	list_for_each_entry(notifier, &vpe->notify, list)
2691a2a6d7eSDeng-Cheng Zhu 		notifier->stop(aprp_cpu_index());
2701a2a6d7eSDeng-Cheng Zhu 
2711a2a6d7eSDeng-Cheng Zhu 	release_progmem(vpe->load_addr);
2721a2a6d7eSDeng-Cheng Zhu 	cleanup_tc(get_tc(aprp_cpu_index()));
2731a2a6d7eSDeng-Cheng Zhu 	vpe_stop(vpe);
2741a2a6d7eSDeng-Cheng Zhu 	vpe_free(vpe);
2751a2a6d7eSDeng-Cheng Zhu 
2761a2a6d7eSDeng-Cheng Zhu 	return len;
2771a2a6d7eSDeng-Cheng Zhu }
2781a2a6d7eSDeng-Cheng Zhu static DEVICE_ATTR(kill, S_IWUSR, NULL, store_kill);
2791a2a6d7eSDeng-Cheng Zhu 
2801a2a6d7eSDeng-Cheng Zhu static ssize_t ntcs_show(struct device *cd, struct device_attribute *attr,
2811a2a6d7eSDeng-Cheng Zhu 			 char *buf)
2821a2a6d7eSDeng-Cheng Zhu {
2831a2a6d7eSDeng-Cheng Zhu 	struct vpe *vpe = get_vpe(aprp_cpu_index());
2841a2a6d7eSDeng-Cheng Zhu 
2851a2a6d7eSDeng-Cheng Zhu 	return sprintf(buf, "%d\n", vpe->ntcs);
2861a2a6d7eSDeng-Cheng Zhu }
2871a2a6d7eSDeng-Cheng Zhu 
2881a2a6d7eSDeng-Cheng Zhu static ssize_t ntcs_store(struct device *dev, struct device_attribute *attr,
2891a2a6d7eSDeng-Cheng Zhu 			  const char *buf, size_t len)
2901a2a6d7eSDeng-Cheng Zhu {
2911a2a6d7eSDeng-Cheng Zhu 	struct vpe *vpe = get_vpe(aprp_cpu_index());
2921a2a6d7eSDeng-Cheng Zhu 	unsigned long new;
2931a2a6d7eSDeng-Cheng Zhu 	int ret;
2941a2a6d7eSDeng-Cheng Zhu 
2951a2a6d7eSDeng-Cheng Zhu 	ret = kstrtoul(buf, 0, &new);
2961a2a6d7eSDeng-Cheng Zhu 	if (ret < 0)
2971a2a6d7eSDeng-Cheng Zhu 		return ret;
2981a2a6d7eSDeng-Cheng Zhu 
2991a2a6d7eSDeng-Cheng Zhu 	if (new == 0 || new > (hw_tcs - aprp_cpu_index()))
3001a2a6d7eSDeng-Cheng Zhu 		return -EINVAL;
3011a2a6d7eSDeng-Cheng Zhu 
3021a2a6d7eSDeng-Cheng Zhu 	vpe->ntcs = new;
3031a2a6d7eSDeng-Cheng Zhu 
3041a2a6d7eSDeng-Cheng Zhu 	return len;
3051a2a6d7eSDeng-Cheng Zhu }
3061a2a6d7eSDeng-Cheng Zhu static DEVICE_ATTR_RW(ntcs);
3071a2a6d7eSDeng-Cheng Zhu 
3081a2a6d7eSDeng-Cheng Zhu static struct attribute *vpe_attrs[] = {
3091a2a6d7eSDeng-Cheng Zhu 	&dev_attr_kill.attr,
3101a2a6d7eSDeng-Cheng Zhu 	&dev_attr_ntcs.attr,
3111a2a6d7eSDeng-Cheng Zhu 	NULL,
3121a2a6d7eSDeng-Cheng Zhu };
3131a2a6d7eSDeng-Cheng Zhu ATTRIBUTE_GROUPS(vpe);
3141a2a6d7eSDeng-Cheng Zhu 
3151a2a6d7eSDeng-Cheng Zhu static void vpe_device_release(struct device *cd)
3161a2a6d7eSDeng-Cheng Zhu {
3171a2a6d7eSDeng-Cheng Zhu 	kfree(cd);
3181a2a6d7eSDeng-Cheng Zhu }
3191a2a6d7eSDeng-Cheng Zhu 
3201a2a6d7eSDeng-Cheng Zhu static struct class vpe_class = {
3211a2a6d7eSDeng-Cheng Zhu 	.name = "vpe",
3221a2a6d7eSDeng-Cheng Zhu 	.owner = THIS_MODULE,
3231a2a6d7eSDeng-Cheng Zhu 	.dev_release = vpe_device_release,
3241a2a6d7eSDeng-Cheng Zhu 	.dev_groups = vpe_groups,
3251a2a6d7eSDeng-Cheng Zhu };
3261a2a6d7eSDeng-Cheng Zhu 
3271a2a6d7eSDeng-Cheng Zhu static struct device vpe_device;
3281a2a6d7eSDeng-Cheng Zhu 
3291a2a6d7eSDeng-Cheng Zhu int __init vpe_module_init(void)
3301a2a6d7eSDeng-Cheng Zhu {
3311a2a6d7eSDeng-Cheng Zhu 	unsigned int mtflags, vpflags;
3321a2a6d7eSDeng-Cheng Zhu 	unsigned long flags, val;
3331a2a6d7eSDeng-Cheng Zhu 	struct vpe *v = NULL;
3341a2a6d7eSDeng-Cheng Zhu 	struct tc *t;
3351a2a6d7eSDeng-Cheng Zhu 	int tc, err;
3361a2a6d7eSDeng-Cheng Zhu 
3371a2a6d7eSDeng-Cheng Zhu 	if (!cpu_has_mipsmt) {
3381a2a6d7eSDeng-Cheng Zhu 		pr_warn("VPE loader: not a MIPS MT capable processor\n");
3391a2a6d7eSDeng-Cheng Zhu 		return -ENODEV;
3401a2a6d7eSDeng-Cheng Zhu 	}
3411a2a6d7eSDeng-Cheng Zhu 
3421a2a6d7eSDeng-Cheng Zhu 	if (vpelimit == 0) {
3431a2a6d7eSDeng-Cheng Zhu 		pr_warn("No VPEs reserved for AP/SP, not initialize VPE loader\n"
3441a2a6d7eSDeng-Cheng Zhu 			"Pass maxvpes=<n> argument as kernel argument\n");
3451a2a6d7eSDeng-Cheng Zhu 
3461a2a6d7eSDeng-Cheng Zhu 		return -ENODEV;
3471a2a6d7eSDeng-Cheng Zhu 	}
3481a2a6d7eSDeng-Cheng Zhu 
3491a2a6d7eSDeng-Cheng Zhu 	if (aprp_cpu_index() == 0) {
3501a2a6d7eSDeng-Cheng Zhu 		pr_warn("No TCs reserved for AP/SP, not initialize VPE loader\n"
3511a2a6d7eSDeng-Cheng Zhu 			"Pass maxtcs=<n> argument as kernel argument\n");
3521a2a6d7eSDeng-Cheng Zhu 
3531a2a6d7eSDeng-Cheng Zhu 		return -ENODEV;
3541a2a6d7eSDeng-Cheng Zhu 	}
3551a2a6d7eSDeng-Cheng Zhu 
3561a2a6d7eSDeng-Cheng Zhu 	major = register_chrdev(0, VPE_MODULE_NAME, &vpe_fops);
3571a2a6d7eSDeng-Cheng Zhu 	if (major < 0) {
3581a2a6d7eSDeng-Cheng Zhu 		pr_warn("VPE loader: unable to register character device\n");
3591a2a6d7eSDeng-Cheng Zhu 		return major;
3601a2a6d7eSDeng-Cheng Zhu 	}
3611a2a6d7eSDeng-Cheng Zhu 
3621a2a6d7eSDeng-Cheng Zhu 	err = class_register(&vpe_class);
3631a2a6d7eSDeng-Cheng Zhu 	if (err) {
3641a2a6d7eSDeng-Cheng Zhu 		pr_err("vpe_class registration failed\n");
3651a2a6d7eSDeng-Cheng Zhu 		goto out_chrdev;
3661a2a6d7eSDeng-Cheng Zhu 	}
3671a2a6d7eSDeng-Cheng Zhu 
3681a2a6d7eSDeng-Cheng Zhu 	device_initialize(&vpe_device);
3691a2a6d7eSDeng-Cheng Zhu 	vpe_device.class	= &vpe_class,
3701a2a6d7eSDeng-Cheng Zhu 	vpe_device.parent	= NULL,
3711a2a6d7eSDeng-Cheng Zhu 	dev_set_name(&vpe_device, "vpe1");
3721a2a6d7eSDeng-Cheng Zhu 	vpe_device.devt = MKDEV(major, VPE_MODULE_MINOR);
3731a2a6d7eSDeng-Cheng Zhu 	err = device_add(&vpe_device);
3741a2a6d7eSDeng-Cheng Zhu 	if (err) {
3751a2a6d7eSDeng-Cheng Zhu 		pr_err("Adding vpe_device failed\n");
3761a2a6d7eSDeng-Cheng Zhu 		goto out_class;
3771a2a6d7eSDeng-Cheng Zhu 	}
3781a2a6d7eSDeng-Cheng Zhu 
3791a2a6d7eSDeng-Cheng Zhu 	local_irq_save(flags);
3801a2a6d7eSDeng-Cheng Zhu 	mtflags = dmt();
3811a2a6d7eSDeng-Cheng Zhu 	vpflags = dvpe();
3821a2a6d7eSDeng-Cheng Zhu 
3831a2a6d7eSDeng-Cheng Zhu 	/* Put MVPE's into 'configuration state' */
3841a2a6d7eSDeng-Cheng Zhu 	set_c0_mvpcontrol(MVPCONTROL_VPC);
3851a2a6d7eSDeng-Cheng Zhu 
3861a2a6d7eSDeng-Cheng Zhu 	val = read_c0_mvpconf0();
3871a2a6d7eSDeng-Cheng Zhu 	hw_tcs = (val & MVPCONF0_PTC) + 1;
3881a2a6d7eSDeng-Cheng Zhu 	hw_vpes = ((val & MVPCONF0_PVPE) >> MVPCONF0_PVPE_SHIFT) + 1;
3891a2a6d7eSDeng-Cheng Zhu 
3901a2a6d7eSDeng-Cheng Zhu 	for (tc = aprp_cpu_index(); tc < hw_tcs; tc++) {
3911a2a6d7eSDeng-Cheng Zhu 		/*
3921a2a6d7eSDeng-Cheng Zhu 		 * Must re-enable multithreading temporarily or in case we
3931a2a6d7eSDeng-Cheng Zhu 		 * reschedule send IPIs or similar we might hang.
3941a2a6d7eSDeng-Cheng Zhu 		 */
3951a2a6d7eSDeng-Cheng Zhu 		clear_c0_mvpcontrol(MVPCONTROL_VPC);
3961a2a6d7eSDeng-Cheng Zhu 		evpe(vpflags);
3971a2a6d7eSDeng-Cheng Zhu 		emt(mtflags);
3981a2a6d7eSDeng-Cheng Zhu 		local_irq_restore(flags);
3991a2a6d7eSDeng-Cheng Zhu 		t = alloc_tc(tc);
4001a2a6d7eSDeng-Cheng Zhu 		if (!t) {
4011a2a6d7eSDeng-Cheng Zhu 			err = -ENOMEM;
4021a2a6d7eSDeng-Cheng Zhu 			goto out_dev;
4031a2a6d7eSDeng-Cheng Zhu 		}
4041a2a6d7eSDeng-Cheng Zhu 
4051a2a6d7eSDeng-Cheng Zhu 		local_irq_save(flags);
4061a2a6d7eSDeng-Cheng Zhu 		mtflags = dmt();
4071a2a6d7eSDeng-Cheng Zhu 		vpflags = dvpe();
4081a2a6d7eSDeng-Cheng Zhu 		set_c0_mvpcontrol(MVPCONTROL_VPC);
4091a2a6d7eSDeng-Cheng Zhu 
4101a2a6d7eSDeng-Cheng Zhu 		/* VPE's */
4111a2a6d7eSDeng-Cheng Zhu 		if (tc < hw_tcs) {
4121a2a6d7eSDeng-Cheng Zhu 			settc(tc);
4131a2a6d7eSDeng-Cheng Zhu 
4141a2a6d7eSDeng-Cheng Zhu 			v = alloc_vpe(tc);
4151a2a6d7eSDeng-Cheng Zhu 			if (v == NULL) {
4161a2a6d7eSDeng-Cheng Zhu 				pr_warn("VPE: unable to allocate VPE\n");
4171a2a6d7eSDeng-Cheng Zhu 				goto out_reenable;
4181a2a6d7eSDeng-Cheng Zhu 			}
4191a2a6d7eSDeng-Cheng Zhu 
4201a2a6d7eSDeng-Cheng Zhu 			v->ntcs = hw_tcs - aprp_cpu_index();
4211a2a6d7eSDeng-Cheng Zhu 
4221a2a6d7eSDeng-Cheng Zhu 			/* add the tc to the list of this vpe's tc's. */
4231a2a6d7eSDeng-Cheng Zhu 			list_add(&t->tc, &v->tc);
4241a2a6d7eSDeng-Cheng Zhu 
4251a2a6d7eSDeng-Cheng Zhu 			/* deactivate all but vpe0 */
4261a2a6d7eSDeng-Cheng Zhu 			if (tc >= aprp_cpu_index()) {
4271a2a6d7eSDeng-Cheng Zhu 				unsigned long tmp = read_vpe_c0_vpeconf0();
4281a2a6d7eSDeng-Cheng Zhu 
4291a2a6d7eSDeng-Cheng Zhu 				tmp &= ~VPECONF0_VPA;
4301a2a6d7eSDeng-Cheng Zhu 
4311a2a6d7eSDeng-Cheng Zhu 				/* master VPE */
4321a2a6d7eSDeng-Cheng Zhu 				tmp |= VPECONF0_MVP;
4331a2a6d7eSDeng-Cheng Zhu 				write_vpe_c0_vpeconf0(tmp);
4341a2a6d7eSDeng-Cheng Zhu 			}
4351a2a6d7eSDeng-Cheng Zhu 
4361a2a6d7eSDeng-Cheng Zhu 			/* disable multi-threading with TC's */
4371a2a6d7eSDeng-Cheng Zhu 			write_vpe_c0_vpecontrol(read_vpe_c0_vpecontrol() &
4381a2a6d7eSDeng-Cheng Zhu 						~VPECONTROL_TE);
4391a2a6d7eSDeng-Cheng Zhu 
4401a2a6d7eSDeng-Cheng Zhu 			if (tc >= vpelimit) {
4411a2a6d7eSDeng-Cheng Zhu 				/*
4421a2a6d7eSDeng-Cheng Zhu 				 * Set config to be the same as vpe0,
4431a2a6d7eSDeng-Cheng Zhu 				 * particularly kseg0 coherency alg
4441a2a6d7eSDeng-Cheng Zhu 				 */
4451a2a6d7eSDeng-Cheng Zhu 				write_vpe_c0_config(read_c0_config());
4461a2a6d7eSDeng-Cheng Zhu 			}
4471a2a6d7eSDeng-Cheng Zhu 		}
4481a2a6d7eSDeng-Cheng Zhu 
4491a2a6d7eSDeng-Cheng Zhu 		/* TC's */
4501a2a6d7eSDeng-Cheng Zhu 		t->pvpe = v;	/* set the parent vpe */
4511a2a6d7eSDeng-Cheng Zhu 
4521a2a6d7eSDeng-Cheng Zhu 		if (tc >= aprp_cpu_index()) {
4531a2a6d7eSDeng-Cheng Zhu 			unsigned long tmp;
4541a2a6d7eSDeng-Cheng Zhu 
4551a2a6d7eSDeng-Cheng Zhu 			settc(tc);
4561a2a6d7eSDeng-Cheng Zhu 
4571a2a6d7eSDeng-Cheng Zhu 			/* Any TC that is bound to VPE0 gets left as is - in
4581a2a6d7eSDeng-Cheng Zhu 			 * case we are running SMTC on VPE0. A TC that is bound
4591a2a6d7eSDeng-Cheng Zhu 			 * to any other VPE gets bound to VPE0, ideally I'd like
4601a2a6d7eSDeng-Cheng Zhu 			 * to make it homeless but it doesn't appear to let me
4611a2a6d7eSDeng-Cheng Zhu 			 * bind a TC to a non-existent VPE. Which is perfectly
4621a2a6d7eSDeng-Cheng Zhu 			 * reasonable.
4631a2a6d7eSDeng-Cheng Zhu 			 *
4641a2a6d7eSDeng-Cheng Zhu 			 * The (un)bound state is visible to an EJTAG probe so
4651a2a6d7eSDeng-Cheng Zhu 			 * may notify GDB...
4661a2a6d7eSDeng-Cheng Zhu 			 */
4671a2a6d7eSDeng-Cheng Zhu 			tmp = read_tc_c0_tcbind();
4681a2a6d7eSDeng-Cheng Zhu 			if (tmp & TCBIND_CURVPE) {
4691a2a6d7eSDeng-Cheng Zhu 				/* tc is bound >vpe0 */
4701a2a6d7eSDeng-Cheng Zhu 				write_tc_c0_tcbind(tmp & ~TCBIND_CURVPE);
4711a2a6d7eSDeng-Cheng Zhu 
4721a2a6d7eSDeng-Cheng Zhu 				t->pvpe = get_vpe(0);	/* set the parent vpe */
4731a2a6d7eSDeng-Cheng Zhu 			}
4741a2a6d7eSDeng-Cheng Zhu 
4751a2a6d7eSDeng-Cheng Zhu 			/* halt the TC */
4761a2a6d7eSDeng-Cheng Zhu 			write_tc_c0_tchalt(TCHALT_H);
4771a2a6d7eSDeng-Cheng Zhu 			mips_ihb();
4781a2a6d7eSDeng-Cheng Zhu 
4791a2a6d7eSDeng-Cheng Zhu 			tmp = read_tc_c0_tcstatus();
4801a2a6d7eSDeng-Cheng Zhu 
4811a2a6d7eSDeng-Cheng Zhu 			/* mark not activated and not dynamically allocatable */
4821a2a6d7eSDeng-Cheng Zhu 			tmp &= ~(TCSTATUS_A | TCSTATUS_DA);
4831a2a6d7eSDeng-Cheng Zhu 			tmp |= TCSTATUS_IXMT;	/* interrupt exempt */
4841a2a6d7eSDeng-Cheng Zhu 			write_tc_c0_tcstatus(tmp);
4851a2a6d7eSDeng-Cheng Zhu 		}
4861a2a6d7eSDeng-Cheng Zhu 	}
4871a2a6d7eSDeng-Cheng Zhu 
4881a2a6d7eSDeng-Cheng Zhu out_reenable:
4891a2a6d7eSDeng-Cheng Zhu 	/* release config state */
4901a2a6d7eSDeng-Cheng Zhu 	clear_c0_mvpcontrol(MVPCONTROL_VPC);
4911a2a6d7eSDeng-Cheng Zhu 
4921a2a6d7eSDeng-Cheng Zhu 	evpe(vpflags);
4931a2a6d7eSDeng-Cheng Zhu 	emt(mtflags);
4941a2a6d7eSDeng-Cheng Zhu 	local_irq_restore(flags);
4951a2a6d7eSDeng-Cheng Zhu 
4961a2a6d7eSDeng-Cheng Zhu 	return 0;
4971a2a6d7eSDeng-Cheng Zhu 
4981a2a6d7eSDeng-Cheng Zhu out_dev:
4991a2a6d7eSDeng-Cheng Zhu 	device_del(&vpe_device);
5001a2a6d7eSDeng-Cheng Zhu 
5011a2a6d7eSDeng-Cheng Zhu out_class:
5021a2a6d7eSDeng-Cheng Zhu 	class_unregister(&vpe_class);
5031a2a6d7eSDeng-Cheng Zhu 
5041a2a6d7eSDeng-Cheng Zhu out_chrdev:
5051a2a6d7eSDeng-Cheng Zhu 	unregister_chrdev(major, VPE_MODULE_NAME);
5061a2a6d7eSDeng-Cheng Zhu 
5071a2a6d7eSDeng-Cheng Zhu 	return err;
5081a2a6d7eSDeng-Cheng Zhu }
5091a2a6d7eSDeng-Cheng Zhu 
5101a2a6d7eSDeng-Cheng Zhu void __exit vpe_module_exit(void)
5111a2a6d7eSDeng-Cheng Zhu {
5121a2a6d7eSDeng-Cheng Zhu 	struct vpe *v, *n;
5131a2a6d7eSDeng-Cheng Zhu 
5141a2a6d7eSDeng-Cheng Zhu 	device_del(&vpe_device);
5151a2a6d7eSDeng-Cheng Zhu 	class_unregister(&vpe_class);
5161a2a6d7eSDeng-Cheng Zhu 	unregister_chrdev(major, VPE_MODULE_NAME);
5171a2a6d7eSDeng-Cheng Zhu 
5181a2a6d7eSDeng-Cheng Zhu 	/* No locking needed here */
5191a2a6d7eSDeng-Cheng Zhu 	list_for_each_entry_safe(v, n, &vpecontrol.vpe_list, list) {
5201a2a6d7eSDeng-Cheng Zhu 		if (v->state != VPE_STATE_UNUSED)
5211a2a6d7eSDeng-Cheng Zhu 			release_vpe(v);
5221a2a6d7eSDeng-Cheng Zhu 	}
5231a2a6d7eSDeng-Cheng Zhu }
524