diff options
author | Chris Metcalf <cmetcalf@tilera.com> | 2013-08-12 14:11:44 -0400 |
---|---|---|
committer | Chris Metcalf <cmetcalf@tilera.com> | 2013-09-03 14:50:40 -0400 |
commit | b5c6c1a72afcc416c11ad932589054dcd3125782 (patch) | |
tree | 4ea65027e6cb9fd4951c5091b49252c3d4d75b2b /drivers/tty | |
parent | 6ec006ede5e0526c20cd7ed5e20df637ea592b1f (diff) |
tilegx: Add tty serial support for TILE-Gx on-chip UART
Signed-off-by: Chris Metcalf <cmetcalf@tilera.com>
Acked-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/tty')
-rw-r--r-- | drivers/tty/serial/Kconfig | 9 | ||||
-rw-r--r-- | drivers/tty/serial/Makefile | 1 | ||||
-rw-r--r-- | drivers/tty/serial/tilegx.c | 708 |
3 files changed, 718 insertions, 0 deletions
diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index 5e3d68917ffe..fdcaaefcdf8c 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig | |||
@@ -1436,6 +1436,15 @@ config SERIAL_EFM32_UART_CONSOLE | |||
1436 | depends on SERIAL_EFM32_UART=y | 1436 | depends on SERIAL_EFM32_UART=y |
1437 | select SERIAL_CORE_CONSOLE | 1437 | select SERIAL_CORE_CONSOLE |
1438 | 1438 | ||
1439 | config SERIAL_TILEGX | ||
1440 | tristate "TILE-Gx on-chip serial port support" | ||
1441 | depends on TILEGX | ||
1442 | select TILE_GXIO_UART | ||
1443 | select SERIAL_CORE | ||
1444 | ---help--- | ||
1445 | This device provides access to the on-chip UARTs on the TILE-Gx | ||
1446 | processor. | ||
1447 | |||
1439 | config SERIAL_ARC | 1448 | config SERIAL_ARC |
1440 | tristate "ARC UART driver support" | 1449 | tristate "ARC UART driver support" |
1441 | select SERIAL_CORE | 1450 | select SERIAL_CORE |
diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile index cf650f0cd6e4..3d0f097e82ff 100644 --- a/drivers/tty/serial/Makefile +++ b/drivers/tty/serial/Makefile | |||
@@ -65,6 +65,7 @@ obj-$(CONFIG_SERIAL_KGDB_NMI) += kgdb_nmi.o | |||
65 | obj-$(CONFIG_SERIAL_KS8695) += serial_ks8695.o | 65 | obj-$(CONFIG_SERIAL_KS8695) += serial_ks8695.o |
66 | obj-$(CONFIG_SERIAL_OMAP) += omap-serial.o | 66 | obj-$(CONFIG_SERIAL_OMAP) += omap-serial.o |
67 | obj-$(CONFIG_SERIAL_ALTERA_UART) += altera_uart.o | 67 | obj-$(CONFIG_SERIAL_ALTERA_UART) += altera_uart.o |
68 | obj-$(CONFIG_SERIAL_TILEGX) += tilegx.o | ||
68 | obj-$(CONFIG_KGDB_SERIAL_CONSOLE) += kgdboc.o | 69 | obj-$(CONFIG_KGDB_SERIAL_CONSOLE) += kgdboc.o |
69 | obj-$(CONFIG_SERIAL_QE) += ucc_uart.o | 70 | obj-$(CONFIG_SERIAL_QE) += ucc_uart.o |
70 | obj-$(CONFIG_SERIAL_TIMBERDALE) += timbuart.o | 71 | obj-$(CONFIG_SERIAL_TIMBERDALE) += timbuart.o |
diff --git a/drivers/tty/serial/tilegx.c b/drivers/tty/serial/tilegx.c new file mode 100644 index 000000000000..f92d7e6bd876 --- /dev/null +++ b/drivers/tty/serial/tilegx.c | |||
@@ -0,0 +1,708 @@ | |||
1 | /* | ||
2 | * Copyright 2013 Tilera Corporation. All Rights Reserved. | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or | ||
5 | * modify it under the terms of the GNU General Public License | ||
6 | * as published by the Free Software Foundation, version 2. | ||
7 | * | ||
8 | * This program is distributed in the hope that it will be useful, but | ||
9 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
10 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or | ||
11 | * NON INFRINGEMENT. See the GNU General Public License for | ||
12 | * more details. | ||
13 | * | ||
14 | * TILEGx UART driver. | ||
15 | */ | ||
16 | |||
17 | #include <linux/delay.h> | ||
18 | #include <linux/init.h> | ||
19 | #include <linux/interrupt.h> | ||
20 | #include <linux/io.h> | ||
21 | #include <linux/irq.h> | ||
22 | #include <linux/module.h> | ||
23 | #include <linux/serial_core.h> | ||
24 | #include <linux/tty.h> | ||
25 | #include <linux/tty_flip.h> | ||
26 | |||
27 | #include <gxio/common.h> | ||
28 | #include <gxio/iorpc_globals.h> | ||
29 | #include <gxio/iorpc_uart.h> | ||
30 | #include <gxio/kiorpc.h> | ||
31 | |||
32 | #include <hv/drv_uart_intf.h> | ||
33 | |||
34 | /* | ||
35 | * Use device name ttyS, major 4, minor 64-65. | ||
36 | * This is the usual serial port name, 8250 conventional range. | ||
37 | */ | ||
38 | #define TILEGX_UART_MAJOR TTY_MAJOR | ||
39 | #define TILEGX_UART_MINOR 64 | ||
40 | #define TILEGX_UART_NAME "ttyS" | ||
41 | #define DRIVER_NAME_STRING "TILEGx_Serial" | ||
42 | #define TILEGX_UART_REF_CLK 125000000; /* REF_CLK is always 125 MHz. */ | ||
43 | |||
44 | struct tile_uart_port { | ||
45 | /* UART port. */ | ||
46 | struct uart_port uart; | ||
47 | |||
48 | /* GXIO device context. */ | ||
49 | gxio_uart_context_t context; | ||
50 | |||
51 | /* UART access mutex. */ | ||
52 | struct mutex mutex; | ||
53 | |||
54 | /* CPU receiving interrupts. */ | ||
55 | int irq_cpu; | ||
56 | }; | ||
57 | |||
58 | static struct tile_uart_port tile_uart_ports[TILEGX_UART_NR]; | ||
59 | static struct uart_driver tilegx_uart_driver; | ||
60 | |||
61 | |||
62 | /* | ||
63 | * Read UART rx fifo, and insert the chars into tty buffer. | ||
64 | */ | ||
65 | static void receive_chars(struct tile_uart_port *tile_uart, | ||
66 | struct tty_struct *tty) | ||
67 | { | ||
68 | int i; | ||
69 | char c; | ||
70 | UART_FIFO_COUNT_t count; | ||
71 | gxio_uart_context_t *context = &tile_uart->context; | ||
72 | struct tty_port *port = tty->port; | ||
73 | |||
74 | count.word = gxio_uart_read(context, UART_FIFO_COUNT); | ||
75 | for (i = 0; i < count.rfifo_count; i++) { | ||
76 | c = (char)gxio_uart_read(context, UART_RECEIVE_DATA); | ||
77 | tty_insert_flip_char(port, c, TTY_NORMAL); | ||
78 | } | ||
79 | } | ||
80 | |||
81 | |||
82 | /* | ||
83 | * Drain the Rx FIFO, called by interrupt handler. | ||
84 | */ | ||
85 | static void handle_receive(struct tile_uart_port *tile_uart) | ||
86 | { | ||
87 | struct tty_port *port = &tile_uart->uart.state->port; | ||
88 | struct tty_struct *tty = tty_port_tty_get(port); | ||
89 | gxio_uart_context_t *context = &tile_uart->context; | ||
90 | |||
91 | if (!tty) | ||
92 | return; | ||
93 | |||
94 | /* First read UART rx fifo. */ | ||
95 | receive_chars(tile_uart, tty); | ||
96 | |||
97 | /* Reset RFIFO_WE interrupt. */ | ||
98 | gxio_uart_write(context, UART_INTERRUPT_STATUS, | ||
99 | UART_INTERRUPT_MASK__RFIFO_WE_MASK); | ||
100 | |||
101 | /* Final read, if any chars comes between the first read and | ||
102 | * the interrupt reset. | ||
103 | */ | ||
104 | receive_chars(tile_uart, tty); | ||
105 | |||
106 | spin_unlock(&tile_uart->uart.lock); | ||
107 | tty_flip_buffer_push(port); | ||
108 | spin_lock(&tile_uart->uart.lock); | ||
109 | tty_kref_put(tty); | ||
110 | } | ||
111 | |||
112 | |||
113 | /* | ||
114 | * Push one char to UART Write FIFO. | ||
115 | * Return 0 on success, -1 if write filo is full. | ||
116 | */ | ||
117 | static int tilegx_putchar(gxio_uart_context_t *context, char c) | ||
118 | { | ||
119 | UART_FLAG_t flag; | ||
120 | flag.word = gxio_uart_read(context, UART_FLAG); | ||
121 | if (flag.wfifo_full) | ||
122 | return -1; | ||
123 | |||
124 | gxio_uart_write(context, UART_TRANSMIT_DATA, (unsigned long)c); | ||
125 | return 0; | ||
126 | } | ||
127 | |||
128 | |||
129 | /* | ||
130 | * Send chars to UART Write FIFO; called by interrupt handler. | ||
131 | */ | ||
132 | static void handle_transmit(struct tile_uart_port *tile_uart) | ||
133 | { | ||
134 | unsigned char ch; | ||
135 | struct uart_port *port; | ||
136 | struct circ_buf *xmit; | ||
137 | gxio_uart_context_t *context = &tile_uart->context; | ||
138 | |||
139 | /* First reset WFIFO_RE interrupt. */ | ||
140 | gxio_uart_write(context, UART_INTERRUPT_STATUS, | ||
141 | UART_INTERRUPT_MASK__WFIFO_RE_MASK); | ||
142 | |||
143 | port = &tile_uart->uart; | ||
144 | xmit = &port->state->xmit; | ||
145 | if (port->x_char) { | ||
146 | if (tilegx_putchar(context, port->x_char)) | ||
147 | return; | ||
148 | port->x_char = 0; | ||
149 | port->icount.tx++; | ||
150 | } | ||
151 | |||
152 | if (uart_circ_empty(xmit) || uart_tx_stopped(port)) | ||
153 | return; | ||
154 | |||
155 | while (!uart_circ_empty(xmit)) { | ||
156 | ch = xmit->buf[xmit->tail]; | ||
157 | if (tilegx_putchar(context, ch)) | ||
158 | break; | ||
159 | xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); | ||
160 | port->icount.tx++; | ||
161 | } | ||
162 | |||
163 | /* Reset WFIFO_RE interrupt. */ | ||
164 | gxio_uart_write(context, UART_INTERRUPT_STATUS, | ||
165 | UART_INTERRUPT_MASK__WFIFO_RE_MASK); | ||
166 | |||
167 | if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) | ||
168 | uart_write_wakeup(port); | ||
169 | } | ||
170 | |||
171 | |||
172 | /* | ||
173 | * UART Interrupt handler. | ||
174 | */ | ||
175 | static irqreturn_t tilegx_interrupt(int irq, void *dev_id) | ||
176 | { | ||
177 | unsigned long flags; | ||
178 | UART_INTERRUPT_STATUS_t intr_stat; | ||
179 | struct tile_uart_port *tile_uart; | ||
180 | gxio_uart_context_t *context; | ||
181 | struct uart_port *port = dev_id; | ||
182 | irqreturn_t ret = IRQ_NONE; | ||
183 | |||
184 | spin_lock_irqsave(&port->lock, flags); | ||
185 | |||
186 | tile_uart = container_of(port, struct tile_uart_port, uart); | ||
187 | context = &tile_uart->context; | ||
188 | intr_stat.word = gxio_uart_read(context, UART_INTERRUPT_STATUS); | ||
189 | |||
190 | if (intr_stat.rfifo_we) { | ||
191 | handle_receive(tile_uart); | ||
192 | ret = IRQ_HANDLED; | ||
193 | } | ||
194 | if (intr_stat.wfifo_re) { | ||
195 | handle_transmit(tile_uart); | ||
196 | ret = IRQ_HANDLED; | ||
197 | } | ||
198 | |||
199 | spin_unlock_irqrestore(&port->lock, flags); | ||
200 | return ret; | ||
201 | } | ||
202 | |||
203 | |||
204 | /* | ||
205 | * Return TIOCSER_TEMT when transmitter FIFO is empty. | ||
206 | */ | ||
207 | static u_int tilegx_tx_empty(struct uart_port *port) | ||
208 | { | ||
209 | int ret; | ||
210 | UART_FLAG_t flag; | ||
211 | struct tile_uart_port *tile_uart; | ||
212 | gxio_uart_context_t *context; | ||
213 | |||
214 | tile_uart = container_of(port, struct tile_uart_port, uart); | ||
215 | if (!mutex_trylock(&tile_uart->mutex)) | ||
216 | return 0; | ||
217 | context = &tile_uart->context; | ||
218 | |||
219 | flag.word = gxio_uart_read(context, UART_FLAG); | ||
220 | ret = (flag.wfifo_empty) ? TIOCSER_TEMT : 0; | ||
221 | mutex_unlock(&tile_uart->mutex); | ||
222 | |||
223 | return ret; | ||
224 | } | ||
225 | |||
226 | |||
227 | /* | ||
228 | * Set state of the modem control output lines. | ||
229 | */ | ||
230 | static void tilegx_set_mctrl(struct uart_port *port, u_int mctrl) | ||
231 | { | ||
232 | /* N/A */ | ||
233 | } | ||
234 | |||
235 | |||
236 | /* | ||
237 | * Get state of the modem control input lines. | ||
238 | */ | ||
239 | static u_int tilegx_get_mctrl(struct uart_port *port) | ||
240 | { | ||
241 | return TIOCM_CTS | TIOCM_DSR | TIOCM_CAR; | ||
242 | } | ||
243 | |||
244 | |||
245 | /* | ||
246 | * Stop transmitting. | ||
247 | */ | ||
248 | static void tilegx_stop_tx(struct uart_port *port) | ||
249 | { | ||
250 | /* N/A */ | ||
251 | } | ||
252 | |||
253 | |||
254 | /* | ||
255 | * Start transmitting. | ||
256 | */ | ||
257 | static void tilegx_start_tx(struct uart_port *port) | ||
258 | { | ||
259 | unsigned char ch; | ||
260 | struct circ_buf *xmit; | ||
261 | struct tile_uart_port *tile_uart; | ||
262 | gxio_uart_context_t *context; | ||
263 | |||
264 | tile_uart = container_of(port, struct tile_uart_port, uart); | ||
265 | if (!mutex_trylock(&tile_uart->mutex)) | ||
266 | return; | ||
267 | context = &tile_uart->context; | ||
268 | xmit = &port->state->xmit; | ||
269 | if (port->x_char) { | ||
270 | if (tilegx_putchar(context, port->x_char)) | ||
271 | return; | ||
272 | port->x_char = 0; | ||
273 | port->icount.tx++; | ||
274 | } | ||
275 | |||
276 | if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { | ||
277 | mutex_unlock(&tile_uart->mutex); | ||
278 | return; | ||
279 | } | ||
280 | |||
281 | while (!uart_circ_empty(xmit)) { | ||
282 | ch = xmit->buf[xmit->tail]; | ||
283 | if (tilegx_putchar(context, ch)) | ||
284 | break; | ||
285 | xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); | ||
286 | port->icount.tx++; | ||
287 | } | ||
288 | |||
289 | if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) | ||
290 | uart_write_wakeup(port); | ||
291 | |||
292 | mutex_unlock(&tile_uart->mutex); | ||
293 | } | ||
294 | |||
295 | |||
296 | /* | ||
297 | * Stop receiving - port is in process of being closed. | ||
298 | */ | ||
299 | static void tilegx_stop_rx(struct uart_port *port) | ||
300 | { | ||
301 | int err; | ||
302 | struct tile_uart_port *tile_uart; | ||
303 | gxio_uart_context_t *context; | ||
304 | int cpu; | ||
305 | |||
306 | tile_uart = container_of(port, struct tile_uart_port, uart); | ||
307 | if (!mutex_trylock(&tile_uart->mutex)) | ||
308 | return; | ||
309 | |||
310 | context = &tile_uart->context; | ||
311 | cpu = tile_uart->irq_cpu; | ||
312 | err = gxio_uart_cfg_interrupt(context, cpu_x(cpu), cpu_y(cpu), | ||
313 | KERNEL_PL, -1); | ||
314 | mutex_unlock(&tile_uart->mutex); | ||
315 | } | ||
316 | |||
317 | |||
318 | /* | ||
319 | * Enable modem status interrupts. | ||
320 | */ | ||
321 | static void tilegx_enable_ms(struct uart_port *port) | ||
322 | { | ||
323 | /* N/A */ | ||
324 | } | ||
325 | |||
326 | /* | ||
327 | * Control the transmission of a break signal. | ||
328 | */ | ||
329 | static void tilegx_break_ctl(struct uart_port *port, int break_state) | ||
330 | { | ||
331 | /* N/A */ | ||
332 | } | ||
333 | |||
334 | |||
335 | /* | ||
336 | * Perform initialization and enable port for reception. | ||
337 | */ | ||
338 | static int tilegx_startup(struct uart_port *port) | ||
339 | { | ||
340 | struct tile_uart_port *tile_uart; | ||
341 | gxio_uart_context_t *context; | ||
342 | int ret = 0; | ||
343 | int cpu = raw_smp_processor_id(); /* pick an arbitrary cpu */ | ||
344 | |||
345 | tile_uart = container_of(port, struct tile_uart_port, uart); | ||
346 | if (mutex_lock_interruptible(&tile_uart->mutex)) | ||
347 | return -EBUSY; | ||
348 | context = &tile_uart->context; | ||
349 | |||
350 | /* Now open the hypervisor device if we haven't already. */ | ||
351 | if (context->fd < 0) { | ||
352 | UART_INTERRUPT_MASK_t intr_mask; | ||
353 | |||
354 | /* Initialize UART device. */ | ||
355 | ret = gxio_uart_init(context, port->line); | ||
356 | if (ret) { | ||
357 | ret = -ENXIO; | ||
358 | goto err; | ||
359 | } | ||
360 | |||
361 | /* Create our IRQs. */ | ||
362 | port->irq = create_irq(); | ||
363 | if (port->irq < 0) | ||
364 | goto err_uart_dest; | ||
365 | tile_irq_activate(port->irq, TILE_IRQ_PERCPU); | ||
366 | |||
367 | /* Register our IRQs. */ | ||
368 | ret = request_irq(port->irq, tilegx_interrupt, 0, | ||
369 | tilegx_uart_driver.driver_name, port); | ||
370 | if (ret) | ||
371 | goto err_dest_irq; | ||
372 | |||
373 | /* Request that the hardware start sending us interrupts. */ | ||
374 | tile_uart->irq_cpu = cpu; | ||
375 | ret = gxio_uart_cfg_interrupt(context, cpu_x(cpu), cpu_y(cpu), | ||
376 | KERNEL_PL, port->irq); | ||
377 | if (ret) | ||
378 | goto err_free_irq; | ||
379 | |||
380 | /* Enable UART Tx/Rx Interrupt. */ | ||
381 | intr_mask.word = gxio_uart_read(context, UART_INTERRUPT_MASK); | ||
382 | intr_mask.wfifo_re = 0; | ||
383 | intr_mask.rfifo_we = 0; | ||
384 | gxio_uart_write(context, UART_INTERRUPT_MASK, intr_mask.word); | ||
385 | |||
386 | /* Reset the Tx/Rx interrupt in case it's set. */ | ||
387 | gxio_uart_write(context, UART_INTERRUPT_STATUS, | ||
388 | UART_INTERRUPT_MASK__WFIFO_RE_MASK | | ||
389 | UART_INTERRUPT_MASK__RFIFO_WE_MASK); | ||
390 | } | ||
391 | |||
392 | mutex_unlock(&tile_uart->mutex); | ||
393 | return ret; | ||
394 | |||
395 | err_free_irq: | ||
396 | free_irq(port->irq, port); | ||
397 | err_dest_irq: | ||
398 | destroy_irq(port->irq); | ||
399 | err_uart_dest: | ||
400 | gxio_uart_destroy(context); | ||
401 | ret = -ENXIO; | ||
402 | err: | ||
403 | mutex_unlock(&tile_uart->mutex); | ||
404 | return ret; | ||
405 | } | ||
406 | |||
407 | |||
408 | /* | ||
409 | * Release kernel resources if it is the last close, disable the port, | ||
410 | * free IRQ and close the port. | ||
411 | */ | ||
412 | static void tilegx_shutdown(struct uart_port *port) | ||
413 | { | ||
414 | int err; | ||
415 | UART_INTERRUPT_MASK_t intr_mask; | ||
416 | struct tile_uart_port *tile_uart; | ||
417 | gxio_uart_context_t *context; | ||
418 | int cpu; | ||
419 | |||
420 | tile_uart = container_of(port, struct tile_uart_port, uart); | ||
421 | if (mutex_lock_interruptible(&tile_uart->mutex)) | ||
422 | return; | ||
423 | context = &tile_uart->context; | ||
424 | |||
425 | /* Disable UART Tx/Rx Interrupt. */ | ||
426 | intr_mask.word = gxio_uart_read(context, UART_INTERRUPT_MASK); | ||
427 | intr_mask.wfifo_re = 1; | ||
428 | intr_mask.rfifo_we = 1; | ||
429 | gxio_uart_write(context, UART_INTERRUPT_MASK, intr_mask.word); | ||
430 | |||
431 | /* Request that the hardware stop sending us interrupts. */ | ||
432 | cpu = tile_uart->irq_cpu; | ||
433 | err = gxio_uart_cfg_interrupt(context, cpu_x(cpu), cpu_y(cpu), | ||
434 | KERNEL_PL, -1); | ||
435 | |||
436 | if (port->irq > 0) { | ||
437 | free_irq(port->irq, port); | ||
438 | destroy_irq(port->irq); | ||
439 | port->irq = 0; | ||
440 | } | ||
441 | |||
442 | gxio_uart_destroy(context); | ||
443 | |||
444 | mutex_unlock(&tile_uart->mutex); | ||
445 | } | ||
446 | |||
447 | |||
448 | /* | ||
449 | * Flush the buffer. | ||
450 | */ | ||
451 | static void tilegx_flush_buffer(struct uart_port *port) | ||
452 | { | ||
453 | /* N/A */ | ||
454 | } | ||
455 | |||
456 | |||
457 | /* | ||
458 | * Change the port parameters. | ||
459 | */ | ||
460 | static void tilegx_set_termios(struct uart_port *port, | ||
461 | struct ktermios *termios, struct ktermios *old) | ||
462 | { | ||
463 | int err; | ||
464 | UART_DIVISOR_t divisor; | ||
465 | UART_TYPE_t type; | ||
466 | unsigned int baud; | ||
467 | struct tile_uart_port *tile_uart; | ||
468 | gxio_uart_context_t *context; | ||
469 | |||
470 | tile_uart = container_of(port, struct tile_uart_port, uart); | ||
471 | if (!mutex_trylock(&tile_uart->mutex)) | ||
472 | return; | ||
473 | context = &tile_uart->context; | ||
474 | |||
475 | /* Open the hypervisor device if we haven't already. */ | ||
476 | if (context->fd < 0) { | ||
477 | err = gxio_uart_init(context, port->line); | ||
478 | if (err) { | ||
479 | mutex_unlock(&tile_uart->mutex); | ||
480 | return; | ||
481 | } | ||
482 | } | ||
483 | |||
484 | divisor.word = gxio_uart_read(context, UART_DIVISOR); | ||
485 | type.word = gxio_uart_read(context, UART_TYPE); | ||
486 | |||
487 | /* Divisor. */ | ||
488 | baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk / 16); | ||
489 | divisor.divisor = uart_get_divisor(port, baud); | ||
490 | |||
491 | /* Byte size. */ | ||
492 | if ((termios->c_cflag & CSIZE) == CS7) | ||
493 | type.dbits = UART_TYPE__DBITS_VAL_SEVEN_DBITS; | ||
494 | else | ||
495 | type.dbits = UART_TYPE__DBITS_VAL_EIGHT_DBITS; | ||
496 | |||
497 | /* Parity. */ | ||
498 | if (termios->c_cflag & PARENB) { | ||
499 | /* Mark or Space parity. */ | ||
500 | if (termios->c_cflag & CMSPAR) | ||
501 | if (termios->c_cflag & PARODD) | ||
502 | type.ptype = UART_TYPE__PTYPE_VAL_MARK; | ||
503 | else | ||
504 | type.ptype = UART_TYPE__PTYPE_VAL_SPACE; | ||
505 | else if (termios->c_cflag & PARODD) | ||
506 | type.ptype = UART_TYPE__PTYPE_VAL_ODD; | ||
507 | else | ||
508 | type.ptype = UART_TYPE__PTYPE_VAL_EVEN; | ||
509 | } else | ||
510 | type.ptype = UART_TYPE__PTYPE_VAL_NONE; | ||
511 | |||
512 | /* Stop bits. */ | ||
513 | if (termios->c_cflag & CSTOPB) | ||
514 | type.sbits = UART_TYPE__SBITS_VAL_TWO_SBITS; | ||
515 | else | ||
516 | type.sbits = UART_TYPE__SBITS_VAL_ONE_SBITS; | ||
517 | |||
518 | /* Set the uart paramters. */ | ||
519 | gxio_uart_write(context, UART_DIVISOR, divisor.word); | ||
520 | gxio_uart_write(context, UART_TYPE, type.word); | ||
521 | |||
522 | mutex_unlock(&tile_uart->mutex); | ||
523 | } | ||
524 | |||
525 | |||
526 | /* | ||
527 | * Return string describing the specified port. | ||
528 | */ | ||
529 | static const char *tilegx_type(struct uart_port *port) | ||
530 | { | ||
531 | return port->type == PORT_TILEGX ? DRIVER_NAME_STRING : NULL; | ||
532 | } | ||
533 | |||
534 | |||
535 | /* | ||
536 | * Release the resources being used by 'port'. | ||
537 | */ | ||
538 | static void tilegx_release_port(struct uart_port *port) | ||
539 | { | ||
540 | /* Nothing to release. */ | ||
541 | } | ||
542 | |||
543 | |||
544 | /* | ||
545 | * Request the resources being used by 'port'. | ||
546 | */ | ||
547 | static int tilegx_request_port(struct uart_port *port) | ||
548 | { | ||
549 | /* Always present. */ | ||
550 | return 0; | ||
551 | } | ||
552 | |||
553 | |||
554 | /* | ||
555 | * Configure/autoconfigure the port. | ||
556 | */ | ||
557 | static void tilegx_config_port(struct uart_port *port, int flags) | ||
558 | { | ||
559 | if (flags & UART_CONFIG_TYPE) | ||
560 | port->type = PORT_TILEGX; | ||
561 | } | ||
562 | |||
563 | |||
564 | /* | ||
565 | * Verify the new serial_struct (for TIOCSSERIAL). | ||
566 | */ | ||
567 | static int tilegx_verify_port(struct uart_port *port, | ||
568 | struct serial_struct *ser) | ||
569 | { | ||
570 | if ((ser->type != PORT_UNKNOWN) && (ser->type != PORT_TILEGX)) | ||
571 | return -EINVAL; | ||
572 | |||
573 | return 0; | ||
574 | } | ||
575 | |||
576 | #ifdef CONFIG_CONSOLE_POLL | ||
577 | |||
578 | /* | ||
579 | * Console polling routines for writing and reading from the uart while | ||
580 | * in an interrupt or debug context. | ||
581 | */ | ||
582 | |||
583 | static int tilegx_poll_get_char(struct uart_port *port) | ||
584 | { | ||
585 | UART_FIFO_COUNT_t count; | ||
586 | gxio_uart_context_t *context; | ||
587 | struct tile_uart_port *tile_uart; | ||
588 | |||
589 | tile_uart = container_of(port, struct tile_uart_port, uart); | ||
590 | context = &tile_uart->context; | ||
591 | count.word = gxio_uart_read(context, UART_FIFO_COUNT); | ||
592 | if (count.rfifo_count == 0) | ||
593 | return NO_POLL_CHAR; | ||
594 | return (char)gxio_uart_read(context, UART_RECEIVE_DATA); | ||
595 | } | ||
596 | |||
597 | static void tilegx_poll_put_char(struct uart_port *port, unsigned char c) | ||
598 | { | ||
599 | gxio_uart_context_t *context; | ||
600 | struct tile_uart_port *tile_uart; | ||
601 | |||
602 | tile_uart = container_of(port, struct tile_uart_port, uart); | ||
603 | context = &tile_uart->context; | ||
604 | gxio_uart_write(context, UART_TRANSMIT_DATA, (unsigned long)c); | ||
605 | } | ||
606 | |||
607 | #endif /* CONFIG_CONSOLE_POLL */ | ||
608 | |||
609 | |||
610 | static const struct uart_ops tilegx_ops = { | ||
611 | .tx_empty = tilegx_tx_empty, | ||
612 | .set_mctrl = tilegx_set_mctrl, | ||
613 | .get_mctrl = tilegx_get_mctrl, | ||
614 | .stop_tx = tilegx_stop_tx, | ||
615 | .start_tx = tilegx_start_tx, | ||
616 | .stop_rx = tilegx_stop_rx, | ||
617 | .enable_ms = tilegx_enable_ms, | ||
618 | .break_ctl = tilegx_break_ctl, | ||
619 | .startup = tilegx_startup, | ||
620 | .shutdown = tilegx_shutdown, | ||
621 | .flush_buffer = tilegx_flush_buffer, | ||
622 | .set_termios = tilegx_set_termios, | ||
623 | .type = tilegx_type, | ||
624 | .release_port = tilegx_release_port, | ||
625 | .request_port = tilegx_request_port, | ||
626 | .config_port = tilegx_config_port, | ||
627 | .verify_port = tilegx_verify_port, | ||
628 | #ifdef CONFIG_CONSOLE_POLL | ||
629 | .poll_get_char = tilegx_poll_get_char, | ||
630 | .poll_put_char = tilegx_poll_put_char, | ||
631 | #endif | ||
632 | }; | ||
633 | |||
634 | |||
635 | static void tilegx_init_ports(void) | ||
636 | { | ||
637 | int i; | ||
638 | struct uart_port *port; | ||
639 | |||
640 | for (i = 0; i < TILEGX_UART_NR; i++) { | ||
641 | port = &tile_uart_ports[i].uart; | ||
642 | port->ops = &tilegx_ops; | ||
643 | port->line = i; | ||
644 | port->type = PORT_TILEGX; | ||
645 | port->uartclk = TILEGX_UART_REF_CLK; | ||
646 | port->flags = UPF_BOOT_AUTOCONF; | ||
647 | |||
648 | tile_uart_ports[i].context.fd = -1; | ||
649 | mutex_init(&tile_uart_ports[i].mutex); | ||
650 | } | ||
651 | } | ||
652 | |||
653 | |||
654 | static struct uart_driver tilegx_uart_driver = { | ||
655 | .owner = THIS_MODULE, | ||
656 | .driver_name = DRIVER_NAME_STRING, | ||
657 | .dev_name = TILEGX_UART_NAME, | ||
658 | .major = TILEGX_UART_MAJOR, | ||
659 | .minor = TILEGX_UART_MINOR, | ||
660 | .nr = TILEGX_UART_NR, | ||
661 | }; | ||
662 | |||
663 | |||
664 | static int __init tilegx_init(void) | ||
665 | { | ||
666 | int i; | ||
667 | int ret; | ||
668 | struct tty_driver *tty_drv; | ||
669 | |||
670 | ret = uart_register_driver(&tilegx_uart_driver); | ||
671 | if (ret) | ||
672 | return ret; | ||
673 | tty_drv = tilegx_uart_driver.tty_driver; | ||
674 | tty_drv->init_termios.c_cflag = B115200 | CS8 | CREAD | HUPCL | CLOCAL; | ||
675 | tty_drv->init_termios.c_ispeed = 115200; | ||
676 | tty_drv->init_termios.c_ospeed = 115200; | ||
677 | |||
678 | tilegx_init_ports(); | ||
679 | |||
680 | for (i = 0; i < TILEGX_UART_NR; i++) { | ||
681 | struct uart_port *port = &tile_uart_ports[i].uart; | ||
682 | ret = uart_add_one_port(&tilegx_uart_driver, port); | ||
683 | } | ||
684 | |||
685 | return 0; | ||
686 | } | ||
687 | |||
688 | |||
689 | static void __exit tilegx_exit(void) | ||
690 | { | ||
691 | int i; | ||
692 | struct uart_port *port; | ||
693 | |||
694 | for (i = 0; i < TILEGX_UART_NR; i++) { | ||
695 | port = &tile_uart_ports[i].uart; | ||
696 | uart_remove_one_port(&tilegx_uart_driver, port); | ||
697 | } | ||
698 | |||
699 | uart_unregister_driver(&tilegx_uart_driver); | ||
700 | } | ||
701 | |||
702 | |||
703 | module_init(tilegx_init); | ||
704 | module_exit(tilegx_exit); | ||
705 | |||
706 | MODULE_AUTHOR("Tilera Corporation"); | ||
707 | MODULE_DESCRIPTION("TILEGx serial port driver"); | ||
708 | MODULE_LICENSE("GPL"); | ||