xref: /linux/arch/mips/kernel/rtlx.c (revision a13d7201d7deedcbb6ac6efa94a1a7d34d3d79ec)
1 /*
2  * This file is subject to the terms and conditions of the GNU General Public
3  * License.  See the file "COPYING" in the main directory of this archive
4  * for more details.
5  *
6  * Copyright (C) 2005 MIPS Technologies, Inc.  All rights reserved.
7  * Copyright (C) 2005, 06 Ralf Baechle (ralf@linux-mips.org)
8  * Copyright (C) 2013 Imagination Technologies Ltd.
9  */
10 #include <linux/kernel.h>
11 #include <linux/fs.h>
12 #include <linux/syscalls.h>
13 #include <linux/moduleloader.h>
14 #include <linux/atomic.h>
15 #include <asm/mipsmtregs.h>
16 #include <asm/mips_mt.h>
17 #include <asm/processor.h>
18 #include <asm/rtlx.h>
19 #include <asm/setup.h>
20 #include <asm/vpe.h>
21 
22 static int sp_stopping;
23 struct rtlx_info *rtlx;
24 struct chan_waitqueues channel_wqs[RTLX_CHANNELS];
25 struct vpe_notifications rtlx_notify;
26 void (*aprp_hook)(void) = NULL;
27 EXPORT_SYMBOL(aprp_hook);
28 
29 static void __used dump_rtlx(void)
30 {
31 	int i;
32 
33 	pr_info("id 0x%lx state %d\n", rtlx->id, rtlx->state);
34 
35 	for (i = 0; i < RTLX_CHANNELS; i++) {
36 		struct rtlx_channel *chan = &rtlx->channel[i];
37 
38 		pr_info(" rt_state %d lx_state %d buffer_size %d\n",
39 			chan->rt_state, chan->lx_state, chan->buffer_size);
40 
41 		pr_info(" rt_read %d rt_write %d\n",
42 			chan->rt_read, chan->rt_write);
43 
44 		pr_info(" lx_read %d lx_write %d\n",
45 			chan->lx_read, chan->lx_write);
46 
47 		pr_info(" rt_buffer <%s>\n", chan->rt_buffer);
48 		pr_info(" lx_buffer <%s>\n", chan->lx_buffer);
49 	}
50 }
51 
52 /* call when we have the address of the shared structure from the SP side. */
53 static int rtlx_init(struct rtlx_info *rtlxi)
54 {
55 	if (rtlxi->id != RTLX_ID) {
56 		pr_err("no valid RTLX id at 0x%p 0x%lx\n", rtlxi, rtlxi->id);
57 		return -ENOEXEC;
58 	}
59 
60 	rtlx = rtlxi;
61 
62 	return 0;
63 }
64 
65 /* notifications */
66 void rtlx_starting(int vpe)
67 {
68 	int i;
69 	sp_stopping = 0;
70 
71 	/* force a reload of rtlx */
72 	rtlx = NULL;
73 
74 	/* wake up any sleeping rtlx_open's */
75 	for (i = 0; i < RTLX_CHANNELS; i++)
76 		wake_up_interruptible(&channel_wqs[i].lx_queue);
77 }
78 
79 void rtlx_stopping(int vpe)
80 {
81 	int i;
82 
83 	sp_stopping = 1;
84 	for (i = 0; i < RTLX_CHANNELS; i++)
85 		wake_up_interruptible(&channel_wqs[i].lx_queue);
86 }
87 
88 
89 int rtlx_open(int index, int can_sleep)
90 {
91 	struct rtlx_info **p;
92 	struct rtlx_channel *chan;
93 	enum rtlx_state state;
94 	int ret = 0;
95 
96 	if (index >= RTLX_CHANNELS) {
97 		pr_debug("rtlx_open index out of range\n");
98 		return -ENOSYS;
99 	}
100 
101 	if (atomic_inc_return(&channel_wqs[index].in_open) > 1) {
102 		pr_debug("rtlx_open channel %d already opened\n", index);
103 		ret = -EBUSY;
104 		goto out_fail;
105 	}
106 
107 	if (rtlx == NULL) {
108 		p = vpe_get_shared(aprp_cpu_index());
109 		if (p == NULL) {
110 			if (can_sleep) {
111 				ret = __wait_event_interruptible(
112 					channel_wqs[index].lx_queue,
113 					(p = vpe_get_shared(aprp_cpu_index())));
114 				if (ret)
115 					goto out_fail;
116 			} else {
117 				pr_debug("No SP program loaded, and device opened with O_NONBLOCK\n");
118 				ret = -ENOSYS;
119 				goto out_fail;
120 			}
121 		}
122 
123 		smp_rmb();
124 		if (*p == NULL) {
125 			if (can_sleep) {
126 				DEFINE_WAIT(wait);
127 
128 				for (;;) {
129 					prepare_to_wait(
130 						&channel_wqs[index].lx_queue,
131 						&wait, TASK_INTERRUPTIBLE);
132 					smp_rmb();
133 					if (*p != NULL)
134 						break;
135 					if (!signal_pending(current)) {
136 						schedule();
137 						continue;
138 					}
139 					ret = -ERESTARTSYS;
140 					goto out_fail;
141 				}
142 				finish_wait(&channel_wqs[index].lx_queue,
143 					    &wait);
144 			} else {
145 				pr_err(" *vpe_get_shared is NULL. Has an SP program been loaded?\n");
146 				ret = -ENOSYS;
147 				goto out_fail;
148 			}
149 		}
150 
151 		if ((unsigned int)*p < KSEG0) {
152 			pr_warn("vpe_get_shared returned an invalid pointer maybe an error code %d\n",
153 				(int)*p);
154 			ret = -ENOSYS;
155 			goto out_fail;
156 		}
157 
158 		ret = rtlx_init(*p);
159 		if (ret < 0)
160 			goto out_ret;
161 	}
162 
163 	chan = &rtlx->channel[index];
164 
165 	state = xchg(&chan->lx_state, RTLX_STATE_OPENED);
166 	if (state == RTLX_STATE_OPENED) {
167 		ret = -EBUSY;
168 		goto out_fail;
169 	}
170 
171 out_fail:
172 	smp_mb();
173 	atomic_dec(&channel_wqs[index].in_open);
174 	smp_mb();
175 
176 out_ret:
177 	return ret;
178 }
179 
180 int rtlx_release(int index)
181 {
182 	if (rtlx == NULL) {
183 		pr_err("rtlx_release() with null rtlx\n");
184 		return 0;
185 	}
186 	rtlx->channel[index].lx_state = RTLX_STATE_UNUSED;
187 	return 0;
188 }
189 
190 unsigned int rtlx_read_poll(int index, int can_sleep)
191 {
192 	struct rtlx_channel *chan;
193 
194 	if (rtlx == NULL)
195 		return 0;
196 
197 	chan = &rtlx->channel[index];
198 
199 	/* data available to read? */
200 	if (chan->lx_read == chan->lx_write) {
201 		if (can_sleep) {
202 			int ret = __wait_event_interruptible(
203 				channel_wqs[index].lx_queue,
204 				(chan->lx_read != chan->lx_write) ||
205 				sp_stopping);
206 			if (ret)
207 				return ret;
208 
209 			if (sp_stopping)
210 				return 0;
211 		} else
212 			return 0;
213 	}
214 
215 	return (chan->lx_write + chan->buffer_size - chan->lx_read)
216 	       % chan->buffer_size;
217 }
218 
219 static inline int write_spacefree(int read, int write, int size)
220 {
221 	if (read == write) {
222 		/*
223 		 * Never fill the buffer completely, so indexes are always
224 		 * equal if empty and only empty, or !equal if data available
225 		 */
226 		return size - 1;
227 	}
228 
229 	return ((read + size - write) % size) - 1;
230 }
231 
232 unsigned int rtlx_write_poll(int index)
233 {
234 	struct rtlx_channel *chan = &rtlx->channel[index];
235 
236 	return write_spacefree(chan->rt_read, chan->rt_write,
237 				chan->buffer_size);
238 }
239 
240 ssize_t rtlx_read(int index, void __user *buff, size_t count)
241 {
242 	size_t lx_write, fl = 0L;
243 	struct rtlx_channel *lx;
244 	unsigned long failed;
245 
246 	if (rtlx == NULL)
247 		return -ENOSYS;
248 
249 	lx = &rtlx->channel[index];
250 
251 	mutex_lock(&channel_wqs[index].mutex);
252 	smp_rmb();
253 	lx_write = lx->lx_write;
254 
255 	/* find out how much in total */
256 	count = min(count,
257 		     (size_t)(lx_write + lx->buffer_size - lx->lx_read)
258 		     % lx->buffer_size);
259 
260 	/* then how much from the read pointer onwards */
261 	fl = min(count, (size_t)lx->buffer_size - lx->lx_read);
262 
263 	failed = copy_to_user(buff, lx->lx_buffer + lx->lx_read, fl);
264 	if (failed)
265 		goto out;
266 
267 	/* and if there is anything left at the beginning of the buffer */
268 	if (count - fl)
269 		failed = copy_to_user(buff + fl, lx->lx_buffer, count - fl);
270 
271 out:
272 	count -= failed;
273 
274 	smp_wmb();
275 	lx->lx_read = (lx->lx_read + count) % lx->buffer_size;
276 	smp_wmb();
277 	mutex_unlock(&channel_wqs[index].mutex);
278 
279 	return count;
280 }
281 
282 ssize_t rtlx_write(int index, const void __user *buffer, size_t count)
283 {
284 	struct rtlx_channel *rt;
285 	unsigned long failed;
286 	size_t rt_read;
287 	size_t fl;
288 
289 	if (rtlx == NULL)
290 		return -ENOSYS;
291 
292 	rt = &rtlx->channel[index];
293 
294 	mutex_lock(&channel_wqs[index].mutex);
295 	smp_rmb();
296 	rt_read = rt->rt_read;
297 
298 	/* total number of bytes to copy */
299 	count = min_t(size_t, count, write_spacefree(rt_read, rt->rt_write,
300 						     rt->buffer_size));
301 
302 	/* first bit from write pointer to the end of the buffer, or count */
303 	fl = min(count, (size_t) rt->buffer_size - rt->rt_write);
304 
305 	failed = copy_from_user(rt->rt_buffer + rt->rt_write, buffer, fl);
306 	if (failed)
307 		goto out;
308 
309 	/* if there's any left copy to the beginning of the buffer */
310 	if (count - fl)
311 		failed = copy_from_user(rt->rt_buffer, buffer + fl, count - fl);
312 
313 out:
314 	count -= failed;
315 
316 	smp_wmb();
317 	rt->rt_write = (rt->rt_write + count) % rt->buffer_size;
318 	smp_wmb();
319 	mutex_unlock(&channel_wqs[index].mutex);
320 
321 	_interrupt_sp();
322 
323 	return count;
324 }
325 
326 
327 static int file_open(struct inode *inode, struct file *filp)
328 {
329 	return rtlx_open(iminor(inode), (filp->f_flags & O_NONBLOCK) ? 0 : 1);
330 }
331 
332 static int file_release(struct inode *inode, struct file *filp)
333 {
334 	return rtlx_release(iminor(inode));
335 }
336 
337 static unsigned int file_poll(struct file *file, poll_table *wait)
338 {
339 	int minor = iminor(file_inode(file));
340 	unsigned int mask = 0;
341 
342 	poll_wait(file, &channel_wqs[minor].rt_queue, wait);
343 	poll_wait(file, &channel_wqs[minor].lx_queue, wait);
344 
345 	if (rtlx == NULL)
346 		return 0;
347 
348 	/* data available to read? */
349 	if (rtlx_read_poll(minor, 0))
350 		mask |= POLLIN | POLLRDNORM;
351 
352 	/* space to write */
353 	if (rtlx_write_poll(minor))
354 		mask |= POLLOUT | POLLWRNORM;
355 
356 	return mask;
357 }
358 
359 static ssize_t file_read(struct file *file, char __user *buffer, size_t count,
360 			 loff_t *ppos)
361 {
362 	int minor = iminor(file_inode(file));
363 
364 	/* data available? */
365 	if (!rtlx_read_poll(minor, (file->f_flags & O_NONBLOCK) ? 0 : 1))
366 		return 0;	/* -EAGAIN makes 'cat' whine */
367 
368 	return rtlx_read(minor, buffer, count);
369 }
370 
371 static ssize_t file_write(struct file *file, const char __user *buffer,
372 			  size_t count, loff_t *ppos)
373 {
374 	int minor = iminor(file_inode(file));
375 
376 	/* any space left... */
377 	if (!rtlx_write_poll(minor)) {
378 		int ret;
379 
380 		if (file->f_flags & O_NONBLOCK)
381 			return -EAGAIN;
382 
383 		ret = __wait_event_interruptible(channel_wqs[minor].rt_queue,
384 					   rtlx_write_poll(minor));
385 		if (ret)
386 			return ret;
387 	}
388 
389 	return rtlx_write(minor, buffer, count);
390 }
391 
392 const struct file_operations rtlx_fops = {
393 	.owner =   THIS_MODULE,
394 	.open =    file_open,
395 	.release = file_release,
396 	.write =   file_write,
397 	.read =    file_read,
398 	.poll =    file_poll,
399 	.llseek =  noop_llseek,
400 };
401 
402 module_init(rtlx_module_init);
403 module_exit(rtlx_module_exit);
404 
405 MODULE_DESCRIPTION("MIPS RTLX");
406 MODULE_AUTHOR("Elizabeth Oldham, MIPS Technologies, Inc.");
407 MODULE_LICENSE("GPL");
408