diff options
author | James Hogan <james.hogan@imgtec.com> | 2013-02-06 09:45:01 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2013-02-06 14:10:17 -0500 |
commit | 8200e38a0cbf95dd2acaa565badb9b6a71c0e9c3 (patch) | |
tree | 6becf07cd2f608d3feeb56132484438fe3607c52 /drivers/tty | |
parent | 26f7936c332a3ddc7ce88d5eceffbac3062ab836 (diff) |
tty: metag_da: Add metag DA TTY driver
Add a TTY driver for communicating over a Meta DA (Debug Adapter)
channel using the bios channel SWITCH operation.
Signed-off-by: James Hogan <james.hogan@imgtec.com>
Cc: Jiri Slaby <jslaby@suse.cz>
Acked-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/tty')
-rw-r--r-- | drivers/tty/Kconfig | 13 | ||||
-rw-r--r-- | drivers/tty/Makefile | 1 | ||||
-rw-r--r-- | drivers/tty/metag_da.c | 680 |
3 files changed, 694 insertions, 0 deletions
diff --git a/drivers/tty/Kconfig b/drivers/tty/Kconfig index 29dfc24f2dbb..978db344bda0 100644 --- a/drivers/tty/Kconfig +++ b/drivers/tty/Kconfig | |||
@@ -406,4 +406,17 @@ config GOLDFISH_TTY | |||
406 | help | 406 | help |
407 | Console and system TTY driver for the Goldfish virtual platform. | 407 | Console and system TTY driver for the Goldfish virtual platform. |
408 | 408 | ||
409 | config DA_TTY | ||
410 | bool "DA TTY" | ||
411 | depends on METAG_DA | ||
412 | select SERIAL_NONSTANDARD | ||
413 | help | ||
414 | This enables a TTY on a Dash channel. | ||
415 | |||
416 | config DA_CONSOLE | ||
417 | bool "DA Console" | ||
418 | depends on DA_TTY | ||
419 | help | ||
420 | This enables a console on a Dash channel. | ||
421 | |||
409 | endif # TTY | 422 | endif # TTY |
diff --git a/drivers/tty/Makefile b/drivers/tty/Makefile index 35649d35abf0..6b78399bc7c9 100644 --- a/drivers/tty/Makefile +++ b/drivers/tty/Makefile | |||
@@ -28,5 +28,6 @@ obj-$(CONFIG_SYNCLINKMP) += synclinkmp.o | |||
28 | obj-$(CONFIG_SYNCLINK) += synclink.o | 28 | obj-$(CONFIG_SYNCLINK) += synclink.o |
29 | obj-$(CONFIG_PPC_EPAPR_HV_BYTECHAN) += ehv_bytechan.o | 29 | obj-$(CONFIG_PPC_EPAPR_HV_BYTECHAN) += ehv_bytechan.o |
30 | obj-$(CONFIG_GOLDFISH_TTY) += goldfish.o | 30 | obj-$(CONFIG_GOLDFISH_TTY) += goldfish.o |
31 | obj-$(CONFIG_DA_TTY) += metag_da.o | ||
31 | 32 | ||
32 | obj-y += ipwireless/ | 33 | obj-y += ipwireless/ |
diff --git a/drivers/tty/metag_da.c b/drivers/tty/metag_da.c new file mode 100644 index 000000000000..fc2a36bddd3f --- /dev/null +++ b/drivers/tty/metag_da.c | |||
@@ -0,0 +1,680 @@ | |||
1 | /* | ||
2 | * dashtty.c - tty driver for Dash channels interface. | ||
3 | * | ||
4 | * Copyright (C) 2007,2008,2012 Imagination Technologies | ||
5 | * | ||
6 | * This file is subject to the terms and conditions of the GNU General Public | ||
7 | * License. See the file COPYING in the main directory of this archive | ||
8 | * for more details. | ||
9 | * | ||
10 | */ | ||
11 | |||
12 | #include <linux/atomic.h> | ||
13 | #include <linux/completion.h> | ||
14 | #include <linux/console.h> | ||
15 | #include <linux/delay.h> | ||
16 | #include <linux/export.h> | ||
17 | #include <linux/init.h> | ||
18 | #include <linux/kernel.h> | ||
19 | #include <linux/kthread.h> | ||
20 | #include <linux/mutex.h> | ||
21 | #include <linux/sched.h> | ||
22 | #include <linux/serial.h> | ||
23 | #include <linux/slab.h> | ||
24 | #include <linux/spinlock.h> | ||
25 | #include <linux/string.h> | ||
26 | #include <linux/timer.h> | ||
27 | #include <linux/tty.h> | ||
28 | #include <linux/tty_driver.h> | ||
29 | #include <linux/tty_flip.h> | ||
30 | #include <linux/uaccess.h> | ||
31 | |||
32 | #include <asm/da.h> | ||
33 | |||
34 | /* Channel error codes */ | ||
35 | #define CONAOK 0 | ||
36 | #define CONERR 1 | ||
37 | #define CONBAD 2 | ||
38 | #define CONPRM 3 | ||
39 | #define CONADR 4 | ||
40 | #define CONCNT 5 | ||
41 | #define CONCBF 6 | ||
42 | #define CONCBE 7 | ||
43 | #define CONBSY 8 | ||
44 | |||
45 | /* Default channel for the console */ | ||
46 | #define CONSOLE_CHANNEL 1 | ||
47 | |||
48 | #define NUM_TTY_CHANNELS 6 | ||
49 | |||
50 | /* Auto allocate */ | ||
51 | #define DA_TTY_MAJOR 0 | ||
52 | |||
53 | /* A speedy poll rate helps the userland debug process connection response. | ||
54 | * But, if you set it too high then no other userland processes get much | ||
55 | * of a look in. | ||
56 | */ | ||
57 | #define DA_TTY_POLL (HZ / 50) | ||
58 | |||
59 | /* | ||
60 | * A short put delay improves latency but has a high throughput overhead | ||
61 | */ | ||
62 | #define DA_TTY_PUT_DELAY (HZ / 100) | ||
63 | |||
64 | static atomic_t num_channels_need_poll = ATOMIC_INIT(0); | ||
65 | |||
66 | static struct timer_list poll_timer; | ||
67 | |||
68 | static struct tty_driver *channel_driver; | ||
69 | |||
70 | static struct timer_list put_timer; | ||
71 | static struct task_struct *dashtty_thread; | ||
72 | |||
73 | #define RX_BUF_SIZE 1024 | ||
74 | |||
75 | enum { | ||
76 | INCHR = 1, | ||
77 | OUTCHR, | ||
78 | RDBUF, | ||
79 | WRBUF, | ||
80 | RDSTAT | ||
81 | }; | ||
82 | |||
83 | /** | ||
84 | * struct dashtty_port - Wrapper struct for dashtty tty_port. | ||
85 | * @port: TTY port data | ||
86 | * @rx_lock: Lock for rx_buf. | ||
87 | * This protects between the poll timer and user context. | ||
88 | * It's also held during read SWITCH operations. | ||
89 | * @rx_buf: Read buffer | ||
90 | * @xmit_lock: Lock for xmit_*, and port.xmit_buf. | ||
91 | * This protects between user context and kernel thread. | ||
92 | * It's also held during write SWITCH operations. | ||
93 | * @xmit_cnt: Size of xmit buffer contents | ||
94 | * @xmit_head: Head of xmit buffer where data is written | ||
95 | * @xmit_tail: Tail of xmit buffer where data is read | ||
96 | * @xmit_empty: Completion for xmit buffer being empty | ||
97 | */ | ||
98 | struct dashtty_port { | ||
99 | struct tty_port port; | ||
100 | spinlock_t rx_lock; | ||
101 | void *rx_buf; | ||
102 | struct mutex xmit_lock; | ||
103 | unsigned int xmit_cnt; | ||
104 | unsigned int xmit_head; | ||
105 | unsigned int xmit_tail; | ||
106 | struct completion xmit_empty; | ||
107 | }; | ||
108 | |||
109 | static struct dashtty_port dashtty_ports[NUM_TTY_CHANNELS]; | ||
110 | |||
111 | static atomic_t dashtty_xmit_cnt = ATOMIC_INIT(0); | ||
112 | static wait_queue_head_t dashtty_waitqueue; | ||
113 | |||
114 | /* | ||
115 | * Low-level DA channel access routines | ||
116 | */ | ||
117 | static int chancall(int in_bios_function, int in_channel, | ||
118 | int in_arg2, void *in_arg3, | ||
119 | void *in_arg4) | ||
120 | { | ||
121 | register int bios_function asm("D1Ar1") = in_bios_function; | ||
122 | register int channel asm("D0Ar2") = in_channel; | ||
123 | register int arg2 asm("D1Ar3") = in_arg2; | ||
124 | register void *arg3 asm("D0Ar4") = in_arg3; | ||
125 | register void *arg4 asm("D1Ar5") = in_arg4; | ||
126 | register int bios_call asm("D0Ar6") = 3; | ||
127 | register int result asm("D0Re0"); | ||
128 | |||
129 | asm volatile ( | ||
130 | "MSETL [A0StP++], %6,%4,%2\n\t" | ||
131 | "ADD A0StP, A0StP, #8\n\t" | ||
132 | "SWITCH #0x0C30208\n\t" | ||
133 | "GETD %0, [A0StP+#-8]\n\t" | ||
134 | "SUB A0StP, A0StP, #(4*6)+8\n\t" | ||
135 | : "=d" (result) /* outs */ | ||
136 | : "d" (bios_function), | ||
137 | "d" (channel), | ||
138 | "d" (arg2), | ||
139 | "d" (arg3), | ||
140 | "d" (arg4), | ||
141 | "d" (bios_call) /* ins */ | ||
142 | : "memory"); | ||
143 | |||
144 | return result; | ||
145 | } | ||
146 | |||
147 | /* | ||
148 | * Attempts to fetch count bytes from channel and returns actual count. | ||
149 | */ | ||
150 | static int fetch_data(struct tty_struct *tty) | ||
151 | { | ||
152 | unsigned int channel = tty->index; | ||
153 | struct dashtty_port *dport = &dashtty_ports[channel]; | ||
154 | int received = 0; | ||
155 | |||
156 | spin_lock_bh(&dport->rx_lock); | ||
157 | /* check the port isn't being shut down */ | ||
158 | if (!dport->rx_buf) | ||
159 | goto unlock; | ||
160 | if (chancall(RDBUF, channel, RX_BUF_SIZE, | ||
161 | (void *)dport->rx_buf, &received) == CONAOK) { | ||
162 | if (received) { | ||
163 | int space; | ||
164 | unsigned char *cbuf; | ||
165 | |||
166 | space = tty_prepare_flip_string(&dport->port, &cbuf, | ||
167 | received); | ||
168 | |||
169 | if (space <= 0) | ||
170 | goto unlock; | ||
171 | |||
172 | memcpy(cbuf, dport->rx_buf, space); | ||
173 | tty_flip_buffer_push(&dport->port); | ||
174 | } | ||
175 | } | ||
176 | unlock: | ||
177 | spin_unlock_bh(&dport->rx_lock); | ||
178 | |||
179 | return received; | ||
180 | } | ||
181 | |||
182 | /** | ||
183 | * find_channel_to_poll() - Returns kref to the next channel tty to poll. | ||
184 | * Returns: The TTY of the next channel to poll, or NULL if no TTY needs | ||
185 | * polling. Release with tty_kref_put(). | ||
186 | */ | ||
187 | static struct tty_struct *find_channel_to_poll(void) | ||
188 | { | ||
189 | static int last_polled_channel; | ||
190 | int last = last_polled_channel; | ||
191 | int chan; | ||
192 | struct tty_struct *tty = NULL; | ||
193 | |||
194 | for (chan = last + 1; ; ++chan) { | ||
195 | if (chan >= NUM_TTY_CHANNELS) | ||
196 | chan = 0; | ||
197 | |||
198 | tty = tty_port_tty_get(&dashtty_ports[chan].port); | ||
199 | if (tty) { | ||
200 | last_polled_channel = chan; | ||
201 | return tty; | ||
202 | } | ||
203 | |||
204 | if (chan == last) | ||
205 | break; | ||
206 | } | ||
207 | return tty; | ||
208 | } | ||
209 | |||
210 | /** | ||
211 | * put_channel_data() - Write out a block of channel data. | ||
212 | * @chan: DA channel number. | ||
213 | * | ||
214 | * Write a single block of data out to the debug adapter. If the circular buffer | ||
215 | * is wrapped then only the first block is written. | ||
216 | * | ||
217 | * Returns: 1 if the remote buffer was too full to accept data. | ||
218 | * 0 otherwise. | ||
219 | */ | ||
220 | static int put_channel_data(unsigned int chan) | ||
221 | { | ||
222 | struct dashtty_port *dport; | ||
223 | struct tty_struct *tty; | ||
224 | int number_written; | ||
225 | unsigned int count = 0; | ||
226 | |||
227 | dport = &dashtty_ports[chan]; | ||
228 | mutex_lock(&dport->xmit_lock); | ||
229 | if (dport->xmit_cnt) { | ||
230 | count = min((unsigned int)(SERIAL_XMIT_SIZE - dport->xmit_tail), | ||
231 | dport->xmit_cnt); | ||
232 | chancall(WRBUF, chan, count, | ||
233 | dport->port.xmit_buf + dport->xmit_tail, | ||
234 | &number_written); | ||
235 | dport->xmit_cnt -= number_written; | ||
236 | if (!dport->xmit_cnt) { | ||
237 | /* reset pointers to avoid wraps */ | ||
238 | dport->xmit_head = 0; | ||
239 | dport->xmit_tail = 0; | ||
240 | complete(&dport->xmit_empty); | ||
241 | } else { | ||
242 | dport->xmit_tail += number_written; | ||
243 | if (dport->xmit_tail >= SERIAL_XMIT_SIZE) | ||
244 | dport->xmit_tail -= SERIAL_XMIT_SIZE; | ||
245 | } | ||
246 | atomic_sub(number_written, &dashtty_xmit_cnt); | ||
247 | } | ||
248 | mutex_unlock(&dport->xmit_lock); | ||
249 | |||
250 | /* if we've made more data available, wake up tty */ | ||
251 | if (count && number_written) { | ||
252 | tty = tty_port_tty_get(&dport->port); | ||
253 | if (tty) { | ||
254 | tty_wakeup(tty); | ||
255 | tty_kref_put(tty); | ||
256 | } | ||
257 | } | ||
258 | |||
259 | /* did the write fail? */ | ||
260 | return count && !number_written; | ||
261 | } | ||
262 | |||
263 | /** | ||
264 | * put_data() - Kernel thread to write out blocks of channel data to DA. | ||
265 | * @arg: Unused. | ||
266 | * | ||
267 | * This kernel thread runs while @dashtty_xmit_cnt != 0, and loops over the | ||
268 | * channels to write out any buffered data. If any of the channels stall due to | ||
269 | * the remote buffer being full, a hold off happens to allow the debugger to | ||
270 | * drain the buffer. | ||
271 | */ | ||
272 | static int put_data(void *arg) | ||
273 | { | ||
274 | unsigned int chan, stall; | ||
275 | |||
276 | __set_current_state(TASK_RUNNING); | ||
277 | while (!kthread_should_stop()) { | ||
278 | /* | ||
279 | * For each channel see if there's anything to transmit in the | ||
280 | * port's xmit_buf. | ||
281 | */ | ||
282 | stall = 0; | ||
283 | for (chan = 0; chan < NUM_TTY_CHANNELS; ++chan) | ||
284 | stall += put_channel_data(chan); | ||
285 | |||
286 | /* | ||
287 | * If some of the buffers are full, hold off for a short while | ||
288 | * to allow them to empty. | ||
289 | */ | ||
290 | if (stall) | ||
291 | msleep(25); | ||
292 | |||
293 | wait_event_interruptible(dashtty_waitqueue, | ||
294 | atomic_read(&dashtty_xmit_cnt)); | ||
295 | } | ||
296 | |||
297 | return 0; | ||
298 | } | ||
299 | |||
300 | /* | ||
301 | * This gets called every DA_TTY_POLL and polls the channels for data | ||
302 | */ | ||
303 | static void dashtty_timer(unsigned long ignored) | ||
304 | { | ||
305 | struct tty_struct *tty; | ||
306 | |||
307 | /* If there are no ports open do nothing and don't poll again. */ | ||
308 | if (!atomic_read(&num_channels_need_poll)) | ||
309 | return; | ||
310 | |||
311 | tty = find_channel_to_poll(); | ||
312 | |||
313 | /* Did we find a channel to poll? */ | ||
314 | if (tty) { | ||
315 | fetch_data(tty); | ||
316 | tty_kref_put(tty); | ||
317 | } | ||
318 | |||
319 | mod_timer_pinned(&poll_timer, jiffies + DA_TTY_POLL); | ||
320 | } | ||
321 | |||
322 | static void add_poll_timer(struct timer_list *poll_timer) | ||
323 | { | ||
324 | setup_timer(poll_timer, dashtty_timer, 0); | ||
325 | poll_timer->expires = jiffies + DA_TTY_POLL; | ||
326 | |||
327 | /* | ||
328 | * Always attach the timer to the boot CPU. The DA channels are per-CPU | ||
329 | * so all polling should be from a single CPU. | ||
330 | */ | ||
331 | add_timer_on(poll_timer, 0); | ||
332 | } | ||
333 | |||
334 | static int dashtty_port_activate(struct tty_port *port, struct tty_struct *tty) | ||
335 | { | ||
336 | struct dashtty_port *dport = container_of(port, struct dashtty_port, | ||
337 | port); | ||
338 | void *rx_buf; | ||
339 | |||
340 | /* Allocate the buffer we use for writing data */ | ||
341 | if (tty_port_alloc_xmit_buf(port) < 0) | ||
342 | goto err; | ||
343 | |||
344 | /* Allocate the buffer we use for reading data */ | ||
345 | rx_buf = kzalloc(RX_BUF_SIZE, GFP_KERNEL); | ||
346 | if (!rx_buf) | ||
347 | goto err_free_xmit; | ||
348 | |||
349 | spin_lock_bh(&dport->rx_lock); | ||
350 | dport->rx_buf = rx_buf; | ||
351 | spin_unlock_bh(&dport->rx_lock); | ||
352 | |||
353 | /* | ||
354 | * Don't add the poll timer if we're opening a console. This | ||
355 | * avoids the overhead of polling the Dash but means it is not | ||
356 | * possible to have a login on /dev/console. | ||
357 | * | ||
358 | */ | ||
359 | if (dport != &dashtty_ports[CONSOLE_CHANNEL]) | ||
360 | if (atomic_inc_return(&num_channels_need_poll) == 1) | ||
361 | add_poll_timer(&poll_timer); | ||
362 | |||
363 | return 0; | ||
364 | err_free_xmit: | ||
365 | tty_port_free_xmit_buf(port); | ||
366 | err: | ||
367 | return -ENOMEM; | ||
368 | } | ||
369 | |||
370 | static void dashtty_port_shutdown(struct tty_port *port) | ||
371 | { | ||
372 | struct dashtty_port *dport = container_of(port, struct dashtty_port, | ||
373 | port); | ||
374 | void *rx_buf; | ||
375 | unsigned int count; | ||
376 | |||
377 | /* stop reading */ | ||
378 | if (dport != &dashtty_ports[CONSOLE_CHANNEL]) | ||
379 | if (atomic_dec_and_test(&num_channels_need_poll)) | ||
380 | del_timer_sync(&poll_timer); | ||
381 | |||
382 | mutex_lock(&dport->xmit_lock); | ||
383 | count = dport->xmit_cnt; | ||
384 | mutex_unlock(&dport->xmit_lock); | ||
385 | if (count) { | ||
386 | /* | ||
387 | * There's still data to write out, so wake and wait for the | ||
388 | * writer thread to drain the buffer. | ||
389 | */ | ||
390 | del_timer(&put_timer); | ||
391 | wake_up_interruptible(&dashtty_waitqueue); | ||
392 | wait_for_completion(&dport->xmit_empty); | ||
393 | } | ||
394 | |||
395 | /* Null the read buffer (timer could still be running!) */ | ||
396 | spin_lock_bh(&dport->rx_lock); | ||
397 | rx_buf = dport->rx_buf; | ||
398 | dport->rx_buf = NULL; | ||
399 | spin_unlock_bh(&dport->rx_lock); | ||
400 | /* Free the read buffer */ | ||
401 | kfree(rx_buf); | ||
402 | |||
403 | /* Free the write buffer */ | ||
404 | tty_port_free_xmit_buf(port); | ||
405 | } | ||
406 | |||
407 | static const struct tty_port_operations dashtty_port_ops = { | ||
408 | .activate = dashtty_port_activate, | ||
409 | .shutdown = dashtty_port_shutdown, | ||
410 | }; | ||
411 | |||
412 | static int dashtty_install(struct tty_driver *driver, struct tty_struct *tty) | ||
413 | { | ||
414 | return tty_port_install(&dashtty_ports[tty->index].port, driver, tty); | ||
415 | } | ||
416 | |||
417 | static int dashtty_open(struct tty_struct *tty, struct file *filp) | ||
418 | { | ||
419 | return tty_port_open(tty->port, tty, filp); | ||
420 | } | ||
421 | |||
422 | static void dashtty_close(struct tty_struct *tty, struct file *filp) | ||
423 | { | ||
424 | return tty_port_close(tty->port, tty, filp); | ||
425 | } | ||
426 | |||
427 | static void dashtty_hangup(struct tty_struct *tty) | ||
428 | { | ||
429 | int channel; | ||
430 | struct dashtty_port *dport; | ||
431 | |||
432 | channel = tty->index; | ||
433 | dport = &dashtty_ports[channel]; | ||
434 | |||
435 | /* drop any data in the xmit buffer */ | ||
436 | mutex_lock(&dport->xmit_lock); | ||
437 | if (dport->xmit_cnt) { | ||
438 | atomic_sub(dport->xmit_cnt, &dashtty_xmit_cnt); | ||
439 | dport->xmit_cnt = 0; | ||
440 | dport->xmit_head = 0; | ||
441 | dport->xmit_tail = 0; | ||
442 | complete(&dport->xmit_empty); | ||
443 | } | ||
444 | mutex_unlock(&dport->xmit_lock); | ||
445 | |||
446 | tty_port_hangup(tty->port); | ||
447 | } | ||
448 | |||
449 | /** | ||
450 | * dashtty_put_timer() - Delayed wake up of kernel thread. | ||
451 | * @ignored: unused | ||
452 | * | ||
453 | * This timer function wakes up the kernel thread if any data exists in the | ||
454 | * buffers. It is used to delay the expensive writeout until the writer has | ||
455 | * stopped writing. | ||
456 | */ | ||
457 | static void dashtty_put_timer(unsigned long ignored) | ||
458 | { | ||
459 | if (atomic_read(&dashtty_xmit_cnt)) | ||
460 | wake_up_interruptible(&dashtty_waitqueue); | ||
461 | } | ||
462 | |||
463 | static int dashtty_write(struct tty_struct *tty, const unsigned char *buf, | ||
464 | int total) | ||
465 | { | ||
466 | int channel, count, block; | ||
467 | struct dashtty_port *dport; | ||
468 | |||
469 | /* Determine the channel */ | ||
470 | channel = tty->index; | ||
471 | dport = &dashtty_ports[channel]; | ||
472 | |||
473 | /* | ||
474 | * Write to output buffer. | ||
475 | * | ||
476 | * The reason that we asynchronously write the buffer is because if we | ||
477 | * were to write the buffer synchronously then because DA channels are | ||
478 | * per-CPU the buffer would be written to the channel of whatever CPU | ||
479 | * we're running on. | ||
480 | * | ||
481 | * What we actually want to happen is have all input and output done on | ||
482 | * one CPU. | ||
483 | */ | ||
484 | mutex_lock(&dport->xmit_lock); | ||
485 | /* work out how many bytes we can write to the xmit buffer */ | ||
486 | total = min(total, (int)(SERIAL_XMIT_SIZE - dport->xmit_cnt)); | ||
487 | atomic_add(total, &dashtty_xmit_cnt); | ||
488 | dport->xmit_cnt += total; | ||
489 | /* write the actual bytes (may need splitting if it wraps) */ | ||
490 | for (count = total; count; count -= block) { | ||
491 | block = min(count, (int)(SERIAL_XMIT_SIZE - dport->xmit_head)); | ||
492 | memcpy(dport->port.xmit_buf + dport->xmit_head, buf, block); | ||
493 | dport->xmit_head += block; | ||
494 | if (dport->xmit_head >= SERIAL_XMIT_SIZE) | ||
495 | dport->xmit_head -= SERIAL_XMIT_SIZE; | ||
496 | buf += block; | ||
497 | } | ||
498 | count = dport->xmit_cnt; | ||
499 | /* xmit buffer no longer empty? */ | ||
500 | if (count) | ||
501 | INIT_COMPLETION(dport->xmit_empty); | ||
502 | mutex_unlock(&dport->xmit_lock); | ||
503 | |||
504 | if (total) { | ||
505 | /* | ||
506 | * If the buffer is full, wake up the kthread, otherwise allow | ||
507 | * some more time for the buffer to fill up a bit before waking | ||
508 | * it. | ||
509 | */ | ||
510 | if (count == SERIAL_XMIT_SIZE) { | ||
511 | del_timer(&put_timer); | ||
512 | wake_up_interruptible(&dashtty_waitqueue); | ||
513 | } else { | ||
514 | mod_timer(&put_timer, jiffies + DA_TTY_PUT_DELAY); | ||
515 | } | ||
516 | } | ||
517 | return total; | ||
518 | } | ||
519 | |||
520 | static int dashtty_write_room(struct tty_struct *tty) | ||
521 | { | ||
522 | struct dashtty_port *dport; | ||
523 | int channel; | ||
524 | int room; | ||
525 | |||
526 | channel = tty->index; | ||
527 | dport = &dashtty_ports[channel]; | ||
528 | |||
529 | /* report the space in the xmit buffer */ | ||
530 | mutex_lock(&dport->xmit_lock); | ||
531 | room = SERIAL_XMIT_SIZE - dport->xmit_cnt; | ||
532 | mutex_unlock(&dport->xmit_lock); | ||
533 | |||
534 | return room; | ||
535 | } | ||
536 | |||
537 | static int dashtty_chars_in_buffer(struct tty_struct *tty) | ||
538 | { | ||
539 | struct dashtty_port *dport; | ||
540 | int channel; | ||
541 | int chars; | ||
542 | |||
543 | channel = tty->index; | ||
544 | dport = &dashtty_ports[channel]; | ||
545 | |||
546 | /* report the number of bytes in the xmit buffer */ | ||
547 | mutex_lock(&dport->xmit_lock); | ||
548 | chars = dport->xmit_cnt; | ||
549 | mutex_unlock(&dport->xmit_lock); | ||
550 | |||
551 | return chars; | ||
552 | } | ||
553 | |||
554 | static const struct tty_operations dashtty_ops = { | ||
555 | .install = dashtty_install, | ||
556 | .open = dashtty_open, | ||
557 | .close = dashtty_close, | ||
558 | .hangup = dashtty_hangup, | ||
559 | .write = dashtty_write, | ||
560 | .write_room = dashtty_write_room, | ||
561 | .chars_in_buffer = dashtty_chars_in_buffer, | ||
562 | }; | ||
563 | |||
564 | static int __init dashtty_init(void) | ||
565 | { | ||
566 | int ret; | ||
567 | int nport; | ||
568 | struct dashtty_port *dport; | ||
569 | |||
570 | if (!metag_da_enabled()) | ||
571 | return -ENODEV; | ||
572 | |||
573 | channel_driver = tty_alloc_driver(NUM_TTY_CHANNELS, | ||
574 | TTY_DRIVER_REAL_RAW); | ||
575 | if (IS_ERR(channel_driver)) | ||
576 | return PTR_ERR(channel_driver); | ||
577 | |||
578 | channel_driver->driver_name = "metag_da"; | ||
579 | channel_driver->name = "ttyDA"; | ||
580 | channel_driver->major = DA_TTY_MAJOR; | ||
581 | channel_driver->minor_start = 0; | ||
582 | channel_driver->type = TTY_DRIVER_TYPE_SERIAL; | ||
583 | channel_driver->subtype = SERIAL_TYPE_NORMAL; | ||
584 | channel_driver->init_termios = tty_std_termios; | ||
585 | channel_driver->init_termios.c_cflag |= CLOCAL; | ||
586 | |||
587 | tty_set_operations(channel_driver, &dashtty_ops); | ||
588 | for (nport = 0; nport < NUM_TTY_CHANNELS; nport++) { | ||
589 | dport = &dashtty_ports[nport]; | ||
590 | tty_port_init(&dport->port); | ||
591 | dport->port.ops = &dashtty_port_ops; | ||
592 | spin_lock_init(&dport->rx_lock); | ||
593 | mutex_init(&dport->xmit_lock); | ||
594 | /* the xmit buffer starts empty, i.e. completely written */ | ||
595 | init_completion(&dport->xmit_empty); | ||
596 | complete(&dport->xmit_empty); | ||
597 | } | ||
598 | |||
599 | setup_timer(&put_timer, dashtty_put_timer, 0); | ||
600 | |||
601 | init_waitqueue_head(&dashtty_waitqueue); | ||
602 | dashtty_thread = kthread_create(put_data, NULL, "ttyDA"); | ||
603 | if (IS_ERR(dashtty_thread)) { | ||
604 | pr_err("Couldn't create dashtty thread\n"); | ||
605 | ret = PTR_ERR(dashtty_thread); | ||
606 | goto err_destroy_ports; | ||
607 | } | ||
608 | /* | ||
609 | * Bind the writer thread to the boot CPU so it can't migrate. | ||
610 | * DA channels are per-CPU and we want all channel I/O to be on a single | ||
611 | * predictable CPU. | ||
612 | */ | ||
613 | kthread_bind(dashtty_thread, 0); | ||
614 | wake_up_process(dashtty_thread); | ||
615 | |||
616 | ret = tty_register_driver(channel_driver); | ||
617 | |||
618 | if (ret < 0) { | ||
619 | pr_err("Couldn't install dashtty driver: err %d\n", | ||
620 | ret); | ||
621 | goto err_stop_kthread; | ||
622 | } | ||
623 | |||
624 | return 0; | ||
625 | |||
626 | err_stop_kthread: | ||
627 | kthread_stop(dashtty_thread); | ||
628 | err_destroy_ports: | ||
629 | for (nport = 0; nport < NUM_TTY_CHANNELS; nport++) { | ||
630 | dport = &dashtty_ports[nport]; | ||
631 | tty_port_destroy(&dport->port); | ||
632 | } | ||
633 | put_tty_driver(channel_driver); | ||
634 | return ret; | ||
635 | } | ||
636 | |||
637 | static void dashtty_exit(void) | ||
638 | { | ||
639 | int nport; | ||
640 | struct dashtty_port *dport; | ||
641 | |||
642 | del_timer_sync(&put_timer); | ||
643 | kthread_stop(dashtty_thread); | ||
644 | del_timer_sync(&poll_timer); | ||
645 | tty_unregister_driver(channel_driver); | ||
646 | for (nport = 0; nport < NUM_TTY_CHANNELS; nport++) { | ||
647 | dport = &dashtty_ports[nport]; | ||
648 | tty_port_destroy(&dport->port); | ||
649 | } | ||
650 | put_tty_driver(channel_driver); | ||
651 | } | ||
652 | |||
653 | module_init(dashtty_init); | ||
654 | module_exit(dashtty_exit); | ||
655 | |||
656 | #ifdef CONFIG_DA_CONSOLE | ||
657 | |||
658 | static void dash_console_write(struct console *co, const char *s, | ||
659 | unsigned int count) | ||
660 | { | ||
661 | int actually_written; | ||
662 | |||
663 | chancall(WRBUF, CONSOLE_CHANNEL, count, (void *)s, &actually_written); | ||
664 | } | ||
665 | |||
666 | static struct tty_driver *dash_console_device(struct console *c, int *index) | ||
667 | { | ||
668 | *index = c->index; | ||
669 | return channel_driver; | ||
670 | } | ||
671 | |||
672 | struct console dash_console = { | ||
673 | .name = "ttyDA", | ||
674 | .write = dash_console_write, | ||
675 | .device = dash_console_device, | ||
676 | .flags = CON_PRINTBUFFER, | ||
677 | .index = 1, | ||
678 | }; | ||
679 | |||
680 | #endif | ||