1/*
2 * arch/ubicom32/kernel/thread.c
3 *   Ubicom32 architecture hardware thread support.
4 *
5 * (C) Copyright 2009, Ubicom, Inc.
6 *
7 * This file is part of the Ubicom32 Linux Kernel Port.
8 *
9 * The Ubicom32 Linux Kernel Port is free software: you can redistribute
10 * it and/or modify it under the terms of the GNU General Public License
11 * as published by the Free Software Foundation, either version 2 of the
12 * License, or (at your option) any later version.
13 *
14 * The Ubicom32 Linux Kernel Port is distributed in the hope that it
15 * will be useful, but WITHOUT ANY WARRANTY; without even the implied
16 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
17 * the GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with the Ubicom32 Linux Kernel Port.  If not,
21 * see <http://www.gnu.org/licenses/>.
22 *
23 * Ubicom32 implementation derived from (with many thanks):
24 *   arch/m68knommu
25 *   arch/blackfin
26 *   arch/parisc
27 */
28
29#include <linux/module.h>
30#include <linux/kernel.h>
31#include <linux/init.h>
32#include <linux/sched.h>
33#include <linux/interrupt.h>
34#include <linux/irq.h>
35#include <linux/profile.h>
36#include <linux/clocksource.h>
37#include <linux/types.h>
38#include <asm/ip5000.h>
39#include <asm/machdep.h>
40#include <asm/asm-offsets.h>
41#include <asm/thread.h>
42
43/*
44 * TODO: At some point change the name here to be thread_ksp
45 */
46unsigned int sw_ksp[THREAD_ARCHITECTURAL_MAX];
47
48static unsigned int thread_mask = -1;
49static unsigned int thread_mainline_mask;
50
51/*
52 * thread_entry()
53 *	Returning from the called function will disable the thread.
54 *
55 * This could be a naked call to allow for hwthreads that do not have stacks.
56 * However, with -O0, the code still writes to thex stack, and this was
57 * corrupting memory just after the callers stack.
58 */
59static void thread_entry(void *arg, thread_exec_fn_t exec)
60{
61	/*
62	 * Call thread function
63	 */
64	exec(arg);
65
66	/*
67	 * Complete => Disable self
68	 */
69	thread_disable(thread_get_self());
70}
71
72/*
73 * thread_start()
74 *	Start the specified function on the specified hardware thread.
75 */
76thread_t thread_start(thread_t thread,
77		      thread_exec_fn_t exec,
78		      void *arg,
79		      unsigned int *sp_high,
80		      thread_type_t type)
81{
82	/*
83	 * Sanity check
84	 */
85	unsigned int enabled, mask, csr;
86	asm volatile (
87		"move.4		%0, MT_EN\n\t"
88		: "=m" (enabled)
89	);
90
91	mask = 1 << thread;
92	if (enabled & mask) {
93		printk(KERN_WARNING "request to enable a previously enabled thread\n");
94		return (thread_t)-1;
95	}
96
97	/*
98	 * Update thread state
99	 */
100	csr = (thread << 15) | (1 << 14);
101	asm volatile (
102		"setcsr		%0		\n\t"
103		"setcsr_flush	0		\n\t"
104
105		"move.4		A0, #0		\n\t"
106		"move.4		A1, #0		\n\t"
107		"move.4		A2, #0		\n\t"
108		"move.4		A3, #0		\n\t"
109		"move.4		A4, #0		\n\t"
110		"move.4		A5, #0		\n\t"
111		"move.4		A6, #0		\n\t"
112		"move.4		SP, %4		\n\t"	/* A7 is SP */
113
114		"move.4		D0, %3		\n\t"
115		"move.4		D1, %2		\n\t"
116		"move.4		D2, #0		\n\t"
117		"move.4		D3, #0		\n\t"
118		"move.4		D4, #0		\n\t"
119		"move.4		D5, #0		\n\t"
120		"move.4		D6, #0		\n\t"
121		"move.4		D7, #0		\n\t"
122		"move.4		D8, #0		\n\t"
123		"move.4		D9, #0		\n\t"
124		"move.4		D10, #0		\n\t"
125		"move.4		D11, #0		\n\t"
126		"move.4		D12, #0		\n\t"
127		"move.4		D13, #0		\n\t"
128		"move.4		D14, #0		\n\t"
129		"move.4		D15, #0		\n\t"
130
131		"move.4		INT_MASK0, #0	\n\t"
132		"move.4		INT_MASK1, #0	\n\t"
133		"move.4		PC, %1		\n\t"
134		"setcsr		#0		\n\t"
135		"setcsr_flush	0		\n\t"
136		:
137		: "r" (csr), "r" (thread_entry), "r" (exec),
138		  "r" (arg), "r" (sp_high)
139	);
140
141	/*
142	 * Apply HRT state
143	 */
144	if (type & THREAD_TYPE_HRT) {
145		asm volatile (
146			"or.4		MT_HRT, MT_HRT, %0\n\t"
147			:
148			: "d" (mask)
149			: "cc"
150		);
151	} else {
152		asm volatile (
153			"and.4		MT_HRT, MT_HRT, %0\n\t"
154			:
155			: "d" (~mask)
156			: "cc"
157		);
158	}
159
160	/*
161	 * Set priority
162	 */
163	asm volatile (
164		"or.4		MT_HPRI, MT_HPRI, %0\n\t"
165		:
166		: "d" (mask)
167		: "cc"
168	);
169
170	/*
171	 * Enable thread
172	 */
173	asm volatile (
174		"move.4		MT_ACTIVE_SET, %0	\n\t"
175		:
176		: "d" (mask)
177	);
178	thread_enable_mask(mask);
179	return thread;
180}
181
182/*
183 * thread_get_mainline()
184 *	Return a mask of those threads that are Linux mainline threads.
185 */
186unsigned int thread_get_mainline(void)
187{
188	return thread_mainline_mask;
189}
190
191/*
192 * thread_set_mainline()
193 *	Indicate that the specified thread is a Linux mainline thread.
194 */
195void thread_set_mainline(thread_t tid)
196{
197	thread_mainline_mask |= (1 << tid);
198}
199
200/*
201 * thread_alloc()
202 *	Allocate an unused hardware thread.
203 */
204thread_t thread_alloc(void)
205{
206	thread_t tid;
207
208	/*
209	 * If this is the first time we are here get the list of unused
210	 * threads from the processor device tree node.
211	 */
212	if (thread_mask == -1) {
213		thread_mask = processor_threads();
214	}
215
216	if (!thread_mask) {
217		return (thread_t)-1;
218	}
219
220	tid = ffs(thread_mask);
221	if (tid != 0) {
222		tid--;
223		thread_mask &= ~(1 << tid);
224		return tid;
225	}
226
227	return (thread_t)-1;
228}
229