diff options
author | Fabian Godehardt <fg@emlix.com> | 2009-06-11 09:53:18 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2009-06-11 11:51:08 -0400 |
commit | b6e4913834cd032082e5c76dfade61050212dc98 (patch) | |
tree | 21f7c1b454ced01895ee121c4980a54d7e0e02f9 /drivers/serial/imx.c | |
parent | 534fca068ec8063ec8b67626b3eb34ba6ec86967 (diff) |
imx: serial: add IrDA support to serial driver
Using the iMX serial driver with an IrDA device
needs extra peripheral settings and specific
timing depending on the transmitter circuitry used.
Signed-off-by: Fabian Godehardt <fg@emlix.com>
Signed-off-by: Oskar Schirmer <os@emlix.com>
Signed-off-by: Alan Cox <alan@linux.intel.com>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'drivers/serial/imx.c')
-rw-r--r-- | drivers/serial/imx.c | 195 |
1 files changed, 176 insertions, 19 deletions
diff --git a/drivers/serial/imx.c b/drivers/serial/imx.c index 0de81f71f884..8c79e8c2fd4e 100644 --- a/drivers/serial/imx.c +++ b/drivers/serial/imx.c | |||
@@ -8,6 +8,9 @@ | |||
8 | * Author: Sascha Hauer <sascha@saschahauer.de> | 8 | * Author: Sascha Hauer <sascha@saschahauer.de> |
9 | * Copyright (C) 2004 Pengutronix | 9 | * Copyright (C) 2004 Pengutronix |
10 | * | 10 | * |
11 | * Copyright (C) 2009 emlix GmbH | ||
12 | * Author: Fabian Godehardt (added IrDA support for iMX) | ||
13 | * | ||
11 | * This program is free software; you can redistribute it and/or modify | 14 | * This program is free software; you can redistribute it and/or modify |
12 | * it under the terms of the GNU General Public License as published by | 15 | * it under the terms of the GNU General Public License as published by |
13 | * the Free Software Foundation; either version 2 of the License, or | 16 | * the Free Software Foundation; either version 2 of the License, or |
@@ -41,6 +44,7 @@ | |||
41 | #include <linux/serial_core.h> | 44 | #include <linux/serial_core.h> |
42 | #include <linux/serial.h> | 45 | #include <linux/serial.h> |
43 | #include <linux/clk.h> | 46 | #include <linux/clk.h> |
47 | #include <linux/delay.h> | ||
44 | #include <linux/rational.h> | 48 | #include <linux/rational.h> |
45 | 49 | ||
46 | #include <asm/io.h> | 50 | #include <asm/io.h> |
@@ -149,6 +153,7 @@ | |||
149 | #define UCR4_DREN (1<<0) /* Recv data ready interrupt enable */ | 153 | #define UCR4_DREN (1<<0) /* Recv data ready interrupt enable */ |
150 | #define UFCR_RXTL_SHF 0 /* Receiver trigger level shift */ | 154 | #define UFCR_RXTL_SHF 0 /* Receiver trigger level shift */ |
151 | #define UFCR_RFDIV (7<<7) /* Reference freq divider mask */ | 155 | #define UFCR_RFDIV (7<<7) /* Reference freq divider mask */ |
156 | #define UFCR_RFDIV_REG(x) (((x) < 7 ? 6 - (x) : 6) << 7) | ||
152 | #define UFCR_TXTL_SHF 10 /* Transmitter trigger level shift */ | 157 | #define UFCR_TXTL_SHF 10 /* Transmitter trigger level shift */ |
153 | #define USR1_PARITYERR (1<<15) /* Parity error interrupt flag */ | 158 | #define USR1_PARITYERR (1<<15) /* Parity error interrupt flag */ |
154 | #define USR1_RTSS (1<<14) /* RTS pin status */ | 159 | #define USR1_RTSS (1<<14) /* RTS pin status */ |
@@ -213,9 +218,19 @@ struct imx_port { | |||
213 | unsigned int old_status; | 218 | unsigned int old_status; |
214 | int txirq,rxirq,rtsirq; | 219 | int txirq,rxirq,rtsirq; |
215 | unsigned int have_rtscts:1; | 220 | unsigned int have_rtscts:1; |
221 | unsigned int use_irda:1; | ||
222 | unsigned int irda_inv_rx:1; | ||
223 | unsigned int irda_inv_tx:1; | ||
224 | unsigned short trcv_delay; /* transceiver delay */ | ||
216 | struct clk *clk; | 225 | struct clk *clk; |
217 | }; | 226 | }; |
218 | 227 | ||
228 | #ifdef CONFIG_IRDA | ||
229 | #define USE_IRDA(sport) ((sport)->use_irda) | ||
230 | #else | ||
231 | #define USE_IRDA(sport) (0) | ||
232 | #endif | ||
233 | |||
219 | /* | 234 | /* |
220 | * Handle any change of modem status signal since we were last called. | 235 | * Handle any change of modem status signal since we were last called. |
221 | */ | 236 | */ |
@@ -269,6 +284,48 @@ static void imx_stop_tx(struct uart_port *port) | |||
269 | struct imx_port *sport = (struct imx_port *)port; | 284 | struct imx_port *sport = (struct imx_port *)port; |
270 | unsigned long temp; | 285 | unsigned long temp; |
271 | 286 | ||
287 | if (USE_IRDA(sport)) { | ||
288 | /* half duplex - wait for end of transmission */ | ||
289 | int n = 256; | ||
290 | while ((--n > 0) && | ||
291 | !(readl(sport->port.membase + USR2) & USR2_TXDC)) { | ||
292 | udelay(5); | ||
293 | barrier(); | ||
294 | } | ||
295 | /* | ||
296 | * irda transceiver - wait a bit more to avoid | ||
297 | * cutoff, hardware dependent | ||
298 | */ | ||
299 | udelay(sport->trcv_delay); | ||
300 | |||
301 | /* | ||
302 | * half duplex - reactivate receive mode, | ||
303 | * flush receive pipe echo crap | ||
304 | */ | ||
305 | if (readl(sport->port.membase + USR2) & USR2_TXDC) { | ||
306 | temp = readl(sport->port.membase + UCR1); | ||
307 | temp &= ~(UCR1_TXMPTYEN | UCR1_TRDYEN); | ||
308 | writel(temp, sport->port.membase + UCR1); | ||
309 | |||
310 | temp = readl(sport->port.membase + UCR4); | ||
311 | temp &= ~(UCR4_TCEN); | ||
312 | writel(temp, sport->port.membase + UCR4); | ||
313 | |||
314 | while (readl(sport->port.membase + URXD0) & | ||
315 | URXD_CHARRDY) | ||
316 | barrier(); | ||
317 | |||
318 | temp = readl(sport->port.membase + UCR1); | ||
319 | temp |= UCR1_RRDYEN; | ||
320 | writel(temp, sport->port.membase + UCR1); | ||
321 | |||
322 | temp = readl(sport->port.membase + UCR4); | ||
323 | temp |= UCR4_DREN; | ||
324 | writel(temp, sport->port.membase + UCR4); | ||
325 | } | ||
326 | return; | ||
327 | } | ||
328 | |||
272 | temp = readl(sport->port.membase + UCR1); | 329 | temp = readl(sport->port.membase + UCR1); |
273 | writel(temp & ~UCR1_TXMPTYEN, sport->port.membase + UCR1); | 330 | writel(temp & ~UCR1_TXMPTYEN, sport->port.membase + UCR1); |
274 | } | 331 | } |
@@ -324,9 +381,30 @@ static void imx_start_tx(struct uart_port *port) | |||
324 | struct imx_port *sport = (struct imx_port *)port; | 381 | struct imx_port *sport = (struct imx_port *)port; |
325 | unsigned long temp; | 382 | unsigned long temp; |
326 | 383 | ||
384 | if (USE_IRDA(sport)) { | ||
385 | /* half duplex in IrDA mode; have to disable receive mode */ | ||
386 | temp = readl(sport->port.membase + UCR4); | ||
387 | temp &= ~(UCR4_DREN); | ||
388 | writel(temp, sport->port.membase + UCR4); | ||
389 | |||
390 | temp = readl(sport->port.membase + UCR1); | ||
391 | temp &= ~(UCR1_RRDYEN); | ||
392 | writel(temp, sport->port.membase + UCR1); | ||
393 | } | ||
394 | |||
327 | temp = readl(sport->port.membase + UCR1); | 395 | temp = readl(sport->port.membase + UCR1); |
328 | writel(temp | UCR1_TXMPTYEN, sport->port.membase + UCR1); | 396 | writel(temp | UCR1_TXMPTYEN, sport->port.membase + UCR1); |
329 | 397 | ||
398 | if (USE_IRDA(sport)) { | ||
399 | temp = readl(sport->port.membase + UCR1); | ||
400 | temp |= UCR1_TRDYEN; | ||
401 | writel(temp, sport->port.membase + UCR1); | ||
402 | |||
403 | temp = readl(sport->port.membase + UCR4); | ||
404 | temp |= UCR4_TCEN; | ||
405 | writel(temp, sport->port.membase + UCR4); | ||
406 | } | ||
407 | |||
330 | if (readl(sport->port.membase + UTS) & UTS_TXEMPTY) | 408 | if (readl(sport->port.membase + UTS) & UTS_TXEMPTY) |
331 | imx_transmit_buffer(sport); | 409 | imx_transmit_buffer(sport); |
332 | } | 410 | } |
@@ -536,12 +614,7 @@ static int imx_setup_ufcr(struct imx_port *sport, unsigned int mode) | |||
536 | if(!ufcr_rfdiv) | 614 | if(!ufcr_rfdiv) |
537 | ufcr_rfdiv = 1; | 615 | ufcr_rfdiv = 1; |
538 | 616 | ||
539 | if(ufcr_rfdiv >= 7) | 617 | val |= UFCR_RFDIV_REG(ufcr_rfdiv); |
540 | ufcr_rfdiv = 6; | ||
541 | else | ||
542 | ufcr_rfdiv = 6 - ufcr_rfdiv; | ||
543 | |||
544 | val |= UFCR_RFDIV & (ufcr_rfdiv << 7); | ||
545 | 618 | ||
546 | writel(val, sport->port.membase + UFCR); | 619 | writel(val, sport->port.membase + UFCR); |
547 | 620 | ||
@@ -560,8 +633,24 @@ static int imx_startup(struct uart_port *port) | |||
560 | * requesting IRQs | 633 | * requesting IRQs |
561 | */ | 634 | */ |
562 | temp = readl(sport->port.membase + UCR4); | 635 | temp = readl(sport->port.membase + UCR4); |
636 | |||
637 | if (USE_IRDA(sport)) | ||
638 | temp |= UCR4_IRSC; | ||
639 | |||
563 | writel(temp & ~UCR4_DREN, sport->port.membase + UCR4); | 640 | writel(temp & ~UCR4_DREN, sport->port.membase + UCR4); |
564 | 641 | ||
642 | if (USE_IRDA(sport)) { | ||
643 | /* reset fifo's and state machines */ | ||
644 | int i = 100; | ||
645 | temp = readl(sport->port.membase + UCR2); | ||
646 | temp &= ~UCR2_SRST; | ||
647 | writel(temp, sport->port.membase + UCR2); | ||
648 | while (!(readl(sport->port.membase + UCR2) & UCR2_SRST) && | ||
649 | (--i > 0)) { | ||
650 | udelay(1); | ||
651 | } | ||
652 | } | ||
653 | |||
565 | /* | 654 | /* |
566 | * Allocate the IRQ(s) i.MX1 has three interrupts whereas later | 655 | * Allocate the IRQ(s) i.MX1 has three interrupts whereas later |
567 | * chips only have one interrupt. | 656 | * chips only have one interrupt. |
@@ -577,12 +666,16 @@ static int imx_startup(struct uart_port *port) | |||
577 | if (retval) | 666 | if (retval) |
578 | goto error_out2; | 667 | goto error_out2; |
579 | 668 | ||
580 | retval = request_irq(sport->rtsirq, imx_rtsint, | 669 | /* do not use RTS IRQ on IrDA */ |
581 | (sport->rtsirq < MAX_INTERNAL_IRQ) ? 0 : | 670 | if (!USE_IRDA(sport)) { |
582 | IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, | 671 | retval = request_irq(sport->rtsirq, imx_rtsint, |
583 | DRIVER_NAME, sport); | 672 | (sport->rtsirq < MAX_INTERNAL_IRQ) ? 0 : |
584 | if (retval) | 673 | IRQF_TRIGGER_FALLING | |
585 | goto error_out3; | 674 | IRQF_TRIGGER_RISING, |
675 | DRIVER_NAME, sport); | ||
676 | if (retval) | ||
677 | goto error_out3; | ||
678 | } | ||
586 | } else { | 679 | } else { |
587 | retval = request_irq(sport->port.irq, imx_int, 0, | 680 | retval = request_irq(sport->port.irq, imx_int, 0, |
588 | DRIVER_NAME, sport); | 681 | DRIVER_NAME, sport); |
@@ -599,18 +692,49 @@ static int imx_startup(struct uart_port *port) | |||
599 | 692 | ||
600 | temp = readl(sport->port.membase + UCR1); | 693 | temp = readl(sport->port.membase + UCR1); |
601 | temp |= UCR1_RRDYEN | UCR1_RTSDEN | UCR1_UARTEN; | 694 | temp |= UCR1_RRDYEN | UCR1_RTSDEN | UCR1_UARTEN; |
695 | |||
696 | if (USE_IRDA(sport)) { | ||
697 | temp |= UCR1_IREN; | ||
698 | temp &= ~(UCR1_RTSDEN); | ||
699 | } | ||
700 | |||
602 | writel(temp, sport->port.membase + UCR1); | 701 | writel(temp, sport->port.membase + UCR1); |
603 | 702 | ||
604 | temp = readl(sport->port.membase + UCR2); | 703 | temp = readl(sport->port.membase + UCR2); |
605 | temp |= (UCR2_RXEN | UCR2_TXEN); | 704 | temp |= (UCR2_RXEN | UCR2_TXEN); |
606 | writel(temp, sport->port.membase + UCR2); | 705 | writel(temp, sport->port.membase + UCR2); |
607 | 706 | ||
707 | if (USE_IRDA(sport)) { | ||
708 | /* clear RX-FIFO */ | ||
709 | int i = 64; | ||
710 | while ((--i > 0) && | ||
711 | (readl(sport->port.membase + URXD0) & URXD_CHARRDY)) { | ||
712 | barrier(); | ||
713 | } | ||
714 | } | ||
715 | |||
608 | #if defined CONFIG_ARCH_MX2 || defined CONFIG_ARCH_MX3 | 716 | #if defined CONFIG_ARCH_MX2 || defined CONFIG_ARCH_MX3 |
609 | temp = readl(sport->port.membase + UCR3); | 717 | temp = readl(sport->port.membase + UCR3); |
610 | temp |= UCR3_RXDMUXSEL; | 718 | temp |= UCR3_RXDMUXSEL; |
611 | writel(temp, sport->port.membase + UCR3); | 719 | writel(temp, sport->port.membase + UCR3); |
612 | #endif | 720 | #endif |
613 | 721 | ||
722 | if (USE_IRDA(sport)) { | ||
723 | temp = readl(sport->port.membase + UCR4); | ||
724 | if (sport->irda_inv_rx) | ||
725 | temp |= UCR4_INVR; | ||
726 | else | ||
727 | temp &= ~(UCR4_INVR); | ||
728 | writel(temp | UCR4_DREN, sport->port.membase + UCR4); | ||
729 | |||
730 | temp = readl(sport->port.membase + UCR3); | ||
731 | if (sport->irda_inv_tx) | ||
732 | temp |= UCR3_INVT; | ||
733 | else | ||
734 | temp &= ~(UCR3_INVT); | ||
735 | writel(temp, sport->port.membase + UCR3); | ||
736 | } | ||
737 | |||
614 | /* | 738 | /* |
615 | * Enable modem status interrupts | 739 | * Enable modem status interrupts |
616 | */ | 740 | */ |
@@ -618,6 +742,16 @@ static int imx_startup(struct uart_port *port) | |||
618 | imx_enable_ms(&sport->port); | 742 | imx_enable_ms(&sport->port); |
619 | spin_unlock_irqrestore(&sport->port.lock,flags); | 743 | spin_unlock_irqrestore(&sport->port.lock,flags); |
620 | 744 | ||
745 | if (USE_IRDA(sport)) { | ||
746 | struct imxuart_platform_data *pdata; | ||
747 | pdata = sport->port.dev->platform_data; | ||
748 | sport->irda_inv_rx = pdata->irda_inv_rx; | ||
749 | sport->irda_inv_tx = pdata->irda_inv_tx; | ||
750 | sport->trcv_delay = pdata->transceiver_delay; | ||
751 | if (pdata->irda_enable) | ||
752 | pdata->irda_enable(1); | ||
753 | } | ||
754 | |||
621 | return 0; | 755 | return 0; |
622 | 756 | ||
623 | error_out3: | 757 | error_out3: |
@@ -639,6 +773,13 @@ static void imx_shutdown(struct uart_port *port) | |||
639 | temp &= ~(UCR2_TXEN); | 773 | temp &= ~(UCR2_TXEN); |
640 | writel(temp, sport->port.membase + UCR2); | 774 | writel(temp, sport->port.membase + UCR2); |
641 | 775 | ||
776 | if (USE_IRDA(sport)) { | ||
777 | struct imxuart_platform_data *pdata; | ||
778 | pdata = sport->port.dev->platform_data; | ||
779 | if (pdata->irda_enable) | ||
780 | pdata->irda_enable(0); | ||
781 | } | ||
782 | |||
642 | /* | 783 | /* |
643 | * Stop our timer. | 784 | * Stop our timer. |
644 | */ | 785 | */ |
@@ -648,7 +789,8 @@ static void imx_shutdown(struct uart_port *port) | |||
648 | * Free the interrupts | 789 | * Free the interrupts |
649 | */ | 790 | */ |
650 | if (sport->txirq > 0) { | 791 | if (sport->txirq > 0) { |
651 | free_irq(sport->rtsirq, sport); | 792 | if (!USE_IRDA(sport)) |
793 | free_irq(sport->rtsirq, sport); | ||
652 | free_irq(sport->txirq, sport); | 794 | free_irq(sport->txirq, sport); |
653 | free_irq(sport->rxirq, sport); | 795 | free_irq(sport->rxirq, sport); |
654 | } else | 796 | } else |
@@ -660,6 +802,9 @@ static void imx_shutdown(struct uart_port *port) | |||
660 | 802 | ||
661 | temp = readl(sport->port.membase + UCR1); | 803 | temp = readl(sport->port.membase + UCR1); |
662 | temp &= ~(UCR1_TXMPTYEN | UCR1_RRDYEN | UCR1_RTSDEN | UCR1_UARTEN); | 804 | temp &= ~(UCR1_TXMPTYEN | UCR1_RRDYEN | UCR1_RTSDEN | UCR1_UARTEN); |
805 | if (USE_IRDA(sport)) | ||
806 | temp &= ~(UCR1_IREN); | ||
807 | |||
663 | writel(temp, sport->port.membase + UCR1); | 808 | writel(temp, sport->port.membase + UCR1); |
664 | } | 809 | } |
665 | 810 | ||
@@ -768,11 +913,19 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios, | |||
768 | sport->port.membase + UCR2); | 913 | sport->port.membase + UCR2); |
769 | old_txrxen &= (UCR2_TXEN | UCR2_RXEN); | 914 | old_txrxen &= (UCR2_TXEN | UCR2_RXEN); |
770 | 915 | ||
771 | div = sport->port.uartclk / (baud * 16); | 916 | if (USE_IRDA(sport)) { |
772 | if (div > 7) | 917 | /* |
773 | div = 7; | 918 | * use maximum available submodule frequency to |
774 | if (!div) | 919 | * avoid missing short pulses due to low sampling rate |
920 | */ | ||
775 | div = 1; | 921 | div = 1; |
922 | } else { | ||
923 | div = sport->port.uartclk / (baud * 16); | ||
924 | if (div > 7) | ||
925 | div = 7; | ||
926 | if (!div) | ||
927 | div = 1; | ||
928 | } | ||
776 | 929 | ||
777 | rational_best_approximation(16 * div * baud, sport->port.uartclk, | 930 | rational_best_approximation(16 * div * baud, sport->port.uartclk, |
778 | 1 << 16, 1 << 16, &num, &denom); | 931 | 1 << 16, 1 << 16, &num, &denom); |
@@ -781,8 +934,7 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios, | |||
781 | denom -= 1; | 934 | denom -= 1; |
782 | 935 | ||
783 | ufcr = readl(sport->port.membase + UFCR); | 936 | ufcr = readl(sport->port.membase + UFCR); |
784 | ufcr = (ufcr & (~UFCR_RFDIV)) | | 937 | ufcr = (ufcr & (~UFCR_RFDIV)) | UFCR_RFDIV_REG(div); |
785 | (div << 7); | ||
786 | writel(ufcr, sport->port.membase + UFCR); | 938 | writel(ufcr, sport->port.membase + UFCR); |
787 | 939 | ||
788 | writel(num, sport->port.membase + UBIR); | 940 | writel(num, sport->port.membase + UBIR); |
@@ -1141,6 +1293,11 @@ static int serial_imx_probe(struct platform_device *pdev) | |||
1141 | if (pdata && (pdata->flags & IMXUART_HAVE_RTSCTS)) | 1293 | if (pdata && (pdata->flags & IMXUART_HAVE_RTSCTS)) |
1142 | sport->have_rtscts = 1; | 1294 | sport->have_rtscts = 1; |
1143 | 1295 | ||
1296 | #ifdef CONFIG_IRDA | ||
1297 | if (pdata && (pdata->flags & IMXUART_IRDA)) | ||
1298 | sport->use_irda = 1; | ||
1299 | #endif | ||
1300 | |||
1144 | if (pdata->init) { | 1301 | if (pdata->init) { |
1145 | ret = pdata->init(pdev); | 1302 | ret = pdata->init(pdev); |
1146 | if (ret) | 1303 | if (ret) |