Blame view

arch/mips/kernel/rtlx.c 12.2 KB
e01402b11   Ralf Baechle   More AP / SP bits...
1
2
  /*
   * Copyright (C) 2005 MIPS Technologies, Inc.  All rights reserved.
a84c96e20   Ralf Baechle   [MIPS] RTLX compi...
3
   * Copyright (C) 2005, 06 Ralf Baechle (ralf@linux-mips.org)
e01402b11   Ralf Baechle   More AP / SP bits...
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
   *
   *  This program is free software; you can distribute it and/or modify it
   *  under the terms of the GNU General Public License (Version 2) as
   *  published by the Free Software Foundation.
   *
   *  This program is distributed in the hope it will be useful, but WITHOUT
   *  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   *  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   *  for more details.
   *
   *  You should have received a copy of the GNU General Public License along
   *  with this program; if not, write to the Free Software Foundation, Inc.,
   *  59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
   *
   */
bb3d7c7ff   Ralf Baechle   [MIPS] RTLX: Spri...
19
  #include <linux/device.h>
e01402b11   Ralf Baechle   More AP / SP bits...
20
  #include <linux/kernel.h>
e01402b11   Ralf Baechle   More AP / SP bits...
21
22
  #include <linux/fs.h>
  #include <linux/init.h>
2600990e6   Ralf Baechle   [MIPS] kpsd and o...
23
  #include <asm/uaccess.h>
2600990e6   Ralf Baechle   [MIPS] kpsd and o...
24
25
26
27
28
29
  #include <linux/list.h>
  #include <linux/vmalloc.h>
  #include <linux/elf.h>
  #include <linux/seq_file.h>
  #include <linux/syscalls.h>
  #include <linux/moduleloader.h>
a84c96e20   Ralf Baechle   [MIPS] RTLX compi...
30
  #include <linux/interrupt.h>
e01402b11   Ralf Baechle   More AP / SP bits...
31
32
33
34
  #include <linux/poll.h>
  #include <linux/sched.h>
  #include <linux/wait.h>
  #include <asm/mipsmtregs.h>
bb3d7c7ff   Ralf Baechle   [MIPS] RTLX: Spri...
35
  #include <asm/mips_mt.h>
2600990e6   Ralf Baechle   [MIPS] kpsd and o...
36
  #include <asm/cacheflush.h>
60063497a   Arun Sharma   atomic: use <linu...
37
  #include <linux/atomic.h>
e01402b11   Ralf Baechle   More AP / SP bits...
38
39
  #include <asm/cpu.h>
  #include <asm/processor.h>
2600990e6   Ralf Baechle   [MIPS] kpsd and o...
40
41
  #include <asm/system.h>
  #include <asm/vpe.h>
e01402b11   Ralf Baechle   More AP / SP bits...
42
  #include <asm/rtlx.h>
afc4841d8   Ralf Baechle   Turn rtlx upside ...
43
  static struct rtlx_info *rtlx;
e01402b11   Ralf Baechle   More AP / SP bits...
44
45
  static int major;
  static char module_name[] = "rtlx";
e01402b11   Ralf Baechle   More AP / SP bits...
46
47
48
49
  
  static struct chan_waitqueues {
  	wait_queue_head_t rt_queue;
  	wait_queue_head_t lx_queue;
c4c4018b0   Ralf Baechle   [MIPS] RTLX, VPE:...
50
  	atomic_t in_open;
bc4809e93   Ralf Baechle   [MIPS] RTLX: Prot...
51
  	struct mutex mutex;
e01402b11   Ralf Baechle   More AP / SP bits...
52
  } channel_wqs[RTLX_CHANNELS];
2600990e6   Ralf Baechle   [MIPS] kpsd and o...
53
  static struct vpe_notifications notify;
982f6ffee   Ralf Baechle   MIPS: Remove usel...
54
  static int sp_stopping;
2600990e6   Ralf Baechle   [MIPS] kpsd and o...
55

e01402b11   Ralf Baechle   More AP / SP bits...
56
  extern void *vpe_get_shared(int index);
937a80157   Ralf Baechle   [MIPS] Complete f...
57
  static void rtlx_dispatch(void)
e01402b11   Ralf Baechle   More AP / SP bits...
58
  {
97dcb82de   Atsushi Nemoto   [MIPS] Define MIP...
59
  	do_IRQ(MIPS_CPU_IRQ_BASE + MIPS_CPU_RTLX_IRQ);
e01402b11   Ralf Baechle   More AP / SP bits...
60
  }
2600990e6   Ralf Baechle   [MIPS] kpsd and o...
61
62
63
64
  
  /* Interrupt handler may be called before rtlx_init has otherwise had
     a chance to run.
  */
