aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/tty
diff options
context:
space:
mode:
authorBaruch Siach <baruch@tkos.co.il>2014-12-18 14:45:24 -0500
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2015-01-09 17:30:16 -0500
commit5930cb3511dfb4392de1180c892b353316ef29ec (patch)
tree058b5cc436f4bbd0c407c4405a74e5665e672031 /drivers/tty
parent48c738631753186e0ec7dd0079beffb3a6f4bb5c (diff)
serial: driver for Conexant Digicolor USART
Signed-off-by: Baruch Siach <baruch@tkos.co.il> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/tty')
-rw-r--r--drivers/tty/serial/Kconfig15
-rw-r--r--drivers/tty/serial/Makefile1
-rw-r--r--drivers/tty/serial/digicolor-usart.c563
3 files changed, 579 insertions, 0 deletions
diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig
index c79b43cd6014..96ec6cfd74e8 100644
--- a/drivers/tty/serial/Kconfig
+++ b/drivers/tty/serial/Kconfig
@@ -1549,6 +1549,21 @@ config SERIAL_FSL_LPUART_CONSOLE
1549 If you have enabled the lpuart serial port on the Freescale SoCs, 1549 If you have enabled the lpuart serial port on the Freescale SoCs,
1550 you can make it the console by answering Y to this option. 1550 you can make it the console by answering Y to this option.
1551 1551
1552config SERIAL_CONEXANT_DIGICOLOR
1553 tristate "Conexant Digicolor CX92xxx USART serial port support"
1554 depends on OF
1555 select SERIAL_CORE
1556 help
1557 Support for the on-chip USART on Conexant Digicolor SoCs.
1558
1559config SERIAL_CONEXANT_DIGICOLOR_CONSOLE
1560 bool "Console on Conexant Digicolor serial port"
1561 depends on SERIAL_CONEXANT_DIGICOLOR=y
1562 select SERIAL_CORE_CONSOLE
1563 help
1564 If you have enabled the USART serial port on Conexant Digicolor
1565 SoCs, you can make it the console by answering Y to this option.
1566
1552config SERIAL_ST_ASC 1567config SERIAL_ST_ASC
1553 tristate "ST ASC serial port support" 1568 tristate "ST ASC serial port support"
1554 select SERIAL_CORE 1569 select SERIAL_CORE
diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile
index 9a548acf5fdc..770a19bb7fcb 100644
--- a/drivers/tty/serial/Makefile
+++ b/drivers/tty/serial/Makefile
@@ -92,6 +92,7 @@ obj-$(CONFIG_SERIAL_EFM32_UART) += efm32-uart.o
92obj-$(CONFIG_SERIAL_ARC) += arc_uart.o 92obj-$(CONFIG_SERIAL_ARC) += arc_uart.o
93obj-$(CONFIG_SERIAL_RP2) += rp2.o 93obj-$(CONFIG_SERIAL_RP2) += rp2.o
94obj-$(CONFIG_SERIAL_FSL_LPUART) += fsl_lpuart.o 94obj-$(CONFIG_SERIAL_FSL_LPUART) += fsl_lpuart.o
95obj-$(CONFIG_SERIAL_CONEXANT_DIGICOLOR) += digicolor-usart.o
95obj-$(CONFIG_SERIAL_MEN_Z135) += men_z135_uart.o 96obj-$(CONFIG_SERIAL_MEN_Z135) += men_z135_uart.o
96 97
97# GPIOLIB helpers for modem control lines 98# GPIOLIB helpers for modem control lines
diff --git a/drivers/tty/serial/digicolor-usart.c b/drivers/tty/serial/digicolor-usart.c
new file mode 100644
index 000000000000..09ce0b3764e2
--- /dev/null
+++ b/drivers/tty/serial/digicolor-usart.c
@@ -0,0 +1,563 @@
1/*
2 * Driver for Conexant Digicolor serial ports (USART)
3 *
4 * Author: Baruch Siach <baruch@tkos.co.il>
5 *
6 * Copyright (C) 2014 Paradox Innovation Ltd.
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 */
13
14#include <linux/module.h>
15#include <linux/console.h>
16#include <linux/serial_core.h>
17#include <linux/serial.h>
18#include <linux/clk.h>
19#include <linux/io.h>
20#include <linux/tty.h>
21#include <linux/tty_flip.h>
22#include <linux/of.h>
23#include <linux/platform_device.h>
24#include <linux/workqueue.h>
25
26#define UA_ENABLE 0x00
27#define UA_ENABLE_ENABLE BIT(0)
28
29#define UA_CONTROL 0x01
30#define UA_CONTROL_RX_ENABLE BIT(0)
31#define UA_CONTROL_TX_ENABLE BIT(1)
32#define UA_CONTROL_SOFT_RESET BIT(2)
33
34#define UA_STATUS 0x02
35#define UA_STATUS_PARITY_ERR BIT(0)
36#define UA_STATUS_FRAME_ERR BIT(1)
37#define UA_STATUS_OVERRUN_ERR BIT(2)
38#define UA_STATUS_TX_READY BIT(6)
39
40#define UA_CONFIG 0x03
41#define UA_CONFIG_CHAR_LEN BIT(0)
42#define UA_CONFIG_STOP_BITS BIT(1)
43#define UA_CONFIG_PARITY BIT(2)
44#define UA_CONFIG_ODD_PARITY BIT(4)
45
46#define UA_EMI_REC 0x04
47
48#define UA_HBAUD_LO 0x08
49#define UA_HBAUD_HI 0x09
50
51#define UA_STATUS_FIFO 0x0a
52#define UA_STATUS_FIFO_RX_EMPTY BIT(2)
53#define UA_STATUS_FIFO_RX_INT_ALMOST BIT(3)
54#define UA_STATUS_FIFO_TX_FULL BIT(4)
55#define UA_STATUS_FIFO_TX_INT_ALMOST BIT(7)
56
57#define UA_CONFIG_FIFO 0x0b
58#define UA_CONFIG_FIFO_RX_THRESH 7
59#define UA_CONFIG_FIFO_RX_FIFO_MODE BIT(3)
60#define UA_CONFIG_FIFO_TX_FIFO_MODE BIT(7)
61
62#define UA_INTFLAG_CLEAR 0x1c
63#define UA_INTFLAG_SET 0x1d
64#define UA_INT_ENABLE 0x1e
65#define UA_INT_STATUS 0x1f
66
67#define UA_INT_TX BIT(0)
68#define UA_INT_RX BIT(1)
69
70#define DIGICOLOR_USART_NR 3
71
72/*
73 * We use the 16 bytes hardware FIFO to buffer Rx traffic. Rx interrupt is
74 * only produced when the FIFO is filled more than a certain configurable
75 * threshold. Unfortunately, there is no way to set this threshold below half
76 * FIFO. This means that we must periodically poll the FIFO status register to
77 * see whether there are waiting Rx bytes.
78 */
79
80struct digicolor_port {
81 struct uart_port port;
82 struct delayed_work rx_poll_work;
83};
84
85static struct uart_port *digicolor_ports[DIGICOLOR_USART_NR];
86
87static bool digicolor_uart_tx_full(struct uart_port *port)
88{
89 return !!(readb_relaxed(port->membase + UA_STATUS_FIFO) &
90 UA_STATUS_FIFO_TX_FULL);
91}
92
93static bool digicolor_uart_rx_empty(struct uart_port *port)
94{
95 return !!(readb_relaxed(port->membase + UA_STATUS_FIFO) &
96 UA_STATUS_FIFO_RX_EMPTY);
97}
98
99static void digicolor_uart_stop_tx(struct uart_port *port)
100{
101 u8 int_enable = readb_relaxed(port->membase + UA_INT_ENABLE);
102
103 int_enable &= ~UA_INT_TX;
104 writeb_relaxed(int_enable, port->membase + UA_INT_ENABLE);
105}
106
107static void digicolor_uart_start_tx(struct uart_port *port)
108{
109 u8 int_enable = readb_relaxed(port->membase + UA_INT_ENABLE);
110
111 int_enable |= UA_INT_TX;
112 writeb_relaxed(int_enable, port->membase + UA_INT_ENABLE);
113}
114
115static void digicolor_uart_stop_rx(struct uart_port *port)
116{
117 u8 int_enable = readb_relaxed(port->membase + UA_INT_ENABLE);
118
119 int_enable &= ~UA_INT_RX;
120 writeb_relaxed(int_enable, port->membase + UA_INT_ENABLE);
121}
122
123static void digicolor_rx_poll(struct work_struct *work)
124{
125 struct digicolor_port *dp =
126 container_of(to_delayed_work(work),
127 struct digicolor_port, rx_poll_work);
128
129 if (!digicolor_uart_rx_empty(&dp->port))
130 /* force RX interrupt */
131 writeb_relaxed(UA_INT_RX, dp->port.membase + UA_INTFLAG_SET);
132
133 schedule_delayed_work(&dp->rx_poll_work, msecs_to_jiffies(100));
134}
135
136static void digicolor_uart_rx(struct uart_port *port)
137{
138 unsigned long flags;
139
140 spin_lock_irqsave(&port->lock, flags);
141
142 while (1) {
143 u8 status, ch;
144 unsigned int ch_flag;
145
146 if (digicolor_uart_rx_empty(port))
147 break;
148
149 ch = readb_relaxed(port->membase + UA_EMI_REC);
150 status = readb_relaxed(port->membase + UA_STATUS);
151
152 port->icount.rx++;
153 ch_flag = TTY_NORMAL;
154
155 if (status) {
156 if (status & UA_STATUS_PARITY_ERR)
157 port->icount.parity++;
158 else if (status & UA_STATUS_FRAME_ERR)
159 port->icount.frame++;
160 else if (status & UA_STATUS_OVERRUN_ERR)
161 port->icount.overrun++;
162
163 status &= port->read_status_mask;
164
165 if (status & UA_STATUS_PARITY_ERR)
166 ch_flag = TTY_PARITY;
167 else if (status & UA_STATUS_FRAME_ERR)
168 ch_flag = TTY_FRAME;
169 else if (status & UA_STATUS_OVERRUN_ERR)
170 ch_flag = TTY_OVERRUN;
171 }
172
173 if (uart_handle_sysrq_char(port, ch))
174 continue;
175
176 if (status & port->ignore_status_mask)
177 continue;
178
179 uart_insert_char(port, status, UA_STATUS_OVERRUN_ERR, ch,
180 ch_flag);
181 }
182
183 spin_unlock_irqrestore(&port->lock, flags);
184
185 tty_flip_buffer_push(&port->state->port);
186}
187
188static void digicolor_uart_tx(struct uart_port *port)
189{
190 struct circ_buf *xmit = &port->state->xmit;
191 unsigned long flags;
192
193 if (digicolor_uart_tx_full(port))
194 return;
195
196 spin_lock_irqsave(&port->lock, flags);
197
198 if (port->x_char) {
199 writeb_relaxed(port->x_char, port->membase + UA_EMI_REC);
200 port->icount.tx++;
201 port->x_char = 0;
202 goto out;
203 }
204
205 if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
206 digicolor_uart_stop_tx(port);
207 goto out;
208 }
209
210 while (!uart_circ_empty(xmit)) {
211 writeb(xmit->buf[xmit->tail], port->membase + UA_EMI_REC);
212 xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
213 port->icount.tx++;
214
215 if (digicolor_uart_tx_full(port))
216 break;
217 }
218
219 if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
220 uart_write_wakeup(port);
221
222out:
223 spin_unlock_irqrestore(&port->lock, flags);
224}
225
226static irqreturn_t digicolor_uart_int(int irq, void *dev_id)
227{
228 struct uart_port *port = dev_id;
229 u8 int_status = readb_relaxed(port->membase + UA_INT_STATUS);
230
231 writeb_relaxed(UA_INT_RX | UA_INT_TX,
232 port->membase + UA_INTFLAG_CLEAR);
233
234 if (int_status & UA_INT_RX)
235 digicolor_uart_rx(port);
236 if (int_status & UA_INT_TX)
237 digicolor_uart_tx(port);
238
239 return IRQ_HANDLED;
240}
241
242static unsigned int digicolor_uart_tx_empty(struct uart_port *port)
243{
244 u8 status = readb_relaxed(port->membase + UA_STATUS);
245
246 return (status & UA_STATUS_TX_READY) ? TIOCSER_TEMT : 0;
247}
248
249static unsigned int digicolor_uart_get_mctrl(struct uart_port *port)
250{
251 return TIOCM_CTS;
252}
253
254static void digicolor_uart_set_mctrl(struct uart_port *port, unsigned int mctrl)
255{
256}
257
258static void digicolor_uart_break_ctl(struct uart_port *port, int state)
259{
260}
261
262static int digicolor_uart_startup(struct uart_port *port)
263{
264 struct digicolor_port *dp =
265 container_of(port, struct digicolor_port, port);
266
267 writeb_relaxed(UA_ENABLE_ENABLE, port->membase + UA_ENABLE);
268 writeb_relaxed(UA_CONTROL_SOFT_RESET, port->membase + UA_CONTROL);
269 writeb_relaxed(0, port->membase + UA_CONTROL);
270
271 writeb_relaxed(UA_CONFIG_FIFO_RX_FIFO_MODE
272 | UA_CONFIG_FIFO_TX_FIFO_MODE | UA_CONFIG_FIFO_RX_THRESH,
273 port->membase + UA_CONFIG_FIFO);
274 writeb_relaxed(UA_STATUS_FIFO_RX_INT_ALMOST,
275 port->membase + UA_STATUS_FIFO);
276 writeb_relaxed(UA_CONTROL_RX_ENABLE | UA_CONTROL_TX_ENABLE,
277 port->membase + UA_CONTROL);
278 writeb_relaxed(UA_INT_TX | UA_INT_RX,
279 port->membase + UA_INT_ENABLE);
280
281 schedule_delayed_work(&dp->rx_poll_work, msecs_to_jiffies(100));
282
283 return 0;
284}
285
286static void digicolor_uart_shutdown(struct uart_port *port)
287{
288 struct digicolor_port *dp =
289 container_of(port, struct digicolor_port, port);
290
291 writeb_relaxed(0, port->membase + UA_ENABLE);
292 cancel_delayed_work_sync(&dp->rx_poll_work);
293}
294
295static void digicolor_uart_set_termios(struct uart_port *port,
296 struct ktermios *termios,
297 struct ktermios *old)
298{
299 unsigned int baud, divisor;
300 u8 config = 0;
301 unsigned long flags;
302
303 /* Mask termios capabilities we don't support */
304 termios->c_cflag &= ~CMSPAR;
305 termios->c_iflag &= ~(BRKINT | IGNBRK);
306
307 /* Limit baud rates so that we don't need the fractional divider */
308 baud = uart_get_baud_rate(port, termios, old,
309 port->uartclk / (0x10000*16),
310 port->uartclk / 256);
311 divisor = uart_get_divisor(port, baud) - 1;
312
313 switch (termios->c_cflag & CSIZE) {
314 case CS7:
315 break;
316 case CS8:
317 default:
318 config |= UA_CONFIG_CHAR_LEN;
319 break;
320 }
321
322 if (termios->c_cflag & CSTOPB)
323 config |= UA_CONFIG_STOP_BITS;
324
325 if (termios->c_cflag & PARENB) {
326 config |= UA_CONFIG_PARITY;
327 if (termios->c_cflag & PARODD)
328 config |= UA_CONFIG_ODD_PARITY;
329 }
330
331 /* Set read status mask */
332 port->read_status_mask = UA_STATUS_OVERRUN_ERR;
333 if (termios->c_iflag & INPCK)
334 port->read_status_mask |= UA_STATUS_PARITY_ERR
335 | UA_STATUS_FRAME_ERR;
336
337 /* Set status ignore mask */
338 port->ignore_status_mask = 0;
339 if (!(termios->c_cflag & CREAD))
340 port->ignore_status_mask |= UA_STATUS_OVERRUN_ERR
341 | UA_STATUS_PARITY_ERR | UA_STATUS_FRAME_ERR;
342
343 spin_lock_irqsave(&port->lock, flags);
344
345 uart_update_timeout(port, termios->c_cflag, baud);
346
347 writeb_relaxed(config, port->membase + UA_CONFIG);
348 writeb_relaxed(divisor & 0xff, port->membase + UA_HBAUD_LO);
349 writeb_relaxed(divisor >> 8, port->membase + UA_HBAUD_HI);
350
351 spin_unlock_irqrestore(&port->lock, flags);
352}
353
354static const char *digicolor_uart_type(struct uart_port *port)
355{
356 return (port->type == PORT_DIGICOLOR) ? "DIGICOLOR USART" : NULL;
357}
358
359static void digicolor_uart_config_port(struct uart_port *port, int flags)
360{
361 if (flags & UART_CONFIG_TYPE)
362 port->type = PORT_DIGICOLOR;
363}
364
365static void digicolor_uart_release_port(struct uart_port *port)
366{
367}
368
369static int digicolor_uart_request_port(struct uart_port *port)
370{
371 return 0;
372}
373
374static const struct uart_ops digicolor_uart_ops = {
375 .tx_empty = digicolor_uart_tx_empty,
376 .set_mctrl = digicolor_uart_set_mctrl,
377 .get_mctrl = digicolor_uart_get_mctrl,
378 .stop_tx = digicolor_uart_stop_tx,
379 .start_tx = digicolor_uart_start_tx,
380 .stop_rx = digicolor_uart_stop_rx,
381 .break_ctl = digicolor_uart_break_ctl,
382 .startup = digicolor_uart_startup,
383 .shutdown = digicolor_uart_shutdown,
384 .set_termios = digicolor_uart_set_termios,
385 .type = digicolor_uart_type,
386 .config_port = digicolor_uart_config_port,
387 .release_port = digicolor_uart_release_port,
388 .request_port = digicolor_uart_request_port,
389};
390
391static void digicolor_uart_console_putchar(struct uart_port *port, int ch)
392{
393 while (digicolor_uart_tx_full(port))
394 cpu_relax();
395
396 writeb_relaxed(ch, port->membase + UA_EMI_REC);
397}
398
399static void digicolor_uart_console_write(struct console *co, const char *c,
400 unsigned n)
401{
402 struct uart_port *port = digicolor_ports[co->index];
403 u8 status;
404 unsigned long flags;
405 int locked = 1;
406
407 if (port->sysrq || oops_in_progress)
408 locked = spin_trylock_irqsave(&port->lock, flags);
409 else
410 spin_lock_irqsave(&port->lock, flags);
411
412 uart_console_write(port, c, n, digicolor_uart_console_putchar);
413
414 if (locked)
415 spin_unlock_irqrestore(&port->lock, flags);
416
417 /* Wait for transmitter to become empty */
418 do {
419 status = readb_relaxed(port->membase + UA_STATUS);
420 } while ((status & UA_STATUS_TX_READY) == 0);
421}
422
423static int digicolor_uart_console_setup(struct console *co, char *options)
424{
425 int baud = 115200, bits = 8, parity = 'n', flow = 'n';
426 struct uart_port *port;
427
428 if (co->index < 0 || co->index >= DIGICOLOR_USART_NR)
429 return -EINVAL;
430
431 port = digicolor_ports[co->index];
432 if (!port)
433 return -ENODEV;
434
435 if (options)
436 uart_parse_options(options, &baud, &parity, &bits, &flow);
437
438 return uart_set_options(port, co, baud, parity, bits, flow);
439}
440
441static struct console digicolor_console = {
442 .name = "ttyS",
443 .device = uart_console_device,
444 .write = digicolor_uart_console_write,
445 .setup = digicolor_uart_console_setup,
446 .flags = CON_PRINTBUFFER,
447 .index = -1,
448};
449
450static struct uart_driver digicolor_uart = {
451 .driver_name = "digicolor-usart",
452 .dev_name = "ttyS",
453 .nr = DIGICOLOR_USART_NR,
454};
455
456static int digicolor_uart_probe(struct platform_device *pdev)
457{
458 struct device_node *np = pdev->dev.of_node;
459 int ret, index;
460 struct digicolor_port *dp;
461 struct resource *res;
462 struct clk *uart_clk;
463
464 if (!np) {
465 dev_err(&pdev->dev, "Missing device tree node\n");
466 return -ENXIO;
467 }
468
469 index = of_alias_get_id(np, "serial");
470 if (index < 0 || index >= DIGICOLOR_USART_NR)
471 return -EINVAL;
472
473 dp = devm_kzalloc(&pdev->dev, sizeof(*dp), GFP_KERNEL);
474 if (!dp)
475 return -ENOMEM;
476
477 uart_clk = devm_clk_get(&pdev->dev, NULL);
478 if (IS_ERR(uart_clk))
479 return PTR_ERR(uart_clk);
480
481 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
482 dp->port.mapbase = res->start;
483 dp->port.membase = devm_ioremap_resource(&pdev->dev, res);
484 if (IS_ERR(dp->port.membase))
485 return PTR_ERR(dp->port.membase);
486
487 dp->port.irq = platform_get_irq(pdev, 0);
488 if (IS_ERR_VALUE(dp->port.irq))
489 return dp->port.irq;
490
491 dp->port.iotype = UPIO_MEM;
492 dp->port.uartclk = clk_get_rate(uart_clk);
493 dp->port.fifosize = 16;
494 dp->port.dev = &pdev->dev;
495 dp->port.ops = &digicolor_uart_ops;
496 dp->port.line = index;
497 dp->port.type = PORT_DIGICOLOR;
498 spin_lock_init(&dp->port.lock);
499
500 digicolor_ports[index] = &dp->port;
501 platform_set_drvdata(pdev, &dp->port);
502
503 INIT_DELAYED_WORK(&dp->rx_poll_work, digicolor_rx_poll);
504
505 ret = devm_request_irq(&pdev->dev, dp->port.irq, digicolor_uart_int, 0,
506 dev_name(&pdev->dev), &dp->port);
507 if (ret)
508 return ret;
509
510 return uart_add_one_port(&digicolor_uart, &dp->port);
511}
512
513static int digicolor_uart_remove(struct platform_device *pdev)
514{
515 struct uart_port *port = platform_get_drvdata(pdev);
516
517 uart_remove_one_port(&digicolor_uart, port);
518
519 return 0;
520}
521
522static const struct of_device_id digicolor_uart_dt_ids[] = {
523 { .compatible = "cnxt,cx92755-usart", },
524 { }
525};
526MODULE_DEVICE_TABLE(of, digicolor_uart_dt_ids);
527
528static struct platform_driver digicolor_uart_platform = {
529 .driver = {
530 .name = "digicolor-usart",
531 .of_match_table = of_match_ptr(digicolor_uart_dt_ids),
532 },
533 .probe = digicolor_uart_probe,
534 .remove = digicolor_uart_remove,
535};
536
537static int __init digicolor_uart_init(void)
538{
539 int ret;
540
541 if (IS_ENABLED(CONFIG_SERIAL_CONEXANT_DIGICOLOR_CONSOLE)) {
542 digicolor_uart.cons = &digicolor_console;
543 digicolor_console.data = &digicolor_uart;
544 }
545
546 ret = uart_register_driver(&digicolor_uart);
547 if (ret)
548 return ret;
549
550 return platform_driver_register(&digicolor_uart_platform);
551}
552module_init(digicolor_uart_init);
553
554static void __exit digicolor_uart_exit(void)
555{
556 platform_driver_unregister(&digicolor_uart_platform);
557 uart_unregister_driver(&digicolor_uart);
558}
559module_exit(digicolor_uart_exit);
560
561MODULE_AUTHOR("Baruch Siach <baruch@tkos.co.il>");
562MODULE_DESCRIPTION("Conexant Digicolor USART serial driver");
563MODULE_LICENSE("GPL");