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