diff options
author | Joachim Eastwood <manabian@gmail.com> | 2015-05-30 17:14:44 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2015-05-31 17:56:33 -0400 |
commit | d2aef35a5cc543292dae7130734c390f2a251f73 (patch) | |
tree | 4e5e69cc33e0fc6d681e1898c819331032595397 | |
parent | 1a8d2903cb6a92ce47cfc8841951b8227c09e505 (diff) |
serial: 8250: add LPC18xx/43xx UART driver
Serial port driver for the 8250-based UART found on LPC18xx/43xx
devices. The UART is 16550A compatible with additional features
like RS485 support, synchronous mode, IrDA, and DMA.
For now only basic UART and RS485 operation is supported.
Signed-off-by: Joachim Eastwood <manabian@gmail.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r-- | drivers/tty/serial/8250/8250_lpc18xx.c | 230 | ||||
-rw-r--r-- | drivers/tty/serial/8250/Kconfig | 8 | ||||
-rw-r--r-- | drivers/tty/serial/8250/Makefile | 1 |
3 files changed, 239 insertions, 0 deletions
diff --git a/drivers/tty/serial/8250/8250_lpc18xx.c b/drivers/tty/serial/8250/8250_lpc18xx.c new file mode 100644 index 000000000000..99cd478851ff --- /dev/null +++ b/drivers/tty/serial/8250/8250_lpc18xx.c | |||
@@ -0,0 +1,230 @@ | |||
1 | /* | ||
2 | * Serial port driver for NXP LPC18xx/43xx UART | ||
3 | * | ||
4 | * Copyright (C) 2015 Joachim Eastwood <manabian@gmail.com> | ||
5 | * | ||
6 | * Based on 8250_mtk.c: | ||
7 | * Copyright (c) 2014 MundoReader S.L. | ||
8 | * Matthias Brugger <matthias.bgg@gmail.com> | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify | ||
11 | * it under the terms of the GNU General Public License version 2 as | ||
12 | * published by the Free Software Foundation. | ||
13 | * | ||
14 | */ | ||
15 | |||
16 | #include <linux/clk.h> | ||
17 | #include <linux/io.h> | ||
18 | #include <linux/module.h> | ||
19 | #include <linux/of.h> | ||
20 | #include <linux/platform_device.h> | ||
21 | |||
22 | #include "8250.h" | ||
23 | |||
24 | /* Additional LPC18xx/43xx 8250 registers and bits */ | ||
25 | #define LPC18XX_UART_RS485CTRL (0x04c / sizeof(u32)) | ||
26 | #define LPC18XX_UART_RS485CTRL_NMMEN BIT(0) | ||
27 | #define LPC18XX_UART_RS485CTRL_DCTRL BIT(4) | ||
28 | #define LPC18XX_UART_RS485CTRL_OINV BIT(5) | ||
29 | #define LPC18XX_UART_RS485DLY (0x054 / sizeof(u32)) | ||
30 | #define LPC18XX_UART_RS485DLY_MAX 255 | ||
31 | |||
32 | struct lpc18xx_uart_data { | ||
33 | struct uart_8250_dma dma; | ||
34 | struct clk *clk_uart; | ||
35 | struct clk *clk_reg; | ||
36 | int line; | ||
37 | }; | ||
38 | |||
39 | static int lpc18xx_rs485_config(struct uart_port *port, | ||
40 | struct serial_rs485 *rs485) | ||
41 | { | ||
42 | struct uart_8250_port *up = up_to_u8250p(port); | ||
43 | u32 rs485_ctrl_reg = 0; | ||
44 | u32 rs485_dly_reg = 0; | ||
45 | unsigned baud_clk; | ||
46 | |||
47 | if (rs485->flags & SER_RS485_ENABLED) | ||
48 | memset(rs485->padding, 0, sizeof(rs485->padding)); | ||
49 | else | ||
50 | memset(rs485, 0, sizeof(*rs485)); | ||
51 | |||
52 | rs485->flags &= SER_RS485_ENABLED | SER_RS485_RTS_ON_SEND | | ||
53 | SER_RS485_RTS_AFTER_SEND; | ||
54 | |||
55 | if (rs485->flags & SER_RS485_ENABLED) { | ||
56 | rs485_ctrl_reg |= LPC18XX_UART_RS485CTRL_NMMEN | | ||
57 | LPC18XX_UART_RS485CTRL_DCTRL; | ||
58 | |||
59 | if (rs485->flags & SER_RS485_RTS_ON_SEND) { | ||
60 | rs485_ctrl_reg |= LPC18XX_UART_RS485CTRL_OINV; | ||
61 | rs485->flags &= ~SER_RS485_RTS_AFTER_SEND; | ||
62 | } else { | ||
63 | rs485->flags |= SER_RS485_RTS_AFTER_SEND; | ||
64 | } | ||
65 | } | ||
66 | |||
67 | if (rs485->delay_rts_after_send) { | ||
68 | baud_clk = port->uartclk / up->dl_read(up); | ||
69 | rs485_dly_reg = DIV_ROUND_UP(rs485->delay_rts_after_send | ||
70 | * baud_clk, MSEC_PER_SEC); | ||
71 | |||
72 | if (rs485_dly_reg > LPC18XX_UART_RS485DLY_MAX) | ||
73 | rs485_dly_reg = LPC18XX_UART_RS485DLY_MAX; | ||
74 | |||
75 | /* Calculate the resulting delay in ms */ | ||
76 | rs485->delay_rts_after_send = (rs485_dly_reg * MSEC_PER_SEC) | ||
77 | / baud_clk; | ||
78 | } | ||
79 | |||
80 | /* Delay RTS before send not supported */ | ||
81 | rs485->delay_rts_before_send = 0; | ||
82 | |||
83 | serial_out(up, LPC18XX_UART_RS485CTRL, rs485_ctrl_reg); | ||
84 | serial_out(up, LPC18XX_UART_RS485DLY, rs485_dly_reg); | ||
85 | |||
86 | port->rs485 = *rs485; | ||
87 | |||
88 | return 0; | ||
89 | } | ||
90 | |||
91 | static void lpc18xx_uart_serial_out(struct uart_port *p, int offset, int value) | ||
92 | { | ||
93 | /* | ||
94 | * For DMA mode one must ensure that the UART_FCR_DMA_SELECT | ||
95 | * bit is set when FIFO is enabled. Even if DMA is not used | ||
96 | * setting this bit doesn't seem to affect anything. | ||
97 | */ | ||
98 | if (offset == UART_FCR && (value & UART_FCR_ENABLE_FIFO)) | ||
99 | value |= UART_FCR_DMA_SELECT; | ||
100 | |||
101 | offset = offset << p->regshift; | ||
102 | writel(value, p->membase + offset); | ||
103 | } | ||
104 | |||
105 | static int lpc18xx_serial_probe(struct platform_device *pdev) | ||
106 | { | ||
107 | struct lpc18xx_uart_data *data; | ||
108 | struct uart_8250_port uart; | ||
109 | struct resource *res; | ||
110 | int irq, ret; | ||
111 | |||
112 | irq = platform_get_irq(pdev, 0); | ||
113 | if (irq < 0) { | ||
114 | dev_err(&pdev->dev, "irq not found"); | ||
115 | return irq; | ||
116 | } | ||
117 | |||
118 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
119 | if (!res) { | ||
120 | dev_err(&pdev->dev, "memory resource not found"); | ||
121 | return -EINVAL; | ||
122 | } | ||
123 | |||
124 | memset(&uart, 0, sizeof(uart)); | ||
125 | |||
126 | uart.port.membase = devm_ioremap(&pdev->dev, res->start, | ||
127 | resource_size(res)); | ||
128 | if (!uart.port.membase) | ||
129 | return -ENOMEM; | ||
130 | |||
131 | data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); | ||
132 | if (!data) | ||
133 | return -ENOMEM; | ||
134 | |||
135 | data->clk_uart = devm_clk_get(&pdev->dev, "uartclk"); | ||
136 | if (IS_ERR(data->clk_uart)) { | ||
137 | dev_err(&pdev->dev, "uart clock not found\n"); | ||
138 | return PTR_ERR(data->clk_uart); | ||
139 | } | ||
140 | |||
141 | data->clk_reg = devm_clk_get(&pdev->dev, "reg"); | ||
142 | if (IS_ERR(data->clk_reg)) { | ||
143 | dev_err(&pdev->dev, "reg clock not found\n"); | ||
144 | return PTR_ERR(data->clk_reg); | ||
145 | } | ||
146 | |||
147 | ret = clk_prepare_enable(data->clk_reg); | ||
148 | if (ret) { | ||
149 | dev_err(&pdev->dev, "unable to enable reg clock\n"); | ||
150 | return ret; | ||
151 | } | ||
152 | |||
153 | ret = clk_prepare_enable(data->clk_uart); | ||
154 | if (ret) { | ||
155 | dev_err(&pdev->dev, "unable to enable uart clock\n"); | ||
156 | goto dis_clk_reg; | ||
157 | } | ||
158 | |||
159 | ret = of_alias_get_id(pdev->dev.of_node, "serial"); | ||
160 | if (ret >= 0) | ||
161 | uart.port.line = ret; | ||
162 | |||
163 | data->dma.rx_param = data; | ||
164 | data->dma.tx_param = data; | ||
165 | |||
166 | spin_lock_init(&uart.port.lock); | ||
167 | uart.port.dev = &pdev->dev; | ||
168 | uart.port.irq = irq; | ||
169 | uart.port.iotype = UPIO_MEM32; | ||
170 | uart.port.mapbase = res->start; | ||
171 | uart.port.regshift = 2; | ||
172 | uart.port.type = PORT_16550A; | ||
173 | uart.port.flags = UPF_FIXED_PORT | UPF_FIXED_TYPE | UPF_SKIP_TEST; | ||
174 | uart.port.uartclk = clk_get_rate(data->clk_uart); | ||
175 | uart.port.private_data = data; | ||
176 | uart.port.rs485_config = lpc18xx_rs485_config; | ||
177 | uart.port.serial_out = lpc18xx_uart_serial_out; | ||
178 | |||
179 | uart.dma = &data->dma; | ||
180 | uart.dma->rxconf.src_maxburst = 1; | ||
181 | uart.dma->txconf.dst_maxburst = 1; | ||
182 | |||
183 | ret = serial8250_register_8250_port(&uart); | ||
184 | if (ret < 0) { | ||
185 | dev_err(&pdev->dev, "unable to register 8250 port\n"); | ||
186 | goto dis_uart_clk; | ||
187 | } | ||
188 | |||
189 | data->line = ret; | ||
190 | platform_set_drvdata(pdev, data); | ||
191 | |||
192 | return 0; | ||
193 | |||
194 | dis_uart_clk: | ||
195 | clk_disable_unprepare(data->clk_uart); | ||
196 | dis_clk_reg: | ||
197 | clk_disable_unprepare(data->clk_reg); | ||
198 | return ret; | ||
199 | } | ||
200 | |||
201 | static int lpc18xx_serial_remove(struct platform_device *pdev) | ||
202 | { | ||
203 | struct lpc18xx_uart_data *data = platform_get_drvdata(pdev); | ||
204 | |||
205 | serial8250_unregister_port(data->line); | ||
206 | clk_disable_unprepare(data->clk_uart); | ||
207 | clk_disable_unprepare(data->clk_reg); | ||
208 | |||
209 | return 0; | ||
210 | } | ||
211 | |||
212 | static const struct of_device_id lpc18xx_serial_match[] = { | ||
213 | { .compatible = "nxp,lpc1850-uart" }, | ||
214 | { }, | ||
215 | }; | ||
216 | MODULE_DEVICE_TABLE(of, lpc18xx_serial_match); | ||
217 | |||
218 | static struct platform_driver lpc18xx_serial_driver = { | ||
219 | .probe = lpc18xx_serial_probe, | ||
220 | .remove = lpc18xx_serial_remove, | ||
221 | .driver = { | ||
222 | .name = "lpc18xx-uart", | ||
223 | .of_match_table = lpc18xx_serial_match, | ||
224 | }, | ||
225 | }; | ||
226 | module_platform_driver(lpc18xx_serial_driver); | ||
227 | |||
228 | MODULE_AUTHOR("Joachim Eastwood <manabian@gmail.com>"); | ||
229 | MODULE_DESCRIPTION("Serial port driver NXP LPC18xx/43xx devices"); | ||
230 | MODULE_LICENSE("GPL v2"); | ||
diff --git a/drivers/tty/serial/8250/Kconfig b/drivers/tty/serial/8250/Kconfig index 3e1ae4462cba..a74a8e4717d4 100644 --- a/drivers/tty/serial/8250/Kconfig +++ b/drivers/tty/serial/8250/Kconfig | |||
@@ -336,6 +336,14 @@ config SERIAL_8250_FINTEK | |||
336 | LPC to 4 UART. This device has some RS485 functionality not available | 336 | LPC to 4 UART. This device has some RS485 functionality not available |
337 | through the PNP driver. If unsure, say N. | 337 | through the PNP driver. If unsure, say N. |
338 | 338 | ||
339 | config SERIAL_8250_LPC18XX | ||
340 | bool "NXP LPC18xx/43xx serial port support" | ||
341 | depends on SERIAL_8250 && OF && (ARCH_LPC18XX || COMPILE_TEST) | ||
342 | default ARCH_LPC18XX | ||
343 | help | ||
344 | If you have a LPC18xx/43xx based board and want to use the | ||
345 | serial port, say Y to this option. If unsure, say Y. | ||
346 | |||
339 | config SERIAL_8250_MT6577 | 347 | config SERIAL_8250_MT6577 |
340 | bool "Mediatek serial port support" | 348 | bool "Mediatek serial port support" |
341 | depends on SERIAL_8250 && ARCH_MEDIATEK | 349 | depends on SERIAL_8250 && ARCH_MEDIATEK |
diff --git a/drivers/tty/serial/8250/Makefile b/drivers/tty/serial/8250/Makefile index f7ca8c3ce9e7..6fa22ffad63d 100644 --- a/drivers/tty/serial/8250/Makefile +++ b/drivers/tty/serial/8250/Makefile | |||
@@ -22,5 +22,6 @@ obj-$(CONFIG_SERIAL_8250_DW) += 8250_dw.o | |||
22 | obj-$(CONFIG_SERIAL_8250_EM) += 8250_em.o | 22 | obj-$(CONFIG_SERIAL_8250_EM) += 8250_em.o |
23 | obj-$(CONFIG_SERIAL_8250_OMAP) += 8250_omap.o | 23 | obj-$(CONFIG_SERIAL_8250_OMAP) += 8250_omap.o |
24 | obj-$(CONFIG_SERIAL_8250_FINTEK) += 8250_fintek.o | 24 | obj-$(CONFIG_SERIAL_8250_FINTEK) += 8250_fintek.o |
25 | obj-$(CONFIG_SERIAL_8250_LPC18XX) += 8250_lpc18xx.o | ||
25 | obj-$(CONFIG_SERIAL_8250_MT6577) += 8250_mtk.o | 26 | obj-$(CONFIG_SERIAL_8250_MT6577) += 8250_mtk.o |
26 | obj-$(CONFIG_SERIAL_8250_UNIPHIER) += 8250_uniphier.o | 27 | obj-$(CONFIG_SERIAL_8250_UNIPHIER) += 8250_uniphier.o |