937a80157   Ralf Baechle   [MIPS] Complete f...
65
  static irqreturn_t rtlx_interrupt(int irq, void *dev_id)
e01402b11   Ralf Baechle   More AP / SP bits...
66
  {
1bbfc20d0   Ralf Baechle   MIPS: VPE: Get ri...
67
68
  	unsigned int vpeflags;
  	unsigned long flags;
e01402b11   Ralf Baechle   More AP / SP bits...
69
  	int i;
1928cc84a   Kevin D. Kissell   [MIPS] MT: Functi...
70
71
72
73
74
75
76
77
  
  	/* Ought not to be strictly necessary for SMTC builds */
  	local_irq_save(flags);
  	vpeflags = dvpe();
  	set_c0_status(0x100 << MIPS_CPU_RTLX_IRQ);
  	irq_enable_hazard();
  	evpe(vpeflags);
  	local_irq_restore(flags);
e01402b11   Ralf Baechle   More AP / SP bits...
78
79
  
  	for (i = 0; i < RTLX_CHANNELS; i++) {
2600990e6   Ralf Baechle   [MIPS] kpsd and o...
80
81
  			wake_up(&channel_wqs[i].lx_queue);
  			wake_up(&channel_wqs[i].rt_queue);
e01402b11   Ralf Baechle   More AP / SP bits...
82
  	}
afc4841d8   Ralf Baechle   Turn rtlx upside ...
83
  	return IRQ_HANDLED;
e01402b11   Ralf Baechle   More AP / SP bits...
84
  }
f5dbeaf5e   David Rientjes   [MIPS] Replace __...
85
  static void __used dump_rtlx(void)
