aboutsummaryrefslogtreecommitdiffstats
path: root/arch/mips/kernel
diff options
context:
space:
mode:
Diffstat (limited to 'arch/mips/kernel')
-rw-r--r--arch/mips/kernel/cpufreq/loongson2_clock.c170
-rw-r--r--arch/mips/kernel/init_task.c35
-rw-r--r--arch/mips/kernel/irq-rm9000.c107
-rw-r--r--arch/mips/kernel/kspd.c423
4 files changed, 735 insertions, 0 deletions
diff --git a/arch/mips/kernel/cpufreq/loongson2_clock.c b/arch/mips/kernel/cpufreq/loongson2_clock.c
new file mode 100644
index 00000000000..cefc6e259ba
--- /dev/null
+++ b/arch/mips/kernel/cpufreq/loongson2_clock.c
@@ -0,0 +1,170 @@
1/*
2 * Copyright (C) 2006 - 2008 Lemote Inc. & Insititute of Computing Technology
3 * Author: Yanhua, yanh@lemote.com
4 *
5 * This file is subject to the terms and conditions of the GNU General Public
6 * License. See the file "COPYING" in the main directory of this archive
7 * for more details.
8 */
9
10#include <linux/cpufreq.h>
11#include <linux/platform_device.h>
12
13#include <asm/clock.h>
14
15#include <loongson.h>
16
17static LIST_HEAD(clock_list);
18static DEFINE_SPINLOCK(clock_lock);
19static DEFINE_MUTEX(clock_list_sem);
20
21/* Minimum CLK support */
22enum {
23 DC_ZERO, DC_25PT = 2, DC_37PT, DC_50PT, DC_62PT, DC_75PT,
24 DC_87PT, DC_DISABLE, DC_RESV
25};
26
27struct cpufreq_frequency_table loongson2_clockmod_table[] = {
28 {DC_RESV, CPUFREQ_ENTRY_INVALID},
29 {DC_ZERO, CPUFREQ_ENTRY_INVALID},
30 {DC_25PT, 0},
31 {DC_37PT, 0},
32 {DC_50PT, 0},
33 {DC_62PT, 0},
34 {DC_75PT, 0},
35 {DC_87PT, 0},
36 {DC_DISABLE, 0},
37 {DC_RESV, CPUFREQ_TABLE_END},
38};
39EXPORT_SYMBOL_GPL(loongson2_clockmod_table);
40
41static struct clk cpu_clk = {
42 .name = "cpu_clk",
43 .flags = CLK_ALWAYS_ENABLED | CLK_RATE_PROPAGATES,
44 .rate = 800000000,
45};
46
47struct clk *clk_get(struct device *dev, const char *id)
48{
49 return &cpu_clk;
50}
51EXPORT_SYMBOL(clk_get);
52
53static void propagate_rate(struct clk *clk)
54{
55 struct clk *clkp;
56
57 list_for_each_entry(clkp, &clock_list, node) {
58 if (likely(clkp->parent != clk))
59 continue;
60 if (likely(clkp->ops && clkp->ops->recalc))
61 clkp->ops->recalc(clkp);
62 if (unlikely(clkp->flags & CLK_RATE_PROPAGATES))
63 propagate_rate(clkp);
64 }
65}
66
67int clk_enable(struct clk *clk)
68{
69 return 0;
70}
71EXPORT_SYMBOL(clk_enable);
72
73void clk_disable(struct clk *clk)
74{
75}
76EXPORT_SYMBOL(clk_disable);
77
78unsigned long clk_get_rate(struct clk *clk)
79{
80 return (unsigned long)clk->rate;
81}
82EXPORT_SYMBOL(clk_get_rate);
83
84void clk_put(struct clk *clk)
85{
86}
87EXPORT_SYMBOL(clk_put);
88
89int clk_set_rate(struct clk *clk, unsigned long rate)
90{
91 return clk_set_rate_ex(clk, rate, 0);
92}
93EXPORT_SYMBOL_GPL(clk_set_rate);
94
95int clk_set_rate_ex(struct clk *clk, unsigned long rate, int algo_id)
96{
97 int ret = 0;
98 int regval;
99 int i;
100
101 if (likely(clk->ops && clk->ops->set_rate)) {
102 unsigned long flags;
103
104 spin_lock_irqsave(&clock_lock, flags);
105 ret = clk->ops->set_rate(clk, rate, algo_id);
106 spin_unlock_irqrestore(&clock_lock, flags);
107 }
108
109 if (unlikely(clk->flags & CLK_RATE_PROPAGATES))
110 propagate_rate(clk);
111
112 for (i = 0; loongson2_clockmod_table[i].frequency != CPUFREQ_TABLE_END;
113 i++) {
114 if (loongson2_clockmod_table[i].frequency ==
115 CPUFREQ_ENTRY_INVALID)
116 continue;
117 if (rate == loongson2_clockmod_table[i].frequency)
118 break;
119 }
120 if (rate != loongson2_clockmod_table[i].frequency)
121 return -ENOTSUPP;
122
123 clk->rate = rate;
124
125 regval = LOONGSON_CHIPCFG0;
126 regval = (regval & ~0x7) | (loongson2_clockmod_table[i].index - 1);
127 LOONGSON_CHIPCFG0 = regval;
128
129 return ret;
130}
131EXPORT_SYMBOL_GPL(clk_set_rate_ex);
132
133long clk_round_rate(struct clk *clk, unsigned long rate)
134{
135 if (likely(clk->ops && clk->ops->round_rate)) {
136 unsigned long flags, rounded;
137
138 spin_lock_irqsave(&clock_lock, flags);
139 rounded = clk->ops->round_rate(clk, rate);
140 spin_unlock_irqrestore(&clock_lock, flags);
141
142 return rounded;
143 }
144
145 return rate;
146}
147EXPORT_SYMBOL_GPL(clk_round_rate);
148
149/*
150 * This is the simple version of Loongson-2 wait, Maybe we need do this in
151 * interrupt disabled content
152 */
153
154DEFINE_SPINLOCK(loongson2_wait_lock);
155void loongson2_cpu_wait(void)
156{
157 u32 cpu_freq;
158 unsigned long flags;
159
160 spin_lock_irqsave(&loongson2_wait_lock, flags);
161 cpu_freq = LOONGSON_CHIPCFG0;
162 LOONGSON_CHIPCFG0 &= ~0x7; /* Put CPU into wait mode */
163 LOONGSON_CHIPCFG0 = cpu_freq; /* Restore CPU state */
164 spin_unlock_irqrestore(&loongson2_wait_lock, flags);
165}
166EXPORT_SYMBOL_GPL(loongson2_cpu_wait);
167
168MODULE_AUTHOR("Yanhua <yanh@lemote.com>");
169MODULE_DESCRIPTION("cpufreq driver for Loongson 2F");
170MODULE_LICENSE("GPL");
diff --git a/arch/mips/kernel/init_task.c b/arch/mips/kernel/init_task.c
new file mode 100644
index 00000000000..6d6ca530589
--- /dev/null
+++ b/arch/mips/kernel/init_task.c
@@ -0,0 +1,35 @@
1#include <linux/mm.h>
2#include <linux/module.h>
3#include <linux/sched.h>
4#include <linux/init_task.h>
5#include <linux/fs.h>
6#include <linux/mqueue.h>
7
8#include <asm/thread_info.h>
9#include <asm/uaccess.h>
10#include <asm/pgtable.h>
11
12static struct signal_struct init_signals = INIT_SIGNALS(init_signals);
13static struct sighand_struct init_sighand = INIT_SIGHAND(init_sighand);
14/*
15 * Initial thread structure.
16 *
17 * We need to make sure that this is 8192-byte aligned due to the
18 * way process stacks are handled. This is done by making sure
19 * the linker maps this in the .text segment right after head.S,
20 * and making head.S ensure the proper alignment.
21 *
22 * The things we do for performance..
23 */
24union thread_union init_thread_union __init_task_data
25 __attribute__((__aligned__(THREAD_SIZE))) =
26 { INIT_THREAD_INFO(init_task) };
27
28/*
29 * Initial task structure.
30 *
31 * All other task structs will be allocated on slabs in fork.c
32 */
33struct task_struct init_task = INIT_TASK(init_task);
34
35EXPORT_SYMBOL(init_task);
diff --git a/arch/mips/kernel/irq-rm9000.c b/arch/mips/kernel/irq-rm9000.c
new file mode 100644
index 00000000000..38874a4b925
--- /dev/null
+++ b/arch/mips/kernel/irq-rm9000.c
@@ -0,0 +1,107 @@
1/*
2 * Copyright (C) 2003 Ralf Baechle
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License as published by the
6 * Free Software Foundation; either version 2 of the License, or (at your
7 * option) any later version.
8 *
9 * Handler for RM9000 extended interrupts. These are a non-standard
10 * feature so we handle them separately from standard interrupts.
11 */
12#include <linux/init.h>
13#include <linux/interrupt.h>
14#include <linux/irq.h>
15#include <linux/kernel.h>
16#include <linux/module.h>
17
18#include <asm/irq_cpu.h>
19#include <asm/mipsregs.h>
20#include <asm/system.h>
21
22static inline void unmask_rm9k_irq(struct irq_data *d)
23{
24 set_c0_intcontrol(0x1000 << (d->irq - RM9K_CPU_IRQ_BASE));
25}
26
27static inline void mask_rm9k_irq(struct irq_data *d)
28{
29 clear_c0_intcontrol(0x1000 << (d->irq - RM9K_CPU_IRQ_BASE));
30}
31
32static inline void rm9k_cpu_irq_enable(struct irq_data *d)
33{
34 unsigned long flags;
35
36 local_irq_save(flags);
37 unmask_rm9k_irq(d);
38 local_irq_restore(flags);
39}
40
41/*
42 * Performance counter interrupts are global on all processors.
43 */
44static void local_rm9k_perfcounter_irq_startup(void *args)
45{
46 rm9k_cpu_irq_enable(args);
47}
48
49static unsigned int rm9k_perfcounter_irq_startup(struct irq_data *d)
50{
51 on_each_cpu(local_rm9k_perfcounter_irq_startup, d, 1);
52
53 return 0;
54}
55
56static void local_rm9k_perfcounter_irq_shutdown(void *args)
57{
58 unsigned long flags;
59
60 local_irq_save(flags);
61 mask_rm9k_irq(args);
62 local_irq_restore(flags);
63}
64
65static void rm9k_perfcounter_irq_shutdown(struct irq_data *d)
66{
67 on_each_cpu(local_rm9k_perfcounter_irq_shutdown, d, 1);
68}
69
70static struct irq_chip rm9k_irq_controller = {
71 .name = "RM9000",
72 .irq_ack = mask_rm9k_irq,
73 .irq_mask = mask_rm9k_irq,
74 .irq_mask_ack = mask_rm9k_irq,
75 .irq_unmask = unmask_rm9k_irq,
76 .irq_eoi = unmask_rm9k_irq
77};
78
79static struct irq_chip rm9k_perfcounter_irq = {
80 .name = "RM9000",
81 .irq_startup = rm9k_perfcounter_irq_startup,
82 .irq_shutdown = rm9k_perfcounter_irq_shutdown,
83 .irq_ack = mask_rm9k_irq,
84 .irq_mask = mask_rm9k_irq,
85 .irq_mask_ack = mask_rm9k_irq,
86 .irq_unmask = unmask_rm9k_irq,
87};
88
89unsigned int rm9000_perfcount_irq;
90
91EXPORT_SYMBOL(rm9000_perfcount_irq);
92
93void __init rm9k_cpu_irq_init(void)
94{
95 int base = RM9K_CPU_IRQ_BASE;
96 int i;
97
98 clear_c0_intcontrol(0x0000f000); /* Mask all */
99
100 for (i = base; i < base + 4; i++)
101 irq_set_chip_and_handler(i, &rm9k_irq_controller,
102 handle_level_irq);
103
104 rm9000_perfcount_irq = base + 1;
105 irq_set_chip_and_handler(rm9000_perfcount_irq, &rm9k_perfcounter_irq,
106 handle_percpu_irq);
107}
diff --git a/arch/mips/kernel/kspd.c b/arch/mips/kernel/kspd.c
new file mode 100644
index 00000000000..29811f04339
--- /dev/null
+++ b/arch/mips/kernel/kspd.c
@@ -0,0 +1,423 @@
1/*
2 * Copyright (C) 2005 MIPS Technologies, Inc. All rights reserved.
3 *
4 * This program is free software; you can distribute it and/or modify it
5 * under the terms of the GNU General Public License (Version 2) as
6 * published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope it will be useful, but WITHOUT
9 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
11 * for more details.
12 *
13 * You should have received a copy of the GNU General Public License along
14 * with this program; if not, write to the Free Software Foundation, Inc.,
15 * 59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
16 *
17 */
18#include <linux/kernel.h>
19#include <linux/module.h>
20#include <linux/sched.h>
21#include <linux/unistd.h>
22#include <linux/file.h>
23#include <linux/fdtable.h>
24#include <linux/fs.h>
25#include <linux/syscalls.h>
26#include <linux/workqueue.h>
27#include <linux/errno.h>
28#include <linux/list.h>
29
30#include <asm/vpe.h>
31#include <asm/rtlx.h>
32#include <asm/kspd.h>
33
34static struct workqueue_struct *workqueue;
35static struct work_struct work;
36
37extern unsigned long cpu_khz;
38
39struct mtsp_syscall {
40 int cmd;
41 unsigned char abi;
42 unsigned char size;
43};
44
45struct mtsp_syscall_ret {
46 int retval;
47 int errno;
48};
49
50struct mtsp_syscall_generic {
51 int arg0;
52 int arg1;
53 int arg2;
54 int arg3;
55 int arg4;
56 int arg5;
57 int arg6;
58};
59
60static struct list_head kspd_notifylist;
61static int sp_stopping;
62
63/* these should match with those in the SDE kit */
64#define MTSP_SYSCALL_BASE 0
65#define MTSP_SYSCALL_EXIT (MTSP_SYSCALL_BASE + 0)
66#define MTSP_SYSCALL_OPEN (MTSP_SYSCALL_BASE + 1)
67#define MTSP_SYSCALL_READ (MTSP_SYSCALL_BASE + 2)
68#define MTSP_SYSCALL_WRITE (MTSP_SYSCALL_BASE + 3)
69#define MTSP_SYSCALL_CLOSE (MTSP_SYSCALL_BASE + 4)
70#define MTSP_SYSCALL_LSEEK32 (MTSP_SYSCALL_BASE + 5)
71#define MTSP_SYSCALL_ISATTY (MTSP_SYSCALL_BASE + 6)
72#define MTSP_SYSCALL_GETTIME (MTSP_SYSCALL_BASE + 7)
73#define MTSP_SYSCALL_PIPEFREQ (MTSP_SYSCALL_BASE + 8)
74#define MTSP_SYSCALL_GETTOD (MTSP_SYSCALL_BASE + 9)
75#define MTSP_SYSCALL_IOCTL (MTSP_SYSCALL_BASE + 10)
76
77#define MTSP_O_RDONLY 0x0000
78#define MTSP_O_WRONLY 0x0001
79#define MTSP_O_RDWR 0x0002
80#define MTSP_O_NONBLOCK 0x0004
81#define MTSP_O_APPEND 0x0008
82#define MTSP_O_SHLOCK 0x0010
83#define MTSP_O_EXLOCK 0x0020
84#define MTSP_O_ASYNC 0x0040
85/* XXX: check which of these is actually O_SYNC vs O_DSYNC */
86#define MTSP_O_FSYNC O_SYNC
87#define MTSP_O_NOFOLLOW 0x0100
88#define MTSP_O_SYNC 0x0080
89#define MTSP_O_CREAT 0x0200
90#define MTSP_O_TRUNC 0x0400
91#define MTSP_O_EXCL 0x0800
92#define MTSP_O_BINARY 0x8000
93
94extern int tclimit;
95
96struct apsp_table {
97 int sp;
98 int ap;
99};
100
101/* we might want to do the mode flags too */
102struct apsp_table open_flags_table[] = {
103 { MTSP_O_RDWR, O_RDWR },
104 { MTSP_O_WRONLY, O_WRONLY },
105 { MTSP_O_CREAT, O_CREAT },
106 { MTSP_O_TRUNC, O_TRUNC },
107 { MTSP_O_NONBLOCK, O_NONBLOCK },
108 { MTSP_O_APPEND, O_APPEND },
109 { MTSP_O_NOFOLLOW, O_NOFOLLOW }
110};
111
112struct apsp_table syscall_command_table[] = {
113 { MTSP_SYSCALL_OPEN, __NR_open },
114 { MTSP_SYSCALL_CLOSE, __NR_close },
115 { MTSP_SYSCALL_READ, __NR_read },
116 { MTSP_SYSCALL_WRITE, __NR_write },
117 { MTSP_SYSCALL_LSEEK32, __NR_lseek },
118 { MTSP_SYSCALL_IOCTL, __NR_ioctl }
119};
120
121static int sp_syscall(int num, int arg0, int arg1, int arg2, int arg3)
122{
123 register long int _num __asm__("$2") = num;
124 register long int _arg0 __asm__("$4") = arg0;
125 register long int _arg1 __asm__("$5") = arg1;
126 register long int _arg2 __asm__("$6") = arg2;
127 register long int _arg3 __asm__("$7") = arg3;
128
129 mm_segment_t old_fs;
130
131 old_fs = get_fs();
132 set_fs(KERNEL_DS);
133
134 __asm__ __volatile__ (
135 " syscall \n"
136 : "=r" (_num), "=r" (_arg3)
137 : "r" (_num), "r" (_arg0), "r" (_arg1), "r" (_arg2), "r" (_arg3));
138
139 set_fs(old_fs);
140
141 /* $a3 is error flag */
142 if (_arg3)
143 return -_num;
144
145 return _num;
146}
147
148static int translate_syscall_command(int cmd)
149{
150 int i;
151 int ret = -1;
152
153 for (i = 0; i < ARRAY_SIZE(syscall_command_table); i++) {
154 if ((cmd == syscall_command_table[i].sp))
155 return syscall_command_table[i].ap;
156 }
157
158 return ret;
159}
160
161static unsigned int translate_open_flags(int flags)
162{
163 int i;
164 unsigned int ret = 0;
165
166 for (i = 0; i < ARRAY_SIZE(open_flags_table); i++) {
167 if( (flags & open_flags_table[i].sp) ) {
168 ret |= open_flags_table[i].ap;
169 }
170 }
171
172 return ret;
173}
174
175
176static int sp_setfsuidgid(uid_t uid, gid_t gid)
177{
178 struct cred *new;
179
180 new = prepare_creds();
181 if (!new)
182 return -ENOMEM;
183
184 new->fsuid = uid;
185 new->fsgid = gid;
186
187 commit_creds(new);
188
189 return 0;
190}
191
192/*
193 * Expects a request to be on the sysio channel. Reads it. Decides whether
194 * its a linux syscall and runs it, or whatever. Puts the return code back
195 * into the request and sends the whole thing back.
196 */
197void sp_work_handle_request(void)
198{
199 struct mtsp_syscall sc;
200 struct mtsp_syscall_generic generic;
201 struct mtsp_syscall_ret ret;
202 struct kspd_notifications *n;
203 unsigned long written;
204 mm_segment_t old_fs;
205 struct timeval tv;
206 struct timezone tz;
207 int err, cmd;
208
209 char *vcwd;
210 int size;
211
212 ret.retval = -1;
213
214 old_fs = get_fs();
215 set_fs(KERNEL_DS);
216
217 if (!rtlx_read(RTLX_CHANNEL_SYSIO, &sc, sizeof(struct mtsp_syscall))) {
218 set_fs(old_fs);
219 printk(KERN_ERR "Expected request but nothing to read\n");
220 return;
221 }
222
223 size = sc.size;
224
225 if (size) {
226 if (!rtlx_read(RTLX_CHANNEL_SYSIO, &generic, size)) {
227 set_fs(old_fs);
228 printk(KERN_ERR "Expected request but nothing to read\n");
229 return;
230 }
231 }
232
233 /* Run the syscall at the privilege of the user who loaded the
234 SP program */
235
236 if (vpe_getuid(tclimit)) {
237 err = sp_setfsuidgid(vpe_getuid(tclimit), vpe_getgid(tclimit));
238 if (!err)
239 pr_err("Change of creds failed\n");
240 }
241
242 switch (sc.cmd) {
243 /* needs the flags argument translating from SDE kit to
244 linux */
245 case MTSP_SYSCALL_PIPEFREQ:
246 ret.retval = cpu_khz * 1000;
247 ret.errno = 0;
248 break;
249
250 case MTSP_SYSCALL_GETTOD:
251 memset(&tz, 0, sizeof(tz));
252 if ((ret.retval = sp_syscall(__NR_gettimeofday, (int)&tv,
253 (int)&tz, 0, 0)) == 0)
254 ret.retval = tv.tv_sec;
255 break;
256
257 case MTSP_SYSCALL_EXIT:
258 list_for_each_entry(n, &kspd_notifylist, list)
259 n->kspd_sp_exit(tclimit);
260 sp_stopping = 1;
261
262 printk(KERN_DEBUG "KSPD got exit syscall from SP exitcode %d\n",
263 generic.arg0);
264 break;
265
266 case MTSP_SYSCALL_OPEN:
267 generic.arg1 = translate_open_flags(generic.arg1);
268
269 vcwd = vpe_getcwd(tclimit);
270
271 /* change to cwd of the process that loaded the SP program */
272 old_fs = get_fs();
273 set_fs(KERNEL_DS);
274 sys_chdir(vcwd);
275 set_fs(old_fs);
276
277 sc.cmd = __NR_open;
278
279 /* fall through */
280
281 default:
282 if ((sc.cmd >= __NR_Linux) &&
283 (sc.cmd <= (__NR_Linux + __NR_Linux_syscalls)) )
284 cmd = sc.cmd;
285 else
286 cmd = translate_syscall_command(sc.cmd);
287
288 if (cmd >= 0) {
289 ret.retval = sp_syscall(cmd, generic.arg0, generic.arg1,
290 generic.arg2, generic.arg3);
291 } else
292 printk(KERN_WARNING
293 "KSPD: Unknown SP syscall number %d\n", sc.cmd);
294 break;
295 } /* switch */
296
297 if (vpe_getuid(tclimit)) {
298 err = sp_setfsuidgid(0, 0);
299 if (!err)
300 pr_err("restoring old creds failed\n");
301 }
302
303 old_fs = get_fs();
304 set_fs(KERNEL_DS);
305 written = rtlx_write(RTLX_CHANNEL_SYSIO, &ret, sizeof(ret));
306 set_fs(old_fs);
307 if (written < sizeof(ret))
308 printk("KSPD: sp_work_handle_request failed to send to SP\n");
309}
310
311static void sp_cleanup(void)
312{
313 struct files_struct *files = current->files;
314 int i, j;
315 struct fdtable *fdt;
316
317 j = 0;
318
319 /*
320 * It is safe to dereference the fd table without RCU or
321 * ->file_lock
322 */
323 fdt = files_fdtable(files);
324 for (;;) {
325 unsigned long set;
326 i = j * __NFDBITS;
327 if (i >= fdt->max_fds)
328 break;
329 set = fdt->open_fds->fds_bits[j++];
330 while (set) {
331 if (set & 1) {
332 struct file * file = xchg(&fdt->fd[i], NULL);
333 if (file)
334 filp_close(file, files);
335 }
336 i++;
337 set >>= 1;
338 }
339 }
340
341 /* Put daemon cwd back to root to avoid umount problems */
342 sys_chdir("/");
343}
344
345static int channel_open;
346
347/* the work handler */
348static void sp_work(struct work_struct *unused)
349{
350 if (!channel_open) {
351 if( rtlx_open(RTLX_CHANNEL_SYSIO, 1) != 0) {
352 printk("KSPD: unable to open sp channel\n");
353 sp_stopping = 1;
354 } else {
355 channel_open++;
356 printk(KERN_DEBUG "KSPD: SP channel opened\n");
357 }
358 } else {
359 /* wait for some data, allow it to sleep */
360 rtlx_read_poll(RTLX_CHANNEL_SYSIO, 1);
361
362 /* Check we haven't been woken because we are stopping */
363 if (!sp_stopping)
364 sp_work_handle_request();
365 }
366
367 if (!sp_stopping)
368 queue_work(workqueue, &work);
369 else
370 sp_cleanup();
371}
372
373static void startwork(int vpe)
374{
375 sp_stopping = channel_open = 0;
376
377 if (workqueue == NULL) {
378 if ((workqueue = create_singlethread_workqueue("kspd")) == NULL) {
379 printk(KERN_ERR "unable to start kspd\n");
380 return;
381 }
382
383 INIT_WORK(&work, sp_work);
384 }
385
386 queue_work(workqueue, &work);
387}
388
389static void stopwork(int vpe)
390{
391 sp_stopping = 1;
392
393 printk(KERN_DEBUG "KSPD: SP stopping\n");
394}
395
396void kspd_notify(struct kspd_notifications *notify)
397{
398 list_add(&notify->list, &kspd_notifylist);
399}
400
401static struct vpe_notifications notify;
402static int kspd_module_init(void)
403{
404 INIT_LIST_HEAD(&kspd_notifylist);
405
406 notify.start = startwork;
407 notify.stop = stopwork;
408 vpe_notify(tclimit, &notify);
409
410 return 0;
411}
412
413static void kspd_module_exit(void)
414{
415
416}
417
418module_init(kspd_module_init);
419module_exit(kspd_module_exit);
420
421MODULE_DESCRIPTION("MIPS KSPD");
422MODULE_AUTHOR("Elizabeth Oldham, MIPS Technologies, Inc.");
423MODULE_LICENSE("GPL");