Commit | Line | Data |
---|---|---|
e3b3d0f5 | 1 | // SPDX-License-Identifier: GPL-2.0 |
d2aef35a JE |
2 | /* |
3 | * Serial port driver for NXP LPC18xx/43xx UART | |
4 | * | |
5 | * Copyright (C) 2015 Joachim Eastwood <manabian@gmail.com> | |
6 | * | |
7 | * Based on 8250_mtk.c: | |
8 | * Copyright (c) 2014 MundoReader S.L. | |
9 | * Matthias Brugger <matthias.bgg@gmail.com> | |
10 | * | |
11 | * This program is free software; you can redistribute it and/or modify | |
12 | * it under the terms of the GNU General Public License version 2 as | |
13 | * published by the Free Software Foundation. | |
14 | * | |
15 | */ | |
16 | ||
17 | #include <linux/clk.h> | |
18 | #include <linux/io.h> | |
19 | #include <linux/module.h> | |
20 | #include <linux/of.h> | |
21 | #include <linux/platform_device.h> | |
22 | ||
23 | #include "8250.h" | |
24 | ||
25 | /* Additional LPC18xx/43xx 8250 registers and bits */ | |
26 | #define LPC18XX_UART_RS485CTRL (0x04c / sizeof(u32)) | |
27 | #define LPC18XX_UART_RS485CTRL_NMMEN BIT(0) | |
28 | #define LPC18XX_UART_RS485CTRL_DCTRL BIT(4) | |
29 | #define LPC18XX_UART_RS485CTRL_OINV BIT(5) | |
30 | #define LPC18XX_UART_RS485DLY (0x054 / sizeof(u32)) | |
31 | #define LPC18XX_UART_RS485DLY_MAX 255 | |
32 | ||
33 | struct lpc18xx_uart_data { | |
34 | struct uart_8250_dma dma; | |
35 | struct clk *clk_uart; | |
36 | struct clk *clk_reg; | |
37 | int line; | |
38 | }; | |
39 | ||
40 | static int lpc18xx_rs485_config(struct uart_port *port, | |
41 | struct serial_rs485 *rs485) | |
42 | { | |
43 | struct uart_8250_port *up = up_to_u8250p(port); | |
44 | u32 rs485_ctrl_reg = 0; | |
45 | u32 rs485_dly_reg = 0; | |
46 | unsigned baud_clk; | |
47 | ||
48 | if (rs485->flags & SER_RS485_ENABLED) | |
49 | memset(rs485->padding, 0, sizeof(rs485->padding)); | |
50 | else | |
51 | memset(rs485, 0, sizeof(*rs485)); | |
52 | ||
53 | rs485->flags &= SER_RS485_ENABLED | SER_RS485_RTS_ON_SEND | | |
54 | SER_RS485_RTS_AFTER_SEND; | |
55 | ||
56 | if (rs485->flags & SER_RS485_ENABLED) { | |
57 | rs485_ctrl_reg |= LPC18XX_UART_RS485CTRL_NMMEN | | |
58 | LPC18XX_UART_RS485CTRL_DCTRL; | |
59 | ||
60 | if (rs485->flags & SER_RS485_RTS_ON_SEND) { | |
61 | rs485_ctrl_reg |= LPC18XX_UART_RS485CTRL_OINV; | |
62 | rs485->flags &= ~SER_RS485_RTS_AFTER_SEND; | |
63 | } else { | |
64 | rs485->flags |= SER_RS485_RTS_AFTER_SEND; | |
65 | } | |
66 | } | |
67 | ||
68 | if (rs485->delay_rts_after_send) { | |
69 | baud_clk = port->uartclk / up->dl_read(up); | |
70 | rs485_dly_reg = DIV_ROUND_UP(rs485->delay_rts_after_send | |
71 | * baud_clk, MSEC_PER_SEC); | |
72 | ||
73 | if (rs485_dly_reg > LPC18XX_UART_RS485DLY_MAX) | |
74 | rs485_dly_reg = LPC18XX_UART_RS485DLY_MAX; | |
75 | ||
76 | /* Calculate the resulting delay in ms */ | |
77 | rs485->delay_rts_after_send = (rs485_dly_reg * MSEC_PER_SEC) | |
78 | / baud_clk; | |
79 | } | |
80 | ||
81 | /* Delay RTS before send not supported */ | |
82 | rs485->delay_rts_before_send = 0; | |
83 | ||
84 | serial_out(up, LPC18XX_UART_RS485CTRL, rs485_ctrl_reg); | |
85 | serial_out(up, LPC18XX_UART_RS485DLY, rs485_dly_reg); | |
86 | ||
87 | port->rs485 = *rs485; | |
88 | ||
89 | return 0; | |
90 | } | |
91 | ||
92 | static void lpc18xx_uart_serial_out(struct uart_port *p, int offset, int value) | |
93 | { | |
94 | /* | |
95 | * For DMA mode one must ensure that the UART_FCR_DMA_SELECT | |
96 | * bit is set when FIFO is enabled. Even if DMA is not used | |
97 | * setting this bit doesn't seem to affect anything. | |
98 | */ | |
99 | if (offset == UART_FCR && (value & UART_FCR_ENABLE_FIFO)) | |
100 | value |= UART_FCR_DMA_SELECT; | |
101 | ||
102 | offset = offset << p->regshift; | |
103 | writel(value, p->membase + offset); | |
104 | } | |
105 | ||
106 | static int lpc18xx_serial_probe(struct platform_device *pdev) | |
107 | { | |
108 | struct lpc18xx_uart_data *data; | |
109 | struct uart_8250_port uart; | |
110 | struct resource *res; | |
111 | int irq, ret; | |
112 | ||
113 | irq = platform_get_irq(pdev, 0); | |
114 | if (irq < 0) { | |
115 | dev_err(&pdev->dev, "irq not found"); | |
116 | return irq; | |
117 | } | |
118 | ||
119 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | |
120 | if (!res) { | |
121 | dev_err(&pdev->dev, "memory resource not found"); | |
122 | return -EINVAL; | |
123 | } | |
124 | ||
125 | memset(&uart, 0, sizeof(uart)); | |
126 | ||
127 | uart.port.membase = devm_ioremap(&pdev->dev, res->start, | |
128 | resource_size(res)); | |
129 | if (!uart.port.membase) | |
130 | return -ENOMEM; | |
131 | ||
132 | data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); | |
133 | if (!data) | |
134 | return -ENOMEM; | |
135 | ||
136 | data->clk_uart = devm_clk_get(&pdev->dev, "uartclk"); | |
137 | if (IS_ERR(data->clk_uart)) { | |
138 | dev_err(&pdev->dev, "uart clock not found\n"); | |
139 | return PTR_ERR(data->clk_uart); | |
140 | } | |
141 | ||
142 | data->clk_reg = devm_clk_get(&pdev->dev, "reg"); | |
143 | if (IS_ERR(data->clk_reg)) { | |
144 | dev_err(&pdev->dev, "reg clock not found\n"); | |
145 | return PTR_ERR(data->clk_reg); | |
146 | } | |
147 | ||
148 | ret = clk_prepare_enable(data->clk_reg); | |
149 | if (ret) { | |
150 | dev_err(&pdev->dev, "unable to enable reg clock\n"); | |
151 | return ret; | |
152 | } | |
153 | ||
154 | ret = clk_prepare_enable(data->clk_uart); | |
155 | if (ret) { | |
156 | dev_err(&pdev->dev, "unable to enable uart clock\n"); | |
157 | goto dis_clk_reg; | |
158 | } | |
159 | ||
160 | ret = of_alias_get_id(pdev->dev.of_node, "serial"); | |
161 | if (ret >= 0) | |
162 | uart.port.line = ret; | |
163 | ||
164 | data->dma.rx_param = data; | |
165 | data->dma.tx_param = data; | |
166 | ||
167 | spin_lock_init(&uart.port.lock); | |
168 | uart.port.dev = &pdev->dev; | |
169 | uart.port.irq = irq; | |
170 | uart.port.iotype = UPIO_MEM32; | |
171 | uart.port.mapbase = res->start; | |
172 | uart.port.regshift = 2; | |
173 | uart.port.type = PORT_16550A; | |
174 | uart.port.flags = UPF_FIXED_PORT | UPF_FIXED_TYPE | UPF_SKIP_TEST; | |
175 | uart.port.uartclk = clk_get_rate(data->clk_uart); | |
176 | uart.port.private_data = data; | |
177 | uart.port.rs485_config = lpc18xx_rs485_config; | |
178 | uart.port.serial_out = lpc18xx_uart_serial_out; | |
179 | ||
180 | uart.dma = &data->dma; | |
181 | uart.dma->rxconf.src_maxburst = 1; | |
182 | uart.dma->txconf.dst_maxburst = 1; | |
183 | ||
184 | ret = serial8250_register_8250_port(&uart); | |
185 | if (ret < 0) { | |
186 | dev_err(&pdev->dev, "unable to register 8250 port\n"); | |
187 | goto dis_uart_clk; | |
188 | } | |
189 | ||
190 | data->line = ret; | |
191 | platform_set_drvdata(pdev, data); | |
192 | ||
193 | return 0; | |
194 | ||
195 | dis_uart_clk: | |
196 | clk_disable_unprepare(data->clk_uart); | |
197 | dis_clk_reg: | |
198 | clk_disable_unprepare(data->clk_reg); | |
199 | return ret; | |
200 | } | |
201 | ||
202 | static int lpc18xx_serial_remove(struct platform_device *pdev) | |
203 | { | |
204 | struct lpc18xx_uart_data *data = platform_get_drvdata(pdev); | |
205 | ||
206 | serial8250_unregister_port(data->line); | |
207 | clk_disable_unprepare(data->clk_uart); | |
208 | clk_disable_unprepare(data->clk_reg); | |
209 | ||
210 | return 0; | |
211 | } | |
212 | ||
213 | static const struct of_device_id lpc18xx_serial_match[] = { | |
214 | { .compatible = "nxp,lpc1850-uart" }, | |
215 | { }, | |
216 | }; | |
217 | MODULE_DEVICE_TABLE(of, lpc18xx_serial_match); | |
218 | ||
219 | static struct platform_driver lpc18xx_serial_driver = { | |
220 | .probe = lpc18xx_serial_probe, | |
221 | .remove = lpc18xx_serial_remove, | |
222 | .driver = { | |
223 | .name = "lpc18xx-uart", | |
224 | .of_match_table = lpc18xx_serial_match, | |
225 | }, | |
226 | }; | |
227 | module_platform_driver(lpc18xx_serial_driver); | |
228 | ||
229 | MODULE_AUTHOR("Joachim Eastwood <manabian@gmail.com>"); | |
230 | MODULE_DESCRIPTION("Serial port driver NXP LPC18xx/43xx devices"); | |
231 | MODULE_LICENSE("GPL v2"); |