e01402b11   Ralf Baechle   More AP / SP bits...
86
87
  {
  	int i;
2600990e6   Ralf Baechle   [MIPS] kpsd and o...
88
89
  	printk("id 0x%lx state %d
  ", rtlx->id, rtlx->state);
e01402b11   Ralf Baechle   More AP / SP bits...
90

e01402b11   Ralf Baechle   More AP / SP bits...
91
  	for (i = 0; i < RTLX_CHANNELS; i++) {
2600990e6   Ralf Baechle   [MIPS] kpsd and o...
92
  		struct rtlx_channel *chan = &rtlx->channel[i];
e01402b11   Ralf Baechle   More AP / SP bits...
93

2600990e6   Ralf Baechle   [MIPS] kpsd and o...
94
95
96
  		printk(" rt_state %d lx_state %d buffer_size %d
  ",
  		       chan->rt_state, chan->lx_state, chan->buffer_size);
e01402b11   Ralf Baechle   More AP / SP bits...
97

2600990e6   Ralf Baechle   [MIPS] kpsd and o...
98
99
100
  		printk(" rt_read %d rt_write %d
  ",
  		       chan->rt_read, chan->rt_write);
e01402b11   Ralf Baechle   More AP / SP bits...
101

2600990e6   Ralf Baechle   [MIPS] kpsd and o...
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
  		printk(" lx_read %d lx_write %d
  ",
  		       chan->lx_read, chan->lx_write);
  
  		printk(" rt_buffer <%s>
  ", chan->rt_buffer);
  		printk(" lx_buffer <%s>
  ", chan->lx_buffer);
  	}
  }
  
  /* call when we have the address of the shared structure from the SP side. */
  static int rtlx_init(struct rtlx_info *rtlxi)
  {
  	if (rtlxi->id != RTLX_ID) {
1928cc84a   Kevin D. Kissell   [MIPS] MT: Functi...
117
118
119
  		printk(KERN_ERR "no valid RTLX id at 0x%p 0x%lx
  ",
  			rtlxi, rtlxi->id);
2600990e6   Ralf Baechle   [MIPS] kpsd and o...
120
121
  		return -ENOEXEC;
  	}
e01402b11   Ralf Baechle   More AP / SP bits...
122
123
  
  	rtlx = rtlxi;
afc4841d8   Ralf Baechle   Turn rtlx upside ...
124
125
  
  	return 0;
e01402b11   Ralf Baechle   More AP / SP bits...
126
  }
2600990e6   Ralf Baechle   [MIPS] kpsd and o...
127
128
  /* notifications */
  static void starting(int vpe)
e01402b11   Ralf Baechle   More AP / SP bits...
129
  {
2600990e6   Ralf Baechle   [MIPS] kpsd and o...
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
  	int i;
  	sp_stopping = 0;
  
  	/* force a reload of rtlx */
  	rtlx=NULL;
  
  	/* wake up any sleeping rtlx_open's */
  	for (i = 0; i < RTLX_CHANNELS; i++)
  		wake_up_interruptible(&channel_wqs[i].lx_queue);
  }
  
  static void stopping(int vpe)
  {
  	int i;
  
  	sp_stopping = 1;
  	for (i = 0; i < RTLX_CHANNELS; i++)
  		wake_up_interruptible(&channel_wqs[i].lx_queue);
  }
  
  
  int rtlx_open(int index, int can_sleep)
  {
9e3468202   Ralf Baechle   [MIPS] RTLX: Don'...
153
  	struct rtlx_info **p;
67e2cccec   Ralf Baechle   [MIPS] RTLX: Hand...
154
  	struct rtlx_channel *chan;
c4c4018b0   Ralf Baechle   [MIPS] RTLX, VPE:...
155
  	enum rtlx_state state;
67e2cccec   Ralf Baechle   [MIPS] RTLX: Hand...
156
  	int ret = 0;
e01402b11   Ralf Baechle   More AP / SP bits...
157

2600990e6   Ralf Baechle   [MIPS] kpsd and o...
158
159
160
161
162
  	if (index >= RTLX_CHANNELS) {
  		printk(KERN_DEBUG "rtlx_open index out of range
  ");
  		return -ENOSYS;
  	}
c4c4018b0   Ralf Baechle   [MIPS] RTLX, VPE:...
163
164
165
166
167
168
  	if (atomic_inc_return(&channel_wqs[index].in_open) > 1) {
  		printk(KERN_DEBUG "rtlx_open channel %d already opened
  ",
  		       index);
  		ret = -EBUSY;
  		goto out_fail;
2600990e6   Ralf Baechle   [MIPS] kpsd and o...
169
  	}
e01402b11   Ralf Baechle   More AP / SP bits...
170
  	if (rtlx == NULL) {
07cc0c9e6   Ralf Baechle   [MIPS] MT: Enable...
171
  		if( (p = vpe_get_shared(tclimit)) == NULL) {
1928cc84a   Kevin D. Kissell   [MIPS] MT: Functi...
172
173
174
175
  		    if (can_sleep) {
  			__wait_event_interruptible(channel_wqs[index].lx_queue,
  				(p = vpe_get_shared(tclimit)), ret);
  			if (ret)
67e2cccec   Ralf Baechle   [MIPS] RTLX: Hand...
176
  				goto out_fail;
1928cc84a   Kevin D. Kissell   [MIPS] MT: Functi...
177
178
179
180
181
182
183
  		    } else {
  			printk(KERN_DEBUG "No SP program loaded, and device "
  					"opened with O_NONBLOCK
  ");
  			ret = -ENOSYS;
  			goto out_fail;
  		    }
e01402b11   Ralf Baechle   More AP / SP bits...
184
  		}
9e3468202   Ralf Baechle   [MIPS] RTLX: Don'...
185
  		smp_rmb();
e01402b11   Ralf Baechle   More AP / SP bits...
186
  		if (*p == NULL) {
2600990e6   Ralf Baechle   [MIPS] kpsd and o...
187
  			if (can_sleep) {
9e3468202   Ralf Baechle   [MIPS] RTLX: Don'...
188
189
190
  				DEFINE_WAIT(wait);
  
  				for (;;) {
1928cc84a   Kevin D. Kissell   [MIPS] MT: Functi...
191
192
193
  					prepare_to_wait(
  						&channel_wqs[index].lx_queue,
  						&wait, TASK_INTERRUPTIBLE);
9e3468202   Ralf Baechle   [MIPS] RTLX: Don'...
194
195
196
197
198
199
200
201
  					smp_rmb();
  					if (*p != NULL)
  						break;
  					if (!signal_pending(current)) {
  						schedule();
  						continue;
  					}
  					ret = -ERESTARTSYS;
67e2cccec   Ralf Baechle   [MIPS] RTLX: Hand...
202
  					goto out_fail;
9e3468202   Ralf Baechle   [MIPS] RTLX: Don'...
203
204
  				}
  				finish_wait(&channel_wqs[index].lx_queue, &wait);
67e2cccec   Ralf Baechle   [MIPS] RTLX: Hand...
205
  			} else {
1928cc84a   Kevin D. Kissell   [MIPS] MT: Functi...
206
  				pr_err(" *vpe_get_shared is NULL. "
2600990e6   Ralf Baechle   [MIPS] kpsd and o...
207
208
  				       "Has an SP program been loaded?
  ");
67e2cccec   Ralf Baechle   [MIPS] RTLX: Hand...
209
210
  				ret = -ENOSYS;
  				goto out_fail;
2600990e6   Ralf Baechle   [MIPS] kpsd and o...
211
212
213
214
  			}
  		}
  
  		if ((unsigned int)*p < KSEG0) {
1928cc84a   Kevin D. Kissell   [MIPS] MT: Functi...
215
216
217
218
  			printk(KERN_WARNING "vpe_get_shared returned an "
  			       "invalid pointer maybe an error code %d
  ",
  			       (int)*p);
67e2cccec   Ralf Baechle   [MIPS] RTLX: Hand...
219
220
  			ret = -ENOSYS;
  			goto out_fail;
e01402b11   Ralf Baechle   More AP / SP bits...
221
  		}
c4c4018b0   Ralf Baechle   [MIPS] RTLX, VPE:...
222
223
  		if ((ret = rtlx_init(*p)) < 0)
  			goto out_ret;
e01402b11   Ralf Baechle   More AP / SP bits...
224
  	}
2600990e6   Ralf Baechle   [MIPS] kpsd and o...
225
  	chan = &rtlx->channel[index];
e01402b11   Ralf Baechle   More AP / SP bits...
226

c4c4018b0   Ralf Baechle   [MIPS] RTLX, VPE:...
227
228
229
230
231
  	state = xchg(&chan->lx_state, RTLX_STATE_OPENED);
  	if (state == RTLX_STATE_OPENED) {
  		ret = -EBUSY;
  		goto out_fail;
  	}
67e2cccec   Ralf Baechle   [MIPS] RTLX: Hand...
232
233
  
  out_fail:
c4c4018b0   Ralf Baechle   [MIPS] RTLX, VPE:...
234
235
236
  	smp_mb();
  	atomic_dec(&channel_wqs[index].in_open);
  	smp_mb();
67e2cccec   Ralf Baechle   [MIPS] RTLX: Hand...
237

c4c4018b0   Ralf Baechle   [MIPS] RTLX, VPE:...
238
  out_ret:
67e2cccec   Ralf Baechle   [MIPS] RTLX: Hand...
239
  	return ret;
e01402b11   Ralf Baechle   More AP / SP bits...
240
  }
2600990e6   Ralf Baechle   [MIPS] kpsd and o...
241
  int rtlx_release(int index)
e01402b11   Ralf Baechle   More AP / SP bits...
242
  {
1928cc84a   Kevin D. Kissell   [MIPS] MT: Functi...
243
244
245
246
247
  	if (rtlx == NULL) {
  		pr_err("rtlx_release() with null rtlx
  ");
  		return 0;
  	}
2600990e6   Ralf Baechle   [MIPS] kpsd and o...
248
  	rtlx->channel[index].lx_state = RTLX_STATE_UNUSED;
afc4841d8   Ralf Baechle   Turn rtlx upside ...
249
  	return 0;
e01402b11   Ralf Baechle   More AP / SP bits...
250
  }
2600990e6   Ralf Baechle   [MIPS] kpsd and o...
251
  unsigned int rtlx_read_poll(int index, int can_sleep)
e01402b11   Ralf Baechle   More AP / SP bits...
252
  {
2600990e6   Ralf Baechle   [MIPS] kpsd and o...
253
   	struct rtlx_channel *chan;
e01402b11   Ralf Baechle   More AP / SP bits...
254

2600990e6   Ralf Baechle   [MIPS] kpsd and o...
255
256
   	if (rtlx == NULL)
   		return 0;
e01402b11   Ralf Baechle   More AP / SP bits...
257

2600990e6   Ralf Baechle   [MIPS] kpsd and o...
258
   	chan = &rtlx->channel[index];
e01402b11   Ralf Baechle   More AP / SP bits...
259
260
  
  	/* data available to read? */
2600990e6   Ralf Baechle   [MIPS] kpsd and o...
261
262
  	if (chan->lx_read == chan->lx_write) {
  		if (can_sleep) {
67e2cccec   Ralf Baechle   [MIPS] RTLX: Hand...
263
  			int ret = 0;
e01402b11   Ralf Baechle   More AP / SP bits...
264

67e2cccec   Ralf Baechle   [MIPS] RTLX: Hand...
265
  			__wait_event_interruptible(channel_wqs[index].lx_queue,
1928cc84a   Kevin D. Kissell   [MIPS] MT: Functi...
266
267
  				(chan->lx_read != chan->lx_write) ||
  				sp_stopping, ret);
67e2cccec   Ralf Baechle   [MIPS] RTLX: Hand...
268
269
  			if (ret)
  				return ret;
e01402b11   Ralf Baechle   More AP / SP bits...
270

67e2cccec   Ralf Baechle   [MIPS] RTLX: Hand...
271
272
273
  			if (sp_stopping)
  				return 0;
  		} else
2600990e6   Ralf Baechle   [MIPS] kpsd and o...
274
275
276
277
278
  			return 0;
  	}
  
  	return (chan->lx_write + chan->buffer_size - chan->lx_read)
  	       % chan->buffer_size;
e01402b11   Ralf Baechle   More AP / SP bits...
279
  }
2600990e6   Ralf Baechle   [MIPS] kpsd and o...
280
  static inline int write_spacefree(int read, int write, int size)
e01402b11   Ralf Baechle   More AP / SP bits...
281
  {
2600990e6   Ralf Baechle   [MIPS] kpsd and o...
282
283
284
285
286
287
288
  	if (read == write) {
  		/*
  		 * Never fill the buffer completely, so indexes are always
  		 * equal if empty and only empty, or !equal if data available
  		 */
  		return size - 1;
  	}
e01402b11   Ralf Baechle   More AP / SP bits...
289

2600990e6   Ralf Baechle   [MIPS] kpsd and o...
290
291
  	return ((read + size - write) % size) - 1;
  }
e01402b11   Ralf Baechle   More AP / SP bits...
292

2600990e6   Ralf Baechle   [MIPS] kpsd and o...
293
294
295
  unsigned int rtlx_write_poll(int index)
  {
  	struct rtlx_channel *chan = &rtlx->channel[index];
1928cc84a   Kevin D. Kissell   [MIPS] MT: Functi...
296
297
298
  
  	return write_spacefree(chan->rt_read, chan->rt_write,
  				chan->buffer_size);
2600990e6   Ralf Baechle   [MIPS] kpsd and o...
299
  }
e01402b11   Ralf Baechle   More AP / SP bits...
300

7f5a7716d   Ralf Baechle   [MIPS] Fix AP/SP ...
301
  ssize_t rtlx_read(int index, void __user *buff, size_t count)
2600990e6   Ralf Baechle   [MIPS] kpsd and o...
302
  {
61dcc6f4d   Ralf Baechle   [MIPS] RTLX: Hard...
303
  	size_t lx_write, fl = 0L;
2600990e6   Ralf Baechle   [MIPS] kpsd and o...
304
  	struct rtlx_channel *lx;
46230aa6e   Ralf Baechle   [MIPS] RTLX: Hand...
305
  	unsigned long failed;
e01402b11   Ralf Baechle   More AP / SP bits...
306

2600990e6   Ralf Baechle   [MIPS] kpsd and o...
307
308
309
310
  	if (rtlx == NULL)
  		return -ENOSYS;
  
  	lx = &rtlx->channel[index];
e01402b11   Ralf Baechle   More AP / SP bits...
311

bc4809e93   Ralf Baechle   [MIPS] RTLX: Prot...
312
  	mutex_lock(&channel_wqs[index].mutex);
61dcc6f4d   Ralf Baechle   [MIPS] RTLX: Hard...
313
314
  	smp_rmb();
  	lx_write = lx->lx_write;
e01402b11   Ralf Baechle   More AP / SP bits...
315
  	/* find out how much in total */
afc4841d8   Ralf Baechle   Turn rtlx upside ...
316
  	count = min(count,
61dcc6f4d   Ralf Baechle   [MIPS] RTLX: Hard...
317
  		     (size_t)(lx_write + lx->buffer_size - lx->lx_read)
2600990e6   Ralf Baechle   [MIPS] kpsd and o...
318
  		     % lx->buffer_size);
e01402b11   Ralf Baechle   More AP / SP bits...
319
320
  
  	/* then how much from the read pointer onwards */
61dcc6f4d   Ralf Baechle   [MIPS] RTLX: Hard...
321
  	fl = min(count, (size_t)lx->buffer_size - lx->lx_read);
e01402b11   Ralf Baechle   More AP / SP bits...
322

46230aa6e   Ralf Baechle   [MIPS] RTLX: Hand...
323
324
325
  	failed = copy_to_user(buff, lx->lx_buffer + lx->lx_read, fl);
  	if (failed)
  		goto out;
e01402b11   Ralf Baechle   More AP / SP bits...
326
327
  
  	/* and if there is anything left at the beginning of the buffer */
61dcc6f4d   Ralf Baechle   [MIPS] RTLX: Hard...
328
  	if (count - fl)
46230aa6e   Ralf Baechle   [MIPS] RTLX: Hand...
329
330
331
332
  		failed = copy_to_user(buff + fl, lx->lx_buffer, count - fl);
  
  out:
  	count -= failed;
e01402b11   Ralf Baechle   More AP / SP bits...
333

61dcc6f4d   Ralf Baechle   [MIPS] RTLX: Hard...
334
335
336
  	smp_wmb();
  	lx->lx_read = (lx->lx_read + count) % lx->buffer_size;
  	smp_wmb();
bc4809e93   Ralf Baechle   [MIPS] RTLX: Prot...
337
  	mutex_unlock(&channel_wqs[index].mutex);
e01402b11   Ralf Baechle   More AP / SP bits...
338

afc4841d8   Ralf Baechle   Turn rtlx upside ...
339
  	return count;
e01402b11   Ralf Baechle   More AP / SP bits...
340
  }
7f5a7716d   Ralf Baechle   [MIPS] Fix AP/SP ...
341
  ssize_t rtlx_write(int index, const void __user *buffer, size_t count)
2600990e6   Ralf Baechle   [MIPS] kpsd and o...
342
343
  {
  	struct rtlx_channel *rt;
7f5a7716d   Ralf Baechle   [MIPS] Fix AP/SP ...
344
  	unsigned long failed;
61dcc6f4d   Ralf Baechle   [MIPS] RTLX: Hard...
345
  	size_t rt_read;
2600990e6   Ralf Baechle   [MIPS] kpsd and o...
346
347
348
349
350
351
  	size_t fl;
  
  	if (rtlx == NULL)
  		return(-ENOSYS);
  
  	rt = &rtlx->channel[index];
bc4809e93   Ralf Baechle   [MIPS] RTLX: Prot...
352
  	mutex_lock(&channel_wqs[index].mutex);
61dcc6f4d   Ralf Baechle   [MIPS] RTLX: Hard...
353
354
  	smp_rmb();
  	rt_read = rt->rt_read;
2600990e6   Ralf Baechle   [MIPS] kpsd and o...
355
  	/* total number of bytes to copy */
1928cc84a   Kevin D. Kissell   [MIPS] MT: Functi...
356
357
  	count = min(count, (size_t)write_spacefree(rt_read, rt->rt_write,
  							rt->buffer_size));
2600990e6   Ralf Baechle   [MIPS] kpsd and o...
358
359
360
  
  	/* first bit from write pointer to the end of the buffer, or count */
  	fl = min(count, (size_t) rt->buffer_size - rt->rt_write);
46230aa6e   Ralf Baechle   [MIPS] RTLX: Hand...
361
362
363
  	failed = copy_from_user(rt->rt_buffer + rt->rt_write, buffer, fl);
  	if (failed)
  		goto out;
2600990e6   Ralf Baechle   [MIPS] kpsd and o...
364
365
  
  	/* if there's any left copy to the beginning of the buffer */
46230aa6e   Ralf Baechle   [MIPS] RTLX: Hand...
366
367
368
369
370
  	if (count - fl) {
  		failed = copy_from_user(rt->rt_buffer, buffer + fl, count - fl);
  	}
  
  out:
7f5a7716d   Ralf Baechle   [MIPS] Fix AP/SP ...
371
  	count -= failed;
2600990e6   Ralf Baechle   [MIPS] kpsd and o...
372

61dcc6f4d   Ralf Baechle   [MIPS] RTLX: Hard...
373
374
375
  	smp_wmb();
  	rt->rt_write = (rt->rt_write + count) % rt->buffer_size;
  	smp_wmb();
bc4809e93   Ralf Baechle   [MIPS] RTLX: Prot...
376
  	mutex_unlock(&channel_wqs[index].mutex);
2600990e6   Ralf Baechle   [MIPS] kpsd and o...
377

61dcc6f4d   Ralf Baechle   [MIPS] RTLX: Hard...
378
  	return count;
2600990e6   Ralf Baechle   [MIPS] kpsd and o...
379
380
381
382
383
  }
  
  
  static int file_open(struct inode *inode, struct file *filp)
  {
1bbfc20d0   Ralf Baechle   MIPS: VPE: Get ri...
384
  	return rtlx_open(iminor(inode), (filp->f_flags & O_NONBLOCK) ? 0 : 1);
2600990e6   Ralf Baechle   [MIPS] kpsd and o...
385
386
387
388
  }
  
  static int file_release(struct inode *inode, struct file *filp)
  {
1bbfc20d0   Ralf Baechle   MIPS: VPE: Get ri...
389
  	return rtlx_release(iminor(inode));
2600990e6   Ralf Baechle   [MIPS] kpsd and o...
390
391
392
393
394
395
  }
  
  static unsigned int file_poll(struct file *file, poll_table * wait)
  {
  	int minor;
  	unsigned int mask = 0;
1b04fe9a8   Josef Sipek   [PATCH] struct pa...
396
  	minor = iminor(file->f_path.dentry->d_inode);
2600990e6   Ralf Baechle   [MIPS] kpsd and o...
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
  
  	poll_wait(file, &channel_wqs[minor].rt_queue, wait);
  	poll_wait(file, &channel_wqs[minor].lx_queue, wait);
  
  	if (rtlx == NULL)
  		return 0;
  
  	/* data available to read? */
  	if (rtlx_read_poll(minor, 0))
  		mask |= POLLIN | POLLRDNORM;
  
  	/* space to write */
  	if (rtlx_write_poll(minor))
  		mask |= POLLOUT | POLLWRNORM;
  
  	return mask;
  }
  
  static ssize_t file_read(struct file *file, char __user * buffer, size_t count,
  			 loff_t * ppos)
  {
1b04fe9a8   Josef Sipek   [PATCH] struct pa...
418
  	int minor = iminor(file->f_path.dentry->d_inode);
2600990e6   Ralf Baechle   [MIPS] kpsd and o...
419
420
421
422
423
  
  	/* data available? */
  	if (!rtlx_read_poll(minor, (file->f_flags & O_NONBLOCK) ? 0 : 1)) {
  		return 0;	// -EAGAIN makes cat whinge
  	}
46230aa6e   Ralf Baechle   [MIPS] RTLX: Hand...
424
  	return rtlx_read(minor, buffer, count);
2600990e6   Ralf Baechle   [MIPS] kpsd and o...
425
426
427
  }
  
  static ssize_t file_write(struct file *file, const char __user * buffer,
e01402b11   Ralf Baechle   More AP / SP bits...
428
429
430
431
  			  size_t count, loff_t * ppos)
  {
  	int minor;
  	struct rtlx_channel *rt;
e01402b11   Ralf Baechle   More AP / SP bits...
432

1b04fe9a8   Josef Sipek   [PATCH] struct pa...
433
  	minor = iminor(file->f_path.dentry->d_inode);
e01402b11   Ralf Baechle   More AP / SP bits...
434
435
436
  	rt = &rtlx->channel[minor];
  
  	/* any space left... */
2600990e6   Ralf Baechle   [MIPS] kpsd and o...
437
  	if (!rtlx_write_poll(minor)) {
67e2cccec   Ralf Baechle   [MIPS] RTLX: Hand...
438
  		int ret = 0;
e01402b11   Ralf Baechle   More AP / SP bits...
439
440
  
  		if (file->f_flags & O_NONBLOCK)
afc4841d8   Ralf Baechle   Turn rtlx upside ...
441
  			return -EAGAIN;
e01402b11   Ralf Baechle   More AP / SP bits...
442

67e2cccec   Ralf Baechle   [MIPS] RTLX: Hand...
443
444
445
446
447
  		__wait_event_interruptible(channel_wqs[minor].rt_queue,
  		                           rtlx_write_poll(minor),
  		                           ret);
  		if (ret)
  			return ret;
e01402b11   Ralf Baechle   More AP / SP bits...
448
  	}
46230aa6e   Ralf Baechle   [MIPS] RTLX: Hand...
449
  	return rtlx_write(minor, buffer, count);
e01402b11   Ralf Baechle   More AP / SP bits...
450
  }
5dfe4c964   Arjan van de Ven   [PATCH] mark stru...
451
  static const struct file_operations rtlx_fops = {
2600990e6   Ralf Baechle   [MIPS] kpsd and o...
452
453
454
455
456
  	.owner =   THIS_MODULE,
  	.open =    file_open,
  	.release = file_release,
  	.write =   file_write,
  	.read =    file_read,
6038f373a   Arnd Bergmann   llseek: automatic...
457
458
  	.poll =    file_poll,
  	.llseek =  noop_llseek,
e01402b11   Ralf Baechle   More AP / SP bits...
459
  };
2600990e6   Ralf Baechle   [MIPS] kpsd and o...
460
461
  static struct irqaction rtlx_irq = {
  	.handler	= rtlx_interrupt,
2600990e6   Ralf Baechle   [MIPS] kpsd and o...
462
463
  	.name		= "RTLX",
  };
97dcb82de   Atsushi Nemoto   [MIPS] Define MIP...
464
  static int rtlx_irq_num = MIPS_CPU_IRQ_BASE + MIPS_CPU_RTLX_IRQ;
2600990e6   Ralf Baechle   [MIPS] kpsd and o...
465

afc4841d8   Ralf Baechle   Turn rtlx upside ...
466
467
468
  static char register_chrdev_failed[] __initdata =
  	KERN_ERR "rtlx_module_init: unable to register device
  ";
9d5a3f5fa   Ralf Baechle   [MIPS] rtlx: Fix ...
469
  static int __init rtlx_module_init(void)
e01402b11   Ralf Baechle   More AP / SP bits...
470
  {
bb3d7c7ff   Ralf Baechle   [MIPS] RTLX: Spri...
471
472
  	struct device *dev;
  	int i, err;
2600990e6   Ralf Baechle   [MIPS] kpsd and o...
473

07cc0c9e6   Ralf Baechle   [MIPS] MT: Enable...
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
  	if (!cpu_has_mipsmt) {
  		printk("VPE loader: not a MIPS MT capable processor
  ");
  		return -ENODEV;
  	}
  
  	if (tclimit == 0) {
  		printk(KERN_WARNING "No TCs reserved for AP/SP, not "
  		       "initializing RTLX.
  Pass maxtcs=<n> argument as kernel "
  		       "argument
  ");
  
  		return -ENODEV;
  	}
afc4841d8   Ralf Baechle   Turn rtlx upside ...
489
490
491
492
  	major = register_chrdev(0, module_name, &rtlx_fops);
  	if (major < 0) {
  		printk(register_chrdev_failed);
  		return major;
e01402b11   Ralf Baechle   More AP / SP bits...
493
  	}
2600990e6   Ralf Baechle   [MIPS] kpsd and o...
494
495
496
497
  	/* initialise the wait queues */
  	for (i = 0; i < RTLX_CHANNELS; i++) {
  		init_waitqueue_head(&channel_wqs[i].rt_queue);
  		init_waitqueue_head(&channel_wqs[i].lx_queue);
c4c4018b0   Ralf Baechle   [MIPS] RTLX, VPE:...
498
  		atomic_set(&channel_wqs[i].in_open, 0);
bc4809e93   Ralf Baechle   [MIPS] RTLX: Prot...
499
  		mutex_init(&channel_wqs[i].mutex);
bb3d7c7ff   Ralf Baechle   [MIPS] RTLX: Spri...
500

a9b12619f   Greg Kroah-Hartman   device create: mi...
501
502
  		dev = device_create(mt_class, NULL, MKDEV(major, i), NULL,
  				    "%s%d", module_name, i);
bb3d7c7ff   Ralf Baechle   [MIPS] RTLX: Spri...
503
504
505
506
  		if (IS_ERR(dev)) {
  			err = PTR_ERR(dev);
  			goto out_chrdev;
  		}
2600990e6   Ralf Baechle   [MIPS] kpsd and o...
507
508
509
510
511
  	}
  
  	/* set up notifiers */
  	notify.start = starting;
  	notify.stop = stopping;
07cc0c9e6   Ralf Baechle   [MIPS] MT: Enable...
512
  	vpe_notify(tclimit, &notify);
2600990e6   Ralf Baechle   [MIPS] kpsd and o...
513
514
515
  
  	if (cpu_has_vint)
  		set_vi_handler(MIPS_CPU_RTLX_IRQ, rtlx_dispatch);
1928cc84a   Kevin D. Kissell   [MIPS] MT: Functi...
516
517
518
519
520
521
  	else {
  		pr_err("APRP RTLX init on non-vectored-interrupt processor
  ");
  		err = -ENODEV;
  		goto out_chrdev;
  	}
2600990e6   Ralf Baechle   [MIPS] kpsd and o...
522
523
524
  
  	rtlx_irq.dev_id = rtlx;
  	setup_irq(rtlx_irq_num, &rtlx_irq);
afc4841d8   Ralf Baechle   Turn rtlx upside ...
525
  	return 0;
bb3d7c7ff   Ralf Baechle   [MIPS] RTLX: Spri...
526
527
528
529
530
531
  
  out_chrdev:
  	for (i = 0; i < RTLX_CHANNELS; i++)
  		device_destroy(mt_class, MKDEV(major, i));
  
  	return err;
e01402b11   Ralf Baechle   More AP / SP bits...
532
  }
afc4841d8   Ralf Baechle   Turn rtlx upside ...
533
  static void __exit rtlx_module_exit(void)
e01402b11   Ralf Baechle   More AP / SP bits...
534
  {
bb3d7c7ff   Ralf Baechle   [MIPS] RTLX: Spri...
535
536
537
538
  	int i;
  
  	for (i = 0; i < RTLX_CHANNELS; i++)
  		device_destroy(mt_class, MKDEV(major, i));
e01402b11   Ralf Baechle   More AP / SP bits...
539
540
541
542
543
  	unregister_chrdev(major, module_name);
  }
  
  module_init(rtlx_module_init);
  module_exit(rtlx_module_exit);
afc4841d8   Ralf Baechle   Turn rtlx upside ...
544

e01402b11   Ralf Baechle   More AP / SP bits...
545
  MODULE_DESCRIPTION("MIPS RTLX");
2600990e6   Ralf Baechle   [MIPS] kpsd and o...
546
  MODULE_AUTHOR("Elizabeth Oldham, MIPS Technologies, Inc.");
e01402b11   Ralf Baechle   More AP / SP bits...
547
  MODULE_LICENSE("GPL");