Commit | Line | Data |
---|---|---|
61ec9016 | 1 | /* |
d9bb3fb1 | 2 | * Cadence UART driver (found in Xilinx Zynq) |
61ec9016 | 3 | * |
e555a211 | 4 | * 2011 - 2014 (C) Xilinx Inc. |
61ec9016 JL |
5 | * |
6 | * This program is free software; you can redistribute it | |
7 | * and/or modify it under the terms of the GNU General Public | |
8 | * License as published by the Free Software Foundation; | |
9 | * either version 2 of the License, or (at your option) any | |
10 | * later version. | |
d9bb3fb1 SB |
11 | * |
12 | * This driver has originally been pushed by Xilinx using a Zynq-branding. This | |
13 | * still shows in the naming of this file, the kconfig symbols and some symbols | |
14 | * in the code. | |
61ec9016 JL |
15 | */ |
16 | ||
0c0c47bc VL |
17 | #if defined(CONFIG_SERIAL_XILINX_PS_UART_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) |
18 | #define SUPPORT_SYSRQ | |
19 | #endif | |
20 | ||
61ec9016 | 21 | #include <linux/platform_device.h> |
ee160a38 | 22 | #include <linux/serial.h> |
0c0c47bc | 23 | #include <linux/console.h> |
61ec9016 | 24 | #include <linux/serial_core.h> |
30e1e285 | 25 | #include <linux/slab.h> |
ee160a38 JS |
26 | #include <linux/tty.h> |
27 | #include <linux/tty_flip.h> | |
2326669c | 28 | #include <linux/clk.h> |
61ec9016 JL |
29 | #include <linux/irq.h> |
30 | #include <linux/io.h> | |
31 | #include <linux/of.h> | |
578b9ce0 | 32 | #include <linux/module.h> |
61ec9016 | 33 | |
d9bb3fb1 SB |
34 | #define CDNS_UART_TTY_NAME "ttyPS" |
35 | #define CDNS_UART_NAME "xuartps" | |
36 | #define CDNS_UART_MAJOR 0 /* use dynamic node allocation */ | |
37 | #define CDNS_UART_MINOR 0 /* works best with devtmpfs */ | |
38 | #define CDNS_UART_NR_PORTS 2 | |
39 | #define CDNS_UART_FIFO_SIZE 64 /* FIFO size */ | |
9646e4fe | 40 | #define CDNS_UART_REGISTER_SPACE 0x1000 |
61ec9016 | 41 | |
85baf542 S |
42 | /* Rx Trigger level */ |
43 | static int rx_trigger_level = 56; | |
44 | module_param(rx_trigger_level, uint, S_IRUGO); | |
45 | MODULE_PARM_DESC(rx_trigger_level, "Rx trigger level, 1-63 bytes"); | |
46 | ||
47 | /* Rx Timeout */ | |
48 | static int rx_timeout = 10; | |
49 | module_param(rx_timeout, uint, S_IRUGO); | |
50 | MODULE_PARM_DESC(rx_timeout, "Rx timeout, 1-255"); | |
51 | ||
e555a211 | 52 | /* Register offsets for the UART. */ |
a8df6a51 SB |
53 | #define CDNS_UART_CR 0x00 /* Control Register */ |
54 | #define CDNS_UART_MR 0x04 /* Mode Register */ | |
55 | #define CDNS_UART_IER 0x08 /* Interrupt Enable */ | |
56 | #define CDNS_UART_IDR 0x0C /* Interrupt Disable */ | |
57 | #define CDNS_UART_IMR 0x10 /* Interrupt Mask */ | |
58 | #define CDNS_UART_ISR 0x14 /* Interrupt Status */ | |
59 | #define CDNS_UART_BAUDGEN 0x18 /* Baud Rate Generator */ | |
60 | #define CDNS_UART_RXTOUT 0x1C /* RX Timeout */ | |
61 | #define CDNS_UART_RXWM 0x20 /* RX FIFO Trigger Level */ | |
62 | #define CDNS_UART_MODEMCR 0x24 /* Modem Control */ | |
63 | #define CDNS_UART_MODEMSR 0x28 /* Modem Status */ | |
64 | #define CDNS_UART_SR 0x2C /* Channel Status */ | |
65 | #define CDNS_UART_FIFO 0x30 /* FIFO */ | |
66 | #define CDNS_UART_BAUDDIV 0x34 /* Baud Rate Divider */ | |
67 | #define CDNS_UART_FLOWDEL 0x38 /* Flow Delay */ | |
68 | #define CDNS_UART_IRRX_PWIDTH 0x3C /* IR Min Received Pulse Width */ | |
69 | #define CDNS_UART_IRTX_PWIDTH 0x40 /* IR Transmitted pulse Width */ | |
70 | #define CDNS_UART_TXWM 0x44 /* TX FIFO Trigger Level */ | |
e555a211 SB |
71 | |
72 | /* Control Register Bit Definitions */ | |
d9bb3fb1 SB |
73 | #define CDNS_UART_CR_STOPBRK 0x00000100 /* Stop TX break */ |
74 | #define CDNS_UART_CR_STARTBRK 0x00000080 /* Set TX break */ | |
75 | #define CDNS_UART_CR_TX_DIS 0x00000020 /* TX disabled. */ | |
76 | #define CDNS_UART_CR_TX_EN 0x00000010 /* TX enabled */ | |
77 | #define CDNS_UART_CR_RX_DIS 0x00000008 /* RX disabled. */ | |
78 | #define CDNS_UART_CR_RX_EN 0x00000004 /* RX enabled */ | |
79 | #define CDNS_UART_CR_TXRST 0x00000002 /* TX logic reset */ | |
80 | #define CDNS_UART_CR_RXRST 0x00000001 /* RX logic reset */ | |
81 | #define CDNS_UART_CR_RST_TO 0x00000040 /* Restart Timeout Counter */ | |
61ec9016 | 82 | |
e555a211 SB |
83 | /* |
84 | * Mode Register: | |
61ec9016 JL |
85 | * The mode register (MR) defines the mode of transfer as well as the data |
86 | * format. If this register is modified during transmission or reception, | |
87 | * data validity cannot be guaranteed. | |
61ec9016 | 88 | */ |
d9bb3fb1 SB |
89 | #define CDNS_UART_MR_CLKSEL 0x00000001 /* Pre-scalar selection */ |
90 | #define CDNS_UART_MR_CHMODE_L_LOOP 0x00000200 /* Local loop back mode */ | |
91 | #define CDNS_UART_MR_CHMODE_NORM 0x00000000 /* Normal mode */ | |
61ec9016 | 92 | |
d9bb3fb1 SB |
93 | #define CDNS_UART_MR_STOPMODE_2_BIT 0x00000080 /* 2 stop bits */ |
94 | #define CDNS_UART_MR_STOPMODE_1_BIT 0x00000000 /* 1 stop bit */ | |
61ec9016 | 95 | |
d9bb3fb1 SB |
96 | #define CDNS_UART_MR_PARITY_NONE 0x00000020 /* No parity mode */ |
97 | #define CDNS_UART_MR_PARITY_MARK 0x00000018 /* Mark parity mode */ | |
98 | #define CDNS_UART_MR_PARITY_SPACE 0x00000010 /* Space parity mode */ | |
99 | #define CDNS_UART_MR_PARITY_ODD 0x00000008 /* Odd parity mode */ | |
100 | #define CDNS_UART_MR_PARITY_EVEN 0x00000000 /* Even parity mode */ | |
61ec9016 | 101 | |
d9bb3fb1 SB |
102 | #define CDNS_UART_MR_CHARLEN_6_BIT 0x00000006 /* 6 bits data */ |
103 | #define CDNS_UART_MR_CHARLEN_7_BIT 0x00000004 /* 7 bits data */ | |
104 | #define CDNS_UART_MR_CHARLEN_8_BIT 0x00000000 /* 8 bits data */ | |
61ec9016 | 105 | |
e555a211 SB |
106 | /* |
107 | * Interrupt Registers: | |
61ec9016 JL |
108 | * Interrupt control logic uses the interrupt enable register (IER) and the |
109 | * interrupt disable register (IDR) to set the value of the bits in the | |
110 | * interrupt mask register (IMR). The IMR determines whether to pass an | |
111 | * interrupt to the interrupt status register (ISR). | |
112 | * Writing a 1 to IER Enables an interrupt, writing a 1 to IDR disables an | |
113 | * interrupt. IMR and ISR are read only, and IER and IDR are write only. | |
114 | * Reading either IER or IDR returns 0x00. | |
61ec9016 JL |
115 | * All four registers have the same bit definitions. |
116 | */ | |
d9bb3fb1 SB |
117 | #define CDNS_UART_IXR_TOUT 0x00000100 /* RX Timeout error interrupt */ |
118 | #define CDNS_UART_IXR_PARITY 0x00000080 /* Parity error interrupt */ | |
119 | #define CDNS_UART_IXR_FRAMING 0x00000040 /* Framing error interrupt */ | |
120 | #define CDNS_UART_IXR_OVERRUN 0x00000020 /* Overrun error interrupt */ | |
121 | #define CDNS_UART_IXR_TXFULL 0x00000010 /* TX FIFO Full interrupt */ | |
122 | #define CDNS_UART_IXR_TXEMPTY 0x00000008 /* TX FIFO empty interrupt */ | |
123 | #define CDNS_UART_ISR_RXEMPTY 0x00000002 /* RX FIFO empty interrupt */ | |
124 | #define CDNS_UART_IXR_RXTRIG 0x00000001 /* RX FIFO trigger interrupt */ | |
125 | #define CDNS_UART_IXR_RXFULL 0x00000004 /* RX FIFO full interrupt. */ | |
126 | #define CDNS_UART_IXR_RXEMPTY 0x00000002 /* RX FIFO empty interrupt. */ | |
127 | #define CDNS_UART_IXR_MASK 0x00001FFF /* Valid bit mask */ | |
61ec9016 | 128 | |
373e882f SB |
129 | #define CDNS_UART_RX_IRQS (CDNS_UART_IXR_PARITY | CDNS_UART_IXR_FRAMING | \ |
130 | CDNS_UART_IXR_OVERRUN | CDNS_UART_IXR_RXTRIG | \ | |
131 | CDNS_UART_IXR_TOUT) | |
132 | ||
0c0c47bc | 133 | /* Goes in read_status_mask for break detection as the HW doesn't do it*/ |
d9bb3fb1 | 134 | #define CDNS_UART_IXR_BRK 0x80000000 |
0c0c47bc | 135 | |
19038ad9 LPC |
136 | /* |
137 | * Modem Control register: | |
138 | * The read/write Modem Control register controls the interface with the modem | |
139 | * or data set, or a peripheral device emulating a modem. | |
140 | */ | |
141 | #define CDNS_UART_MODEMCR_FCM 0x00000020 /* Automatic flow control mode */ | |
142 | #define CDNS_UART_MODEMCR_RTS 0x00000002 /* Request to send output control */ | |
143 | #define CDNS_UART_MODEMCR_DTR 0x00000001 /* Data Terminal Ready */ | |
144 | ||
e555a211 SB |
145 | /* |
146 | * Channel Status Register: | |
61ec9016 JL |
147 | * The channel status register (CSR) is provided to enable the control logic |
148 | * to monitor the status of bits in the channel interrupt status register, | |
149 | * even if these are masked out by the interrupt mask register. | |
150 | */ | |
d9bb3fb1 SB |
151 | #define CDNS_UART_SR_RXEMPTY 0x00000002 /* RX FIFO empty */ |
152 | #define CDNS_UART_SR_TXEMPTY 0x00000008 /* TX FIFO empty */ | |
153 | #define CDNS_UART_SR_TXFULL 0x00000010 /* TX FIFO full */ | |
154 | #define CDNS_UART_SR_RXTRIG 0x00000001 /* Rx Trigger */ | |
61ec9016 | 155 | |
e6b39bfd | 156 | /* baud dividers min/max values */ |
d9bb3fb1 SB |
157 | #define CDNS_UART_BDIV_MIN 4 |
158 | #define CDNS_UART_BDIV_MAX 255 | |
159 | #define CDNS_UART_CD_MAX 65535 | |
e6b39bfd | 160 | |
30e1e285 | 161 | /** |
d9bb3fb1 | 162 | * struct cdns_uart - device data |
489810a1 | 163 | * @port: Pointer to the UART port |
d9bb3fb1 SB |
164 | * @uartclk: Reference clock |
165 | * @pclk: APB clock | |
489810a1 MS |
166 | * @baud: Current baud rate |
167 | * @clk_rate_change_nb: Notifier block for clock changes | |
30e1e285 | 168 | */ |
d9bb3fb1 | 169 | struct cdns_uart { |
c4b0510c | 170 | struct uart_port *port; |
d9bb3fb1 SB |
171 | struct clk *uartclk; |
172 | struct clk *pclk; | |
c4b0510c SB |
173 | unsigned int baud; |
174 | struct notifier_block clk_rate_change_nb; | |
30e1e285 | 175 | }; |
d9bb3fb1 SB |
176 | #define to_cdns_uart(_nb) container_of(_nb, struct cdns_uart, \ |
177 | clk_rate_change_nb); | |
30e1e285 | 178 | |
5ede4a5c | 179 | static void cdns_uart_handle_rx(struct uart_port *port, unsigned int isrstatus) |
61ec9016 | 180 | { |
0c0c47bc VL |
181 | /* |
182 | * There is no hardware break detection, so we interpret framing | |
183 | * error with all-zeros data as a break sequence. Most of the time, | |
184 | * there's another non-zero byte at the end of the sequence. | |
185 | */ | |
d9bb3fb1 | 186 | if (isrstatus & CDNS_UART_IXR_FRAMING) { |
a8df6a51 | 187 | while (!(readl(port->membase + CDNS_UART_SR) & |
d9bb3fb1 | 188 | CDNS_UART_SR_RXEMPTY)) { |
a8df6a51 | 189 | if (!readl(port->membase + CDNS_UART_FIFO)) { |
d9bb3fb1 SB |
190 | port->read_status_mask |= CDNS_UART_IXR_BRK; |
191 | isrstatus &= ~CDNS_UART_IXR_FRAMING; | |
0c0c47bc VL |
192 | } |
193 | } | |
a8df6a51 | 194 | writel(CDNS_UART_IXR_FRAMING, port->membase + CDNS_UART_ISR); |
0c0c47bc VL |
195 | } |
196 | ||
61ec9016 | 197 | /* drop byte with parity error if IGNPAR specified */ |
d9bb3fb1 SB |
198 | if (isrstatus & port->ignore_status_mask & CDNS_UART_IXR_PARITY) |
199 | isrstatus &= ~(CDNS_UART_IXR_RXTRIG | CDNS_UART_IXR_TOUT); | |
61ec9016 JL |
200 | |
201 | isrstatus &= port->read_status_mask; | |
202 | isrstatus &= ~port->ignore_status_mask; | |
203 | ||
354fb1a7 SB |
204 | if (!(isrstatus & (CDNS_UART_IXR_TOUT | CDNS_UART_IXR_RXTRIG))) |
205 | return; | |
206 | ||
a8df6a51 | 207 | while (!(readl(port->membase + CDNS_UART_SR) & CDNS_UART_SR_RXEMPTY)) { |
354fb1a7 SB |
208 | u32 data; |
209 | char status = TTY_NORMAL; | |
210 | ||
a8df6a51 | 211 | data = readl(port->membase + CDNS_UART_FIFO); |
354fb1a7 SB |
212 | |
213 | /* Non-NULL byte after BREAK is garbage (99%) */ | |
214 | if (data && (port->read_status_mask & CDNS_UART_IXR_BRK)) { | |
215 | port->read_status_mask &= ~CDNS_UART_IXR_BRK; | |
216 | port->icount.brk++; | |
217 | if (uart_handle_break(port)) | |
218 | continue; | |
219 | } | |
0c0c47bc | 220 | |
74ea66d4 SB |
221 | if (uart_handle_sysrq_char(port, data)) |
222 | continue; | |
0c0c47bc | 223 | |
354fb1a7 | 224 | port->icount.rx++; |
61ec9016 | 225 | |
354fb1a7 SB |
226 | if (isrstatus & CDNS_UART_IXR_PARITY) { |
227 | port->icount.parity++; | |
228 | status = TTY_PARITY; | |
229 | } else if (isrstatus & CDNS_UART_IXR_FRAMING) { | |
230 | port->icount.frame++; | |
231 | status = TTY_FRAME; | |
232 | } else if (isrstatus & CDNS_UART_IXR_OVERRUN) { | |
233 | port->icount.overrun++; | |
61ec9016 | 234 | } |
354fb1a7 SB |
235 | |
236 | uart_insert_char(port, isrstatus, CDNS_UART_IXR_OVERRUN, | |
237 | data, status); | |
61ec9016 | 238 | } |
354fb1a7 | 239 | tty_flip_buffer_push(&port->state->port); |
5ede4a5c SB |
240 | } |
241 | ||
07986580 SB |
242 | static void cdns_uart_handle_tx(struct uart_port *port) |
243 | { | |
244 | unsigned int numbytes; | |
245 | ||
246 | if (uart_circ_empty(&port->state->xmit)) { | |
247 | writel(CDNS_UART_IXR_TXEMPTY, port->membase + CDNS_UART_IDR); | |
248 | return; | |
249 | } | |
250 | ||
251 | numbytes = port->fifosize; | |
252 | while (numbytes && !uart_circ_empty(&port->state->xmit) && | |
253 | !(readl(port->membase + CDNS_UART_SR) & CDNS_UART_SR_TXFULL)) { | |
254 | /* | |
255 | * Get the data from the UART circular buffer | |
256 | * and write it to the cdns_uart's TX_FIFO | |
257 | * register. | |
258 | */ | |
259 | writel(port->state->xmit.buf[port->state->xmit.tail], | |
260 | port->membase + CDNS_UART_FIFO); | |
261 | port->icount.tx++; | |
262 | ||
263 | /* | |
264 | * Adjust the tail of the UART buffer and wrap | |
265 | * the buffer if it reaches limit. | |
266 | */ | |
267 | port->state->xmit.tail = | |
268 | (port->state->xmit.tail + 1) & (UART_XMIT_SIZE - 1); | |
269 | ||
270 | numbytes--; | |
271 | } | |
272 | ||
273 | if (uart_circ_chars_pending(&port->state->xmit) < WAKEUP_CHARS) | |
274 | uart_write_wakeup(port); | |
275 | } | |
276 | ||
5ede4a5c SB |
277 | /** |
278 | * cdns_uart_isr - Interrupt handler | |
279 | * @irq: Irq number | |
280 | * @dev_id: Id of the port | |
281 | * | |
282 | * Return: IRQHANDLED | |
283 | */ | |
284 | static irqreturn_t cdns_uart_isr(int irq, void *dev_id) | |
285 | { | |
286 | struct uart_port *port = (struct uart_port *)dev_id; | |
287 | unsigned long flags; | |
07986580 | 288 | unsigned int isrstatus; |
5ede4a5c SB |
289 | |
290 | spin_lock_irqsave(&port->lock, flags); | |
291 | ||
292 | /* Read the interrupt status register to determine which | |
293 | * interrupt(s) is/are active. | |
294 | */ | |
a8df6a51 | 295 | isrstatus = readl(port->membase + CDNS_UART_ISR); |
5ede4a5c | 296 | |
373e882f SB |
297 | if (isrstatus & CDNS_UART_RX_IRQS) |
298 | cdns_uart_handle_rx(port, isrstatus); | |
61ec9016 | 299 | |
07986580 SB |
300 | if ((isrstatus & CDNS_UART_IXR_TXEMPTY) == CDNS_UART_IXR_TXEMPTY) |
301 | cdns_uart_handle_tx(port); | |
61ec9016 | 302 | |
a8df6a51 | 303 | writel(isrstatus, port->membase + CDNS_UART_ISR); |
61ec9016 JL |
304 | |
305 | /* be sure to release the lock and tty before leaving */ | |
306 | spin_unlock_irqrestore(&port->lock, flags); | |
61ec9016 JL |
307 | |
308 | return IRQ_HANDLED; | |
309 | } | |
310 | ||
311 | /** | |
d9bb3fb1 | 312 | * cdns_uart_calc_baud_divs - Calculate baud rate divisors |
e6b39bfd SB |
313 | * @clk: UART module input clock |
314 | * @baud: Desired baud rate | |
315 | * @rbdiv: BDIV value (return value) | |
316 | * @rcd: CD value (return value) | |
317 | * @div8: Value for clk_sel bit in mod (return value) | |
489810a1 | 318 | * Return: baud rate, requested baud when possible, or actual baud when there |
e6b39bfd SB |
319 | * was too much error, zero if no valid divisors are found. |
320 | * | |
321 | * Formula to obtain baud rate is | |
322 | * baud_tx/rx rate = clk/CD * (BDIV + 1) | |
323 | * input_clk = (Uart User Defined Clock or Apb Clock) | |
324 | * depends on UCLKEN in MR Reg | |
325 | * clk = input_clk or input_clk/8; | |
326 | * depends on CLKS in MR reg | |
327 | * CD and BDIV depends on values in | |
328 | * baud rate generate register | |
329 | * baud rate clock divisor register | |
330 | */ | |
d9bb3fb1 SB |
331 | static unsigned int cdns_uart_calc_baud_divs(unsigned int clk, |
332 | unsigned int baud, u32 *rbdiv, u32 *rcd, int *div8) | |
61ec9016 | 333 | { |
e6b39bfd SB |
334 | u32 cd, bdiv; |
335 | unsigned int calc_baud; | |
336 | unsigned int bestbaud = 0; | |
61ec9016 | 337 | unsigned int bauderror; |
e6b39bfd | 338 | unsigned int besterror = ~0; |
61ec9016 | 339 | |
d9bb3fb1 | 340 | if (baud < clk / ((CDNS_UART_BDIV_MAX + 1) * CDNS_UART_CD_MAX)) { |
e6b39bfd SB |
341 | *div8 = 1; |
342 | clk /= 8; | |
343 | } else { | |
344 | *div8 = 0; | |
345 | } | |
61ec9016 | 346 | |
d9bb3fb1 | 347 | for (bdiv = CDNS_UART_BDIV_MIN; bdiv <= CDNS_UART_BDIV_MAX; bdiv++) { |
e6b39bfd | 348 | cd = DIV_ROUND_CLOSEST(clk, baud * (bdiv + 1)); |
d9bb3fb1 | 349 | if (cd < 1 || cd > CDNS_UART_CD_MAX) |
61ec9016 JL |
350 | continue; |
351 | ||
e6b39bfd | 352 | calc_baud = clk / (cd * (bdiv + 1)); |
61ec9016 JL |
353 | |
354 | if (baud > calc_baud) | |
355 | bauderror = baud - calc_baud; | |
356 | else | |
357 | bauderror = calc_baud - baud; | |
358 | ||
e6b39bfd SB |
359 | if (besterror > bauderror) { |
360 | *rbdiv = bdiv; | |
361 | *rcd = cd; | |
362 | bestbaud = calc_baud; | |
363 | besterror = bauderror; | |
61ec9016 JL |
364 | } |
365 | } | |
e6b39bfd SB |
366 | /* use the values when percent error is acceptable */ |
367 | if (((besterror * 100) / baud) < 3) | |
368 | bestbaud = baud; | |
369 | ||
370 | return bestbaud; | |
371 | } | |
61ec9016 | 372 | |
e6b39bfd | 373 | /** |
d9bb3fb1 | 374 | * cdns_uart_set_baud_rate - Calculate and set the baud rate |
e6b39bfd SB |
375 | * @port: Handle to the uart port structure |
376 | * @baud: Baud rate to set | |
489810a1 | 377 | * Return: baud rate, requested baud when possible, or actual baud when there |
e6b39bfd SB |
378 | * was too much error, zero if no valid divisors are found. |
379 | */ | |
d9bb3fb1 | 380 | static unsigned int cdns_uart_set_baud_rate(struct uart_port *port, |
e6b39bfd SB |
381 | unsigned int baud) |
382 | { | |
383 | unsigned int calc_baud; | |
d54b181e | 384 | u32 cd = 0, bdiv = 0; |
e6b39bfd SB |
385 | u32 mreg; |
386 | int div8; | |
d9bb3fb1 | 387 | struct cdns_uart *cdns_uart = port->private_data; |
e6b39bfd | 388 | |
d9bb3fb1 | 389 | calc_baud = cdns_uart_calc_baud_divs(port->uartclk, baud, &bdiv, &cd, |
e6b39bfd SB |
390 | &div8); |
391 | ||
392 | /* Write new divisors to hardware */ | |
a8df6a51 | 393 | mreg = readl(port->membase + CDNS_UART_MR); |
e6b39bfd | 394 | if (div8) |
d9bb3fb1 | 395 | mreg |= CDNS_UART_MR_CLKSEL; |
e6b39bfd | 396 | else |
d9bb3fb1 | 397 | mreg &= ~CDNS_UART_MR_CLKSEL; |
a8df6a51 SB |
398 | writel(mreg, port->membase + CDNS_UART_MR); |
399 | writel(cd, port->membase + CDNS_UART_BAUDGEN); | |
400 | writel(bdiv, port->membase + CDNS_UART_BAUDDIV); | |
d9bb3fb1 | 401 | cdns_uart->baud = baud; |
61ec9016 JL |
402 | |
403 | return calc_baud; | |
404 | } | |
405 | ||
7ac57347 | 406 | #ifdef CONFIG_COMMON_CLK |
c4b0510c | 407 | /** |
d9bb3fb1 | 408 | * cdns_uart_clk_notitifer_cb - Clock notifier callback |
c4b0510c SB |
409 | * @nb: Notifier block |
410 | * @event: Notify event | |
411 | * @data: Notifier data | |
e555a211 | 412 | * Return: NOTIFY_OK or NOTIFY_DONE on success, NOTIFY_BAD on error. |
c4b0510c | 413 | */ |
d9bb3fb1 | 414 | static int cdns_uart_clk_notifier_cb(struct notifier_block *nb, |
c4b0510c SB |
415 | unsigned long event, void *data) |
416 | { | |
417 | u32 ctrl_reg; | |
418 | struct uart_port *port; | |
419 | int locked = 0; | |
420 | struct clk_notifier_data *ndata = data; | |
421 | unsigned long flags = 0; | |
d9bb3fb1 | 422 | struct cdns_uart *cdns_uart = to_cdns_uart(nb); |
c4b0510c | 423 | |
d9bb3fb1 | 424 | port = cdns_uart->port; |
c4b0510c SB |
425 | if (port->suspended) |
426 | return NOTIFY_OK; | |
427 | ||
428 | switch (event) { | |
429 | case PRE_RATE_CHANGE: | |
430 | { | |
e555a211 | 431 | u32 bdiv, cd; |
c4b0510c SB |
432 | int div8; |
433 | ||
434 | /* | |
435 | * Find out if current baud-rate can be achieved with new clock | |
436 | * frequency. | |
437 | */ | |
d9bb3fb1 | 438 | if (!cdns_uart_calc_baud_divs(ndata->new_rate, cdns_uart->baud, |
5ce15d2d SB |
439 | &bdiv, &cd, &div8)) { |
440 | dev_warn(port->dev, "clock rate change rejected\n"); | |
c4b0510c | 441 | return NOTIFY_BAD; |
5ce15d2d | 442 | } |
c4b0510c | 443 | |
d9bb3fb1 | 444 | spin_lock_irqsave(&cdns_uart->port->lock, flags); |
c4b0510c SB |
445 | |
446 | /* Disable the TX and RX to set baud rate */ | |
a8df6a51 | 447 | ctrl_reg = readl(port->membase + CDNS_UART_CR); |
d9bb3fb1 | 448 | ctrl_reg |= CDNS_UART_CR_TX_DIS | CDNS_UART_CR_RX_DIS; |
a8df6a51 | 449 | writel(ctrl_reg, port->membase + CDNS_UART_CR); |
c4b0510c | 450 | |
d9bb3fb1 | 451 | spin_unlock_irqrestore(&cdns_uart->port->lock, flags); |
c4b0510c SB |
452 | |
453 | return NOTIFY_OK; | |
454 | } | |
455 | case POST_RATE_CHANGE: | |
456 | /* | |
457 | * Set clk dividers to generate correct baud with new clock | |
458 | * frequency. | |
459 | */ | |
460 | ||
d9bb3fb1 | 461 | spin_lock_irqsave(&cdns_uart->port->lock, flags); |
c4b0510c SB |
462 | |
463 | locked = 1; | |
464 | port->uartclk = ndata->new_rate; | |
465 | ||
d9bb3fb1 SB |
466 | cdns_uart->baud = cdns_uart_set_baud_rate(cdns_uart->port, |
467 | cdns_uart->baud); | |
c4b0510c SB |
468 | /* fall through */ |
469 | case ABORT_RATE_CHANGE: | |
470 | if (!locked) | |
d9bb3fb1 | 471 | spin_lock_irqsave(&cdns_uart->port->lock, flags); |
c4b0510c SB |
472 | |
473 | /* Set TX/RX Reset */ | |
a8df6a51 | 474 | ctrl_reg = readl(port->membase + CDNS_UART_CR); |
d9bb3fb1 | 475 | ctrl_reg |= CDNS_UART_CR_TXRST | CDNS_UART_CR_RXRST; |
a8df6a51 | 476 | writel(ctrl_reg, port->membase + CDNS_UART_CR); |
c4b0510c | 477 | |
a8df6a51 | 478 | while (readl(port->membase + CDNS_UART_CR) & |
d9bb3fb1 | 479 | (CDNS_UART_CR_TXRST | CDNS_UART_CR_RXRST)) |
c4b0510c SB |
480 | cpu_relax(); |
481 | ||
482 | /* | |
483 | * Clear the RX disable and TX disable bits and then set the TX | |
484 | * enable bit and RX enable bit to enable the transmitter and | |
485 | * receiver. | |
486 | */ | |
a8df6a51 SB |
487 | writel(rx_timeout, port->membase + CDNS_UART_RXTOUT); |
488 | ctrl_reg = readl(port->membase + CDNS_UART_CR); | |
d9bb3fb1 SB |
489 | ctrl_reg &= ~(CDNS_UART_CR_TX_DIS | CDNS_UART_CR_RX_DIS); |
490 | ctrl_reg |= CDNS_UART_CR_TX_EN | CDNS_UART_CR_RX_EN; | |
a8df6a51 | 491 | writel(ctrl_reg, port->membase + CDNS_UART_CR); |
c4b0510c | 492 | |
d9bb3fb1 | 493 | spin_unlock_irqrestore(&cdns_uart->port->lock, flags); |
c4b0510c SB |
494 | |
495 | return NOTIFY_OK; | |
496 | default: | |
497 | return NOTIFY_DONE; | |
498 | } | |
499 | } | |
7ac57347 | 500 | #endif |
c4b0510c | 501 | |
61ec9016 | 502 | /** |
d9bb3fb1 | 503 | * cdns_uart_start_tx - Start transmitting bytes |
61ec9016 | 504 | * @port: Handle to the uart port structure |
489810a1 | 505 | */ |
d9bb3fb1 | 506 | static void cdns_uart_start_tx(struct uart_port *port) |
61ec9016 | 507 | { |
07986580 | 508 | unsigned int status; |
61ec9016 | 509 | |
ea8dd8e5 | 510 | if (uart_tx_stopped(port)) |
61ec9016 JL |
511 | return; |
512 | ||
e3538c37 SB |
513 | /* |
514 | * Set the TX enable bit and clear the TX disable bit to enable the | |
61ec9016 JL |
515 | * transmitter. |
516 | */ | |
a8df6a51 | 517 | status = readl(port->membase + CDNS_UART_CR); |
e3538c37 SB |
518 | status &= ~CDNS_UART_CR_TX_DIS; |
519 | status |= CDNS_UART_CR_TX_EN; | |
a8df6a51 | 520 | writel(status, port->membase + CDNS_UART_CR); |
61ec9016 | 521 | |
ea8dd8e5 SB |
522 | if (uart_circ_empty(&port->state->xmit)) |
523 | return; | |
524 | ||
07986580 | 525 | cdns_uart_handle_tx(port); |
61ec9016 | 526 | |
a8df6a51 | 527 | writel(CDNS_UART_IXR_TXEMPTY, port->membase + CDNS_UART_ISR); |
61ec9016 | 528 | /* Enable the TX Empty interrupt */ |
a8df6a51 | 529 | writel(CDNS_UART_IXR_TXEMPTY, port->membase + CDNS_UART_IER); |
61ec9016 JL |
530 | } |
531 | ||
532 | /** | |
d9bb3fb1 | 533 | * cdns_uart_stop_tx - Stop TX |
61ec9016 | 534 | * @port: Handle to the uart port structure |
489810a1 | 535 | */ |
d9bb3fb1 | 536 | static void cdns_uart_stop_tx(struct uart_port *port) |
61ec9016 JL |
537 | { |
538 | unsigned int regval; | |
539 | ||
a8df6a51 | 540 | regval = readl(port->membase + CDNS_UART_CR); |
d9bb3fb1 | 541 | regval |= CDNS_UART_CR_TX_DIS; |
61ec9016 | 542 | /* Disable the transmitter */ |
a8df6a51 | 543 | writel(regval, port->membase + CDNS_UART_CR); |
61ec9016 JL |
544 | } |
545 | ||
546 | /** | |
d9bb3fb1 | 547 | * cdns_uart_stop_rx - Stop RX |
61ec9016 | 548 | * @port: Handle to the uart port structure |
489810a1 | 549 | */ |
d9bb3fb1 | 550 | static void cdns_uart_stop_rx(struct uart_port *port) |
61ec9016 JL |
551 | { |
552 | unsigned int regval; | |
553 | ||
373e882f | 554 | /* Disable RX IRQs */ |
a8df6a51 | 555 | writel(CDNS_UART_RX_IRQS, port->membase + CDNS_UART_IDR); |
373e882f SB |
556 | |
557 | /* Disable the receiver */ | |
a8df6a51 | 558 | regval = readl(port->membase + CDNS_UART_CR); |
d9bb3fb1 | 559 | regval |= CDNS_UART_CR_RX_DIS; |
a8df6a51 | 560 | writel(regval, port->membase + CDNS_UART_CR); |
61ec9016 JL |
561 | } |
562 | ||
563 | /** | |
d9bb3fb1 | 564 | * cdns_uart_tx_empty - Check whether TX is empty |
61ec9016 JL |
565 | * @port: Handle to the uart port structure |
566 | * | |
489810a1 MS |
567 | * Return: TIOCSER_TEMT on success, 0 otherwise |
568 | */ | |
d9bb3fb1 | 569 | static unsigned int cdns_uart_tx_empty(struct uart_port *port) |
61ec9016 JL |
570 | { |
571 | unsigned int status; | |
572 | ||
a8df6a51 | 573 | status = readl(port->membase + CDNS_UART_SR) & |
19f22efd | 574 | CDNS_UART_SR_TXEMPTY; |
61ec9016 JL |
575 | return status ? TIOCSER_TEMT : 0; |
576 | } | |
577 | ||
578 | /** | |
d9bb3fb1 | 579 | * cdns_uart_break_ctl - Based on the input ctl we have to start or stop |
61ec9016 JL |
580 | * transmitting char breaks |
581 | * @port: Handle to the uart port structure | |
582 | * @ctl: Value based on which start or stop decision is taken | |
489810a1 | 583 | */ |
d9bb3fb1 | 584 | static void cdns_uart_break_ctl(struct uart_port *port, int ctl) |
61ec9016 JL |
585 | { |
586 | unsigned int status; | |
587 | unsigned long flags; | |
588 | ||
589 | spin_lock_irqsave(&port->lock, flags); | |
590 | ||
a8df6a51 | 591 | status = readl(port->membase + CDNS_UART_CR); |
61ec9016 JL |
592 | |
593 | if (ctl == -1) | |
19f22efd | 594 | writel(CDNS_UART_CR_STARTBRK | status, |
a8df6a51 | 595 | port->membase + CDNS_UART_CR); |
61ec9016 | 596 | else { |
d9bb3fb1 | 597 | if ((status & CDNS_UART_CR_STOPBRK) == 0) |
19f22efd | 598 | writel(CDNS_UART_CR_STOPBRK | status, |
a8df6a51 | 599 | port->membase + CDNS_UART_CR); |
61ec9016 JL |
600 | } |
601 | spin_unlock_irqrestore(&port->lock, flags); | |
602 | } | |
603 | ||
604 | /** | |
d9bb3fb1 | 605 | * cdns_uart_set_termios - termios operations, handling data length, parity, |
61ec9016 JL |
606 | * stop bits, flow control, baud rate |
607 | * @port: Handle to the uart port structure | |
608 | * @termios: Handle to the input termios structure | |
609 | * @old: Values of the previously saved termios structure | |
489810a1 | 610 | */ |
d9bb3fb1 | 611 | static void cdns_uart_set_termios(struct uart_port *port, |
61ec9016 JL |
612 | struct ktermios *termios, struct ktermios *old) |
613 | { | |
614 | unsigned int cval = 0; | |
e6b39bfd | 615 | unsigned int baud, minbaud, maxbaud; |
61ec9016 JL |
616 | unsigned long flags; |
617 | unsigned int ctrl_reg, mode_reg; | |
618 | ||
619 | spin_lock_irqsave(&port->lock, flags); | |
620 | ||
6ecde472 | 621 | /* Wait for the transmit FIFO to empty before making changes */ |
a8df6a51 | 622 | if (!(readl(port->membase + CDNS_UART_CR) & |
19f22efd | 623 | CDNS_UART_CR_TX_DIS)) { |
a8df6a51 | 624 | while (!(readl(port->membase + CDNS_UART_SR) & |
6ecde472 NR |
625 | CDNS_UART_SR_TXEMPTY)) { |
626 | cpu_relax(); | |
627 | } | |
61ec9016 JL |
628 | } |
629 | ||
630 | /* Disable the TX and RX to set baud rate */ | |
a8df6a51 | 631 | ctrl_reg = readl(port->membase + CDNS_UART_CR); |
d9bb3fb1 | 632 | ctrl_reg |= CDNS_UART_CR_TX_DIS | CDNS_UART_CR_RX_DIS; |
a8df6a51 | 633 | writel(ctrl_reg, port->membase + CDNS_UART_CR); |
61ec9016 | 634 | |
e6b39bfd SB |
635 | /* |
636 | * Min baud rate = 6bps and Max Baud Rate is 10Mbps for 100Mhz clk | |
637 | * min and max baud should be calculated here based on port->uartclk. | |
638 | * this way we get a valid baud and can safely call set_baud() | |
639 | */ | |
d9bb3fb1 SB |
640 | minbaud = port->uartclk / |
641 | ((CDNS_UART_BDIV_MAX + 1) * CDNS_UART_CD_MAX * 8); | |
642 | maxbaud = port->uartclk / (CDNS_UART_BDIV_MIN + 1); | |
e6b39bfd | 643 | baud = uart_get_baud_rate(port, termios, old, minbaud, maxbaud); |
d9bb3fb1 | 644 | baud = cdns_uart_set_baud_rate(port, baud); |
61ec9016 JL |
645 | if (tty_termios_baud_rate(termios)) |
646 | tty_termios_encode_baud_rate(termios, baud, baud); | |
647 | ||
e555a211 | 648 | /* Update the per-port timeout. */ |
61ec9016 JL |
649 | uart_update_timeout(port, termios->c_cflag, baud); |
650 | ||
651 | /* Set TX/RX Reset */ | |
a8df6a51 | 652 | ctrl_reg = readl(port->membase + CDNS_UART_CR); |
d9bb3fb1 | 653 | ctrl_reg |= CDNS_UART_CR_TXRST | CDNS_UART_CR_RXRST; |
a8df6a51 | 654 | writel(ctrl_reg, port->membase + CDNS_UART_CR); |
61ec9016 | 655 | |
e555a211 SB |
656 | /* |
657 | * Clear the RX disable and TX disable bits and then set the TX enable | |
61ec9016 JL |
658 | * bit and RX enable bit to enable the transmitter and receiver. |
659 | */ | |
a8df6a51 | 660 | ctrl_reg = readl(port->membase + CDNS_UART_CR); |
d9bb3fb1 SB |
661 | ctrl_reg &= ~(CDNS_UART_CR_TX_DIS | CDNS_UART_CR_RX_DIS); |
662 | ctrl_reg |= CDNS_UART_CR_TX_EN | CDNS_UART_CR_RX_EN; | |
a8df6a51 | 663 | writel(ctrl_reg, port->membase + CDNS_UART_CR); |
61ec9016 | 664 | |
a8df6a51 | 665 | writel(rx_timeout, port->membase + CDNS_UART_RXTOUT); |
61ec9016 | 666 | |
d9bb3fb1 SB |
667 | port->read_status_mask = CDNS_UART_IXR_TXEMPTY | CDNS_UART_IXR_RXTRIG | |
668 | CDNS_UART_IXR_OVERRUN | CDNS_UART_IXR_TOUT; | |
61ec9016 JL |
669 | port->ignore_status_mask = 0; |
670 | ||
671 | if (termios->c_iflag & INPCK) | |
d9bb3fb1 SB |
672 | port->read_status_mask |= CDNS_UART_IXR_PARITY | |
673 | CDNS_UART_IXR_FRAMING; | |
61ec9016 JL |
674 | |
675 | if (termios->c_iflag & IGNPAR) | |
d9bb3fb1 SB |
676 | port->ignore_status_mask |= CDNS_UART_IXR_PARITY | |
677 | CDNS_UART_IXR_FRAMING | CDNS_UART_IXR_OVERRUN; | |
61ec9016 JL |
678 | |
679 | /* ignore all characters if CREAD is not set */ | |
680 | if ((termios->c_cflag & CREAD) == 0) | |
d9bb3fb1 SB |
681 | port->ignore_status_mask |= CDNS_UART_IXR_RXTRIG | |
682 | CDNS_UART_IXR_TOUT | CDNS_UART_IXR_PARITY | | |
683 | CDNS_UART_IXR_FRAMING | CDNS_UART_IXR_OVERRUN; | |
61ec9016 | 684 | |
a8df6a51 | 685 | mode_reg = readl(port->membase + CDNS_UART_MR); |
61ec9016 JL |
686 | |
687 | /* Handling Data Size */ | |
688 | switch (termios->c_cflag & CSIZE) { | |
689 | case CS6: | |
d9bb3fb1 | 690 | cval |= CDNS_UART_MR_CHARLEN_6_BIT; |
61ec9016 JL |
691 | break; |
692 | case CS7: | |
d9bb3fb1 | 693 | cval |= CDNS_UART_MR_CHARLEN_7_BIT; |
61ec9016 JL |
694 | break; |
695 | default: | |
696 | case CS8: | |
d9bb3fb1 | 697 | cval |= CDNS_UART_MR_CHARLEN_8_BIT; |
61ec9016 JL |
698 | termios->c_cflag &= ~CSIZE; |
699 | termios->c_cflag |= CS8; | |
700 | break; | |
701 | } | |
702 | ||
703 | /* Handling Parity and Stop Bits length */ | |
704 | if (termios->c_cflag & CSTOPB) | |
d9bb3fb1 | 705 | cval |= CDNS_UART_MR_STOPMODE_2_BIT; /* 2 STOP bits */ |
61ec9016 | 706 | else |
d9bb3fb1 | 707 | cval |= CDNS_UART_MR_STOPMODE_1_BIT; /* 1 STOP bit */ |
61ec9016 JL |
708 | |
709 | if (termios->c_cflag & PARENB) { | |
710 | /* Mark or Space parity */ | |
711 | if (termios->c_cflag & CMSPAR) { | |
712 | if (termios->c_cflag & PARODD) | |
d9bb3fb1 | 713 | cval |= CDNS_UART_MR_PARITY_MARK; |
61ec9016 | 714 | else |
d9bb3fb1 | 715 | cval |= CDNS_UART_MR_PARITY_SPACE; |
e6b39bfd SB |
716 | } else { |
717 | if (termios->c_cflag & PARODD) | |
d9bb3fb1 | 718 | cval |= CDNS_UART_MR_PARITY_ODD; |
61ec9016 | 719 | else |
d9bb3fb1 | 720 | cval |= CDNS_UART_MR_PARITY_EVEN; |
e6b39bfd SB |
721 | } |
722 | } else { | |
d9bb3fb1 | 723 | cval |= CDNS_UART_MR_PARITY_NONE; |
e6b39bfd SB |
724 | } |
725 | cval |= mode_reg & 1; | |
a8df6a51 | 726 | writel(cval, port->membase + CDNS_UART_MR); |
61ec9016 JL |
727 | |
728 | spin_unlock_irqrestore(&port->lock, flags); | |
729 | } | |
730 | ||
731 | /** | |
d9bb3fb1 | 732 | * cdns_uart_startup - Called when an application opens a cdns_uart port |
61ec9016 JL |
733 | * @port: Handle to the uart port structure |
734 | * | |
e555a211 | 735 | * Return: 0 on success, negative errno otherwise |
489810a1 | 736 | */ |
d9bb3fb1 | 737 | static int cdns_uart_startup(struct uart_port *port) |
61ec9016 | 738 | { |
55861d11 | 739 | int ret; |
6e14f7c1 | 740 | unsigned long flags; |
55861d11 | 741 | unsigned int status = 0; |
61ec9016 | 742 | |
6e14f7c1 SB |
743 | spin_lock_irqsave(&port->lock, flags); |
744 | ||
61ec9016 | 745 | /* Disable the TX and RX */ |
19f22efd | 746 | writel(CDNS_UART_CR_TX_DIS | CDNS_UART_CR_RX_DIS, |
a8df6a51 | 747 | port->membase + CDNS_UART_CR); |
61ec9016 JL |
748 | |
749 | /* Set the Control Register with TX/RX Enable, TX/RX Reset, | |
750 | * no break chars. | |
751 | */ | |
19f22efd | 752 | writel(CDNS_UART_CR_TXRST | CDNS_UART_CR_RXRST, |
a8df6a51 | 753 | port->membase + CDNS_UART_CR); |
61ec9016 | 754 | |
6e14f7c1 SB |
755 | /* |
756 | * Clear the RX disable bit and then set the RX enable bit to enable | |
757 | * the receiver. | |
61ec9016 | 758 | */ |
a8df6a51 | 759 | status = readl(port->membase + CDNS_UART_CR); |
6e14f7c1 SB |
760 | status &= CDNS_UART_CR_RX_DIS; |
761 | status |= CDNS_UART_CR_RX_EN; | |
a8df6a51 | 762 | writel(status, port->membase + CDNS_UART_CR); |
61ec9016 JL |
763 | |
764 | /* Set the Mode Register with normal mode,8 data bits,1 stop bit, | |
765 | * no parity. | |
766 | */ | |
19f22efd | 767 | writel(CDNS_UART_MR_CHMODE_NORM | CDNS_UART_MR_STOPMODE_1_BIT |
d9bb3fb1 | 768 | | CDNS_UART_MR_PARITY_NONE | CDNS_UART_MR_CHARLEN_8_BIT, |
a8df6a51 | 769 | port->membase + CDNS_UART_MR); |
61ec9016 | 770 | |
85baf542 S |
771 | /* |
772 | * Set the RX FIFO Trigger level to use most of the FIFO, but it | |
773 | * can be tuned with a module parameter | |
774 | */ | |
a8df6a51 | 775 | writel(rx_trigger_level, port->membase + CDNS_UART_RXWM); |
61ec9016 | 776 | |
85baf542 S |
777 | /* |
778 | * Receive Timeout register is enabled but it | |
779 | * can be tuned with a module parameter | |
780 | */ | |
a8df6a51 | 781 | writel(rx_timeout, port->membase + CDNS_UART_RXTOUT); |
61ec9016 | 782 | |
855f6fd9 | 783 | /* Clear out any pending interrupts before enabling them */ |
a8df6a51 SB |
784 | writel(readl(port->membase + CDNS_UART_ISR), |
785 | port->membase + CDNS_UART_ISR); | |
61ec9016 | 786 | |
55861d11 SB |
787 | spin_unlock_irqrestore(&port->lock, flags); |
788 | ||
789 | ret = request_irq(port->irq, cdns_uart_isr, 0, CDNS_UART_NAME, port); | |
790 | if (ret) { | |
791 | dev_err(port->dev, "request_irq '%d' failed with %d\n", | |
792 | port->irq, ret); | |
793 | return ret; | |
794 | } | |
795 | ||
61ec9016 | 796 | /* Set the Interrupt Registers with desired interrupts */ |
a8df6a51 | 797 | writel(CDNS_UART_RX_IRQS, port->membase + CDNS_UART_IER); |
61ec9016 | 798 | |
55861d11 | 799 | return 0; |
61ec9016 JL |
800 | } |
801 | ||
802 | /** | |
d9bb3fb1 | 803 | * cdns_uart_shutdown - Called when an application closes a cdns_uart port |
61ec9016 | 804 | * @port: Handle to the uart port structure |
489810a1 | 805 | */ |
d9bb3fb1 | 806 | static void cdns_uart_shutdown(struct uart_port *port) |
61ec9016 JL |
807 | { |
808 | int status; | |
a19eda0f SB |
809 | unsigned long flags; |
810 | ||
811 | spin_lock_irqsave(&port->lock, flags); | |
61ec9016 JL |
812 | |
813 | /* Disable interrupts */ | |
a8df6a51 SB |
814 | status = readl(port->membase + CDNS_UART_IMR); |
815 | writel(status, port->membase + CDNS_UART_IDR); | |
816 | writel(0xffffffff, port->membase + CDNS_UART_ISR); | |
61ec9016 JL |
817 | |
818 | /* Disable the TX and RX */ | |
19f22efd | 819 | writel(CDNS_UART_CR_TX_DIS | CDNS_UART_CR_RX_DIS, |
a8df6a51 | 820 | port->membase + CDNS_UART_CR); |
a19eda0f SB |
821 | |
822 | spin_unlock_irqrestore(&port->lock, flags); | |
823 | ||
61ec9016 JL |
824 | free_irq(port->irq, port); |
825 | } | |
826 | ||
827 | /** | |
d9bb3fb1 | 828 | * cdns_uart_type - Set UART type to cdns_uart port |
61ec9016 JL |
829 | * @port: Handle to the uart port structure |
830 | * | |
489810a1 MS |
831 | * Return: string on success, NULL otherwise |
832 | */ | |
d9bb3fb1 | 833 | static const char *cdns_uart_type(struct uart_port *port) |
61ec9016 | 834 | { |
d9bb3fb1 | 835 | return port->type == PORT_XUARTPS ? CDNS_UART_NAME : NULL; |
61ec9016 JL |
836 | } |
837 | ||
838 | /** | |
d9bb3fb1 | 839 | * cdns_uart_verify_port - Verify the port params |
61ec9016 JL |
840 | * @port: Handle to the uart port structure |
841 | * @ser: Handle to the structure whose members are compared | |
842 | * | |
e555a211 | 843 | * Return: 0 on success, negative errno otherwise. |
489810a1 | 844 | */ |
d9bb3fb1 | 845 | static int cdns_uart_verify_port(struct uart_port *port, |
61ec9016 JL |
846 | struct serial_struct *ser) |
847 | { | |
848 | if (ser->type != PORT_UNKNOWN && ser->type != PORT_XUARTPS) | |
849 | return -EINVAL; | |
850 | if (port->irq != ser->irq) | |
851 | return -EINVAL; | |
852 | if (ser->io_type != UPIO_MEM) | |
853 | return -EINVAL; | |
854 | if (port->iobase != ser->port) | |
855 | return -EINVAL; | |
856 | if (ser->hub6 != 0) | |
857 | return -EINVAL; | |
858 | return 0; | |
859 | } | |
860 | ||
861 | /** | |
d9bb3fb1 SB |
862 | * cdns_uart_request_port - Claim the memory region attached to cdns_uart port, |
863 | * called when the driver adds a cdns_uart port via | |
61ec9016 JL |
864 | * uart_add_one_port() |
865 | * @port: Handle to the uart port structure | |
866 | * | |
e555a211 | 867 | * Return: 0 on success, negative errno otherwise. |
489810a1 | 868 | */ |
d9bb3fb1 | 869 | static int cdns_uart_request_port(struct uart_port *port) |
61ec9016 | 870 | { |
d9bb3fb1 SB |
871 | if (!request_mem_region(port->mapbase, CDNS_UART_REGISTER_SPACE, |
872 | CDNS_UART_NAME)) { | |
61ec9016 JL |
873 | return -ENOMEM; |
874 | } | |
875 | ||
d9bb3fb1 | 876 | port->membase = ioremap(port->mapbase, CDNS_UART_REGISTER_SPACE); |
61ec9016 JL |
877 | if (!port->membase) { |
878 | dev_err(port->dev, "Unable to map registers\n"); | |
d9bb3fb1 | 879 | release_mem_region(port->mapbase, CDNS_UART_REGISTER_SPACE); |
61ec9016 JL |
880 | return -ENOMEM; |
881 | } | |
882 | return 0; | |
883 | } | |
884 | ||
885 | /** | |
d9bb3fb1 | 886 | * cdns_uart_release_port - Release UART port |
61ec9016 | 887 | * @port: Handle to the uart port structure |
e555a211 | 888 | * |
d9bb3fb1 SB |
889 | * Release the memory region attached to a cdns_uart port. Called when the |
890 | * driver removes a cdns_uart port via uart_remove_one_port(). | |
489810a1 | 891 | */ |
d9bb3fb1 | 892 | static void cdns_uart_release_port(struct uart_port *port) |
61ec9016 | 893 | { |
d9bb3fb1 | 894 | release_mem_region(port->mapbase, CDNS_UART_REGISTER_SPACE); |
61ec9016 JL |
895 | iounmap(port->membase); |
896 | port->membase = NULL; | |
897 | } | |
898 | ||
899 | /** | |
d9bb3fb1 | 900 | * cdns_uart_config_port - Configure UART port |
61ec9016 JL |
901 | * @port: Handle to the uart port structure |
902 | * @flags: If any | |
489810a1 | 903 | */ |
d9bb3fb1 | 904 | static void cdns_uart_config_port(struct uart_port *port, int flags) |
61ec9016 | 905 | { |
d9bb3fb1 | 906 | if (flags & UART_CONFIG_TYPE && cdns_uart_request_port(port) == 0) |
61ec9016 JL |
907 | port->type = PORT_XUARTPS; |
908 | } | |
909 | ||
910 | /** | |
d9bb3fb1 | 911 | * cdns_uart_get_mctrl - Get the modem control state |
61ec9016 JL |
912 | * @port: Handle to the uart port structure |
913 | * | |
489810a1 MS |
914 | * Return: the modem control state |
915 | */ | |
d9bb3fb1 | 916 | static unsigned int cdns_uart_get_mctrl(struct uart_port *port) |
61ec9016 JL |
917 | { |
918 | return TIOCM_CTS | TIOCM_DSR | TIOCM_CAR; | |
919 | } | |
920 | ||
d9bb3fb1 | 921 | static void cdns_uart_set_mctrl(struct uart_port *port, unsigned int mctrl) |
61ec9016 | 922 | { |
19038ad9 LPC |
923 | u32 val; |
924 | ||
a8df6a51 | 925 | val = readl(port->membase + CDNS_UART_MODEMCR); |
19038ad9 LPC |
926 | |
927 | val &= ~(CDNS_UART_MODEMCR_RTS | CDNS_UART_MODEMCR_DTR); | |
928 | ||
929 | if (mctrl & TIOCM_RTS) | |
930 | val |= CDNS_UART_MODEMCR_RTS; | |
931 | if (mctrl & TIOCM_DTR) | |
932 | val |= CDNS_UART_MODEMCR_DTR; | |
933 | ||
a8df6a51 | 934 | writel(val, port->membase + CDNS_UART_MODEMCR); |
61ec9016 JL |
935 | } |
936 | ||
6ee04c6c | 937 | #ifdef CONFIG_CONSOLE_POLL |
d9bb3fb1 | 938 | static int cdns_uart_poll_get_char(struct uart_port *port) |
6ee04c6c | 939 | { |
6ee04c6c | 940 | int c; |
f0f54a80 | 941 | unsigned long flags; |
6ee04c6c | 942 | |
f0f54a80 | 943 | spin_lock_irqsave(&port->lock, flags); |
6ee04c6c VL |
944 | |
945 | /* Check if FIFO is empty */ | |
a8df6a51 | 946 | if (readl(port->membase + CDNS_UART_SR) & CDNS_UART_SR_RXEMPTY) |
6ee04c6c VL |
947 | c = NO_POLL_CHAR; |
948 | else /* Read a character */ | |
a8df6a51 | 949 | c = (unsigned char) readl(port->membase + CDNS_UART_FIFO); |
6ee04c6c | 950 | |
f0f54a80 | 951 | spin_unlock_irqrestore(&port->lock, flags); |
6ee04c6c VL |
952 | |
953 | return c; | |
954 | } | |
955 | ||
d9bb3fb1 | 956 | static void cdns_uart_poll_put_char(struct uart_port *port, unsigned char c) |
6ee04c6c | 957 | { |
f0f54a80 | 958 | unsigned long flags; |
6ee04c6c | 959 | |
f0f54a80 | 960 | spin_lock_irqsave(&port->lock, flags); |
6ee04c6c VL |
961 | |
962 | /* Wait until FIFO is empty */ | |
a8df6a51 | 963 | while (!(readl(port->membase + CDNS_UART_SR) & CDNS_UART_SR_TXEMPTY)) |
6ee04c6c VL |
964 | cpu_relax(); |
965 | ||
966 | /* Write a character */ | |
a8df6a51 | 967 | writel(c, port->membase + CDNS_UART_FIFO); |
6ee04c6c VL |
968 | |
969 | /* Wait until FIFO is empty */ | |
a8df6a51 | 970 | while (!(readl(port->membase + CDNS_UART_SR) & CDNS_UART_SR_TXEMPTY)) |
6ee04c6c VL |
971 | cpu_relax(); |
972 | ||
f0f54a80 | 973 | spin_unlock_irqrestore(&port->lock, flags); |
6ee04c6c VL |
974 | |
975 | return; | |
976 | } | |
977 | #endif | |
978 | ||
210417ce SD |
979 | static void cdns_uart_pm(struct uart_port *port, unsigned int state, |
980 | unsigned int oldstate) | |
981 | { | |
982 | struct cdns_uart *cdns_uart = port->private_data; | |
983 | ||
984 | switch (state) { | |
985 | case UART_PM_STATE_OFF: | |
986 | clk_disable(cdns_uart->uartclk); | |
987 | clk_disable(cdns_uart->pclk); | |
988 | break; | |
989 | default: | |
990 | clk_enable(cdns_uart->pclk); | |
991 | clk_enable(cdns_uart->uartclk); | |
992 | break; | |
993 | } | |
994 | } | |
995 | ||
f098a0ae | 996 | static const struct uart_ops cdns_uart_ops = { |
d9bb3fb1 SB |
997 | .set_mctrl = cdns_uart_set_mctrl, |
998 | .get_mctrl = cdns_uart_get_mctrl, | |
d9bb3fb1 SB |
999 | .start_tx = cdns_uart_start_tx, |
1000 | .stop_tx = cdns_uart_stop_tx, | |
1001 | .stop_rx = cdns_uart_stop_rx, | |
1002 | .tx_empty = cdns_uart_tx_empty, | |
1003 | .break_ctl = cdns_uart_break_ctl, | |
1004 | .set_termios = cdns_uart_set_termios, | |
1005 | .startup = cdns_uart_startup, | |
1006 | .shutdown = cdns_uart_shutdown, | |
210417ce | 1007 | .pm = cdns_uart_pm, |
d9bb3fb1 SB |
1008 | .type = cdns_uart_type, |
1009 | .verify_port = cdns_uart_verify_port, | |
1010 | .request_port = cdns_uart_request_port, | |
1011 | .release_port = cdns_uart_release_port, | |
1012 | .config_port = cdns_uart_config_port, | |
6ee04c6c | 1013 | #ifdef CONFIG_CONSOLE_POLL |
d9bb3fb1 SB |
1014 | .poll_get_char = cdns_uart_poll_get_char, |
1015 | .poll_put_char = cdns_uart_poll_put_char, | |
6ee04c6c | 1016 | #endif |
61ec9016 JL |
1017 | }; |
1018 | ||
6db6df0e | 1019 | static struct uart_port cdns_uart_port[CDNS_UART_NR_PORTS]; |
61ec9016 JL |
1020 | |
1021 | /** | |
d9bb3fb1 | 1022 | * cdns_uart_get_port - Configure the port from platform device resource info |
928e9263 MS |
1023 | * @id: Port id |
1024 | * | |
489810a1 MS |
1025 | * Return: a pointer to a uart_port or NULL for failure |
1026 | */ | |
d9bb3fb1 | 1027 | static struct uart_port *cdns_uart_get_port(int id) |
61ec9016 JL |
1028 | { |
1029 | struct uart_port *port; | |
61ec9016 | 1030 | |
928e9263 | 1031 | /* Try the given port id if failed use default method */ |
d9bb3fb1 | 1032 | if (cdns_uart_port[id].mapbase != 0) { |
928e9263 | 1033 | /* Find the next unused port */ |
d9bb3fb1 SB |
1034 | for (id = 0; id < CDNS_UART_NR_PORTS; id++) |
1035 | if (cdns_uart_port[id].mapbase == 0) | |
928e9263 MS |
1036 | break; |
1037 | } | |
61ec9016 | 1038 | |
d9bb3fb1 | 1039 | if (id >= CDNS_UART_NR_PORTS) |
61ec9016 JL |
1040 | return NULL; |
1041 | ||
d9bb3fb1 | 1042 | port = &cdns_uart_port[id]; |
61ec9016 JL |
1043 | |
1044 | /* At this point, we've got an empty uart_port struct, initialize it */ | |
1045 | spin_lock_init(&port->lock); | |
1046 | port->membase = NULL; | |
61ec9016 JL |
1047 | port->irq = 0; |
1048 | port->type = PORT_UNKNOWN; | |
1049 | port->iotype = UPIO_MEM32; | |
1050 | port->flags = UPF_BOOT_AUTOCONF; | |
d9bb3fb1 SB |
1051 | port->ops = &cdns_uart_ops; |
1052 | port->fifosize = CDNS_UART_FIFO_SIZE; | |
61ec9016 JL |
1053 | port->line = id; |
1054 | port->dev = NULL; | |
1055 | return port; | |
1056 | } | |
1057 | ||
61ec9016 JL |
1058 | #ifdef CONFIG_SERIAL_XILINX_PS_UART_CONSOLE |
1059 | /** | |
d9bb3fb1 | 1060 | * cdns_uart_console_wait_tx - Wait for the TX to be full |
61ec9016 | 1061 | * @port: Handle to the uart port structure |
489810a1 | 1062 | */ |
d9bb3fb1 | 1063 | static void cdns_uart_console_wait_tx(struct uart_port *port) |
61ec9016 | 1064 | { |
a8df6a51 | 1065 | while (!(readl(port->membase + CDNS_UART_SR) & CDNS_UART_SR_TXEMPTY)) |
61ec9016 JL |
1066 | barrier(); |
1067 | } | |
1068 | ||
1069 | /** | |
d9bb3fb1 | 1070 | * cdns_uart_console_putchar - write the character to the FIFO buffer |
61ec9016 JL |
1071 | * @port: Handle to the uart port structure |
1072 | * @ch: Character to be written | |
489810a1 | 1073 | */ |
d9bb3fb1 | 1074 | static void cdns_uart_console_putchar(struct uart_port *port, int ch) |
61ec9016 | 1075 | { |
d9bb3fb1 | 1076 | cdns_uart_console_wait_tx(port); |
a8df6a51 | 1077 | writel(ch, port->membase + CDNS_UART_FIFO); |
61ec9016 JL |
1078 | } |
1079 | ||
54585ba0 MY |
1080 | static void __init cdns_early_write(struct console *con, const char *s, |
1081 | unsigned n) | |
6fa62fc4 MS |
1082 | { |
1083 | struct earlycon_device *dev = con->data; | |
1084 | ||
1085 | uart_console_write(&dev->port, s, n, cdns_uart_console_putchar); | |
1086 | } | |
1087 | ||
1088 | static int __init cdns_early_console_setup(struct earlycon_device *device, | |
1089 | const char *opt) | |
1090 | { | |
1091 | if (!device->port.membase) | |
1092 | return -ENODEV; | |
1093 | ||
1094 | device->con->write = cdns_early_write; | |
1095 | ||
1096 | return 0; | |
1097 | } | |
93d7bbaa MS |
1098 | OF_EARLYCON_DECLARE(cdns, "xlnx,xuartps", cdns_early_console_setup); |
1099 | OF_EARLYCON_DECLARE(cdns, "cdns,uart-r1p8", cdns_early_console_setup); | |
1100 | OF_EARLYCON_DECLARE(cdns, "cdns,uart-r1p12", cdns_early_console_setup); | |
6fa62fc4 | 1101 | |
61ec9016 | 1102 | /** |
d9bb3fb1 | 1103 | * cdns_uart_console_write - perform write operation |
489810a1 | 1104 | * @co: Console handle |
61ec9016 JL |
1105 | * @s: Pointer to character array |
1106 | * @count: No of characters | |
489810a1 | 1107 | */ |
d9bb3fb1 | 1108 | static void cdns_uart_console_write(struct console *co, const char *s, |
61ec9016 JL |
1109 | unsigned int count) |
1110 | { | |
d9bb3fb1 | 1111 | struct uart_port *port = &cdns_uart_port[co->index]; |
61ec9016 | 1112 | unsigned long flags; |
d3755f5e | 1113 | unsigned int imr, ctrl; |
61ec9016 JL |
1114 | int locked = 1; |
1115 | ||
74ea66d4 SB |
1116 | if (port->sysrq) |
1117 | locked = 0; | |
1118 | else if (oops_in_progress) | |
61ec9016 JL |
1119 | locked = spin_trylock_irqsave(&port->lock, flags); |
1120 | else | |
1121 | spin_lock_irqsave(&port->lock, flags); | |
1122 | ||
1123 | /* save and disable interrupt */ | |
a8df6a51 SB |
1124 | imr = readl(port->membase + CDNS_UART_IMR); |
1125 | writel(imr, port->membase + CDNS_UART_IDR); | |
61ec9016 | 1126 | |
d3755f5e LPC |
1127 | /* |
1128 | * Make sure that the tx part is enabled. Set the TX enable bit and | |
1129 | * clear the TX disable bit to enable the transmitter. | |
1130 | */ | |
a8df6a51 | 1131 | ctrl = readl(port->membase + CDNS_UART_CR); |
e3538c37 SB |
1132 | ctrl &= ~CDNS_UART_CR_TX_DIS; |
1133 | ctrl |= CDNS_UART_CR_TX_EN; | |
a8df6a51 | 1134 | writel(ctrl, port->membase + CDNS_UART_CR); |
d3755f5e | 1135 | |
d9bb3fb1 SB |
1136 | uart_console_write(port, s, count, cdns_uart_console_putchar); |
1137 | cdns_uart_console_wait_tx(port); | |
61ec9016 | 1138 | |
a8df6a51 | 1139 | writel(ctrl, port->membase + CDNS_UART_CR); |
d3755f5e | 1140 | |
b494a5fa | 1141 | /* restore interrupt state */ |
a8df6a51 | 1142 | writel(imr, port->membase + CDNS_UART_IER); |
61ec9016 JL |
1143 | |
1144 | if (locked) | |
1145 | spin_unlock_irqrestore(&port->lock, flags); | |
1146 | } | |
1147 | ||
1148 | /** | |
d9bb3fb1 | 1149 | * cdns_uart_console_setup - Initialize the uart to default config |
61ec9016 JL |
1150 | * @co: Console handle |
1151 | * @options: Initial settings of uart | |
1152 | * | |
e555a211 | 1153 | * Return: 0 on success, negative errno otherwise. |
489810a1 | 1154 | */ |
d9bb3fb1 | 1155 | static int __init cdns_uart_console_setup(struct console *co, char *options) |
61ec9016 | 1156 | { |
d9bb3fb1 | 1157 | struct uart_port *port = &cdns_uart_port[co->index]; |
61ec9016 JL |
1158 | int baud = 9600; |
1159 | int bits = 8; | |
1160 | int parity = 'n'; | |
1161 | int flow = 'n'; | |
1162 | ||
d9bb3fb1 | 1163 | if (co->index < 0 || co->index >= CDNS_UART_NR_PORTS) |
61ec9016 JL |
1164 | return -EINVAL; |
1165 | ||
136debf7 | 1166 | if (!port->membase) { |
f6415491 PC |
1167 | pr_debug("console on " CDNS_UART_TTY_NAME "%i not present\n", |
1168 | co->index); | |
61ec9016 JL |
1169 | return -ENODEV; |
1170 | } | |
1171 | ||
1172 | if (options) | |
1173 | uart_parse_options(options, &baud, &parity, &bits, &flow); | |
1174 | ||
1175 | return uart_set_options(port, co, baud, parity, bits, flow); | |
1176 | } | |
1177 | ||
d9bb3fb1 | 1178 | static struct uart_driver cdns_uart_uart_driver; |
61ec9016 | 1179 | |
d9bb3fb1 SB |
1180 | static struct console cdns_uart_console = { |
1181 | .name = CDNS_UART_TTY_NAME, | |
1182 | .write = cdns_uart_console_write, | |
61ec9016 | 1183 | .device = uart_console_device, |
d9bb3fb1 | 1184 | .setup = cdns_uart_console_setup, |
61ec9016 JL |
1185 | .flags = CON_PRINTBUFFER, |
1186 | .index = -1, /* Specified on the cmdline (e.g. console=ttyPS ) */ | |
d9bb3fb1 | 1187 | .data = &cdns_uart_uart_driver, |
61ec9016 JL |
1188 | }; |
1189 | ||
1190 | /** | |
d9bb3fb1 | 1191 | * cdns_uart_console_init - Initialization call |
61ec9016 | 1192 | * |
e555a211 | 1193 | * Return: 0 on success, negative errno otherwise |
489810a1 | 1194 | */ |
d9bb3fb1 | 1195 | static int __init cdns_uart_console_init(void) |
61ec9016 | 1196 | { |
d9bb3fb1 | 1197 | register_console(&cdns_uart_console); |
61ec9016 JL |
1198 | return 0; |
1199 | } | |
1200 | ||
d9bb3fb1 | 1201 | console_initcall(cdns_uart_console_init); |
61ec9016 JL |
1202 | |
1203 | #endif /* CONFIG_SERIAL_XILINX_PS_UART_CONSOLE */ | |
1204 | ||
d9bb3fb1 | 1205 | static struct uart_driver cdns_uart_uart_driver = { |
e555a211 | 1206 | .owner = THIS_MODULE, |
d9bb3fb1 SB |
1207 | .driver_name = CDNS_UART_NAME, |
1208 | .dev_name = CDNS_UART_TTY_NAME, | |
1209 | .major = CDNS_UART_MAJOR, | |
1210 | .minor = CDNS_UART_MINOR, | |
1211 | .nr = CDNS_UART_NR_PORTS, | |
d3641f64 | 1212 | #ifdef CONFIG_SERIAL_XILINX_PS_UART_CONSOLE |
d9bb3fb1 | 1213 | .cons = &cdns_uart_console, |
d3641f64 SB |
1214 | #endif |
1215 | }; | |
1216 | ||
4b47d9aa SB |
1217 | #ifdef CONFIG_PM_SLEEP |
1218 | /** | |
d9bb3fb1 | 1219 | * cdns_uart_suspend - suspend event |
4b47d9aa SB |
1220 | * @device: Pointer to the device structure |
1221 | * | |
489810a1 | 1222 | * Return: 0 |
4b47d9aa | 1223 | */ |
d9bb3fb1 | 1224 | static int cdns_uart_suspend(struct device *device) |
4b47d9aa SB |
1225 | { |
1226 | struct uart_port *port = dev_get_drvdata(device); | |
1227 | struct tty_struct *tty; | |
1228 | struct device *tty_dev; | |
1229 | int may_wake = 0; | |
1230 | ||
1231 | /* Get the tty which could be NULL so don't assume it's valid */ | |
1232 | tty = tty_port_tty_get(&port->state->port); | |
1233 | if (tty) { | |
1234 | tty_dev = tty->dev; | |
1235 | may_wake = device_may_wakeup(tty_dev); | |
1236 | tty_kref_put(tty); | |
1237 | } | |
1238 | ||
1239 | /* | |
1240 | * Call the API provided in serial_core.c file which handles | |
1241 | * the suspend. | |
1242 | */ | |
d9bb3fb1 | 1243 | uart_suspend_port(&cdns_uart_uart_driver, port); |
4b47d9aa | 1244 | if (console_suspend_enabled && !may_wake) { |
d9bb3fb1 | 1245 | struct cdns_uart *cdns_uart = port->private_data; |
4b47d9aa | 1246 | |
d9bb3fb1 SB |
1247 | clk_disable(cdns_uart->uartclk); |
1248 | clk_disable(cdns_uart->pclk); | |
4b47d9aa SB |
1249 | } else { |
1250 | unsigned long flags = 0; | |
1251 | ||
1252 | spin_lock_irqsave(&port->lock, flags); | |
1253 | /* Empty the receive FIFO 1st before making changes */ | |
a8df6a51 | 1254 | while (!(readl(port->membase + CDNS_UART_SR) & |
d9bb3fb1 | 1255 | CDNS_UART_SR_RXEMPTY)) |
a8df6a51 | 1256 | readl(port->membase + CDNS_UART_FIFO); |
4b47d9aa | 1257 | /* set RX trigger level to 1 */ |
a8df6a51 | 1258 | writel(1, port->membase + CDNS_UART_RXWM); |
4b47d9aa | 1259 | /* disable RX timeout interrups */ |
a8df6a51 | 1260 | writel(CDNS_UART_IXR_TOUT, port->membase + CDNS_UART_IDR); |
4b47d9aa SB |
1261 | spin_unlock_irqrestore(&port->lock, flags); |
1262 | } | |
1263 | ||
1264 | return 0; | |
1265 | } | |
1266 | ||
1267 | /** | |
d9bb3fb1 | 1268 | * cdns_uart_resume - Resume after a previous suspend |
4b47d9aa SB |
1269 | * @device: Pointer to the device structure |
1270 | * | |
489810a1 | 1271 | * Return: 0 |
4b47d9aa | 1272 | */ |
d9bb3fb1 | 1273 | static int cdns_uart_resume(struct device *device) |
4b47d9aa SB |
1274 | { |
1275 | struct uart_port *port = dev_get_drvdata(device); | |
1276 | unsigned long flags = 0; | |
1277 | u32 ctrl_reg; | |
1278 | struct tty_struct *tty; | |
1279 | struct device *tty_dev; | |
1280 | int may_wake = 0; | |
1281 | ||
1282 | /* Get the tty which could be NULL so don't assume it's valid */ | |
1283 | tty = tty_port_tty_get(&port->state->port); | |
1284 | if (tty) { | |
1285 | tty_dev = tty->dev; | |
1286 | may_wake = device_may_wakeup(tty_dev); | |
1287 | tty_kref_put(tty); | |
1288 | } | |
1289 | ||
1290 | if (console_suspend_enabled && !may_wake) { | |
d9bb3fb1 | 1291 | struct cdns_uart *cdns_uart = port->private_data; |
4b47d9aa | 1292 | |
d9bb3fb1 SB |
1293 | clk_enable(cdns_uart->pclk); |
1294 | clk_enable(cdns_uart->uartclk); | |
4b47d9aa SB |
1295 | |
1296 | spin_lock_irqsave(&port->lock, flags); | |
1297 | ||
1298 | /* Set TX/RX Reset */ | |
a8df6a51 | 1299 | ctrl_reg = readl(port->membase + CDNS_UART_CR); |
d9bb3fb1 | 1300 | ctrl_reg |= CDNS_UART_CR_TXRST | CDNS_UART_CR_RXRST; |
a8df6a51 SB |
1301 | writel(ctrl_reg, port->membase + CDNS_UART_CR); |
1302 | while (readl(port->membase + CDNS_UART_CR) & | |
d9bb3fb1 | 1303 | (CDNS_UART_CR_TXRST | CDNS_UART_CR_RXRST)) |
4b47d9aa SB |
1304 | cpu_relax(); |
1305 | ||
1306 | /* restore rx timeout value */ | |
a8df6a51 | 1307 | writel(rx_timeout, port->membase + CDNS_UART_RXTOUT); |
4b47d9aa | 1308 | /* Enable Tx/Rx */ |
a8df6a51 | 1309 | ctrl_reg = readl(port->membase + CDNS_UART_CR); |
d9bb3fb1 SB |
1310 | ctrl_reg &= ~(CDNS_UART_CR_TX_DIS | CDNS_UART_CR_RX_DIS); |
1311 | ctrl_reg |= CDNS_UART_CR_TX_EN | CDNS_UART_CR_RX_EN; | |
a8df6a51 | 1312 | writel(ctrl_reg, port->membase + CDNS_UART_CR); |
4b47d9aa SB |
1313 | |
1314 | spin_unlock_irqrestore(&port->lock, flags); | |
1315 | } else { | |
1316 | spin_lock_irqsave(&port->lock, flags); | |
1317 | /* restore original rx trigger level */ | |
a8df6a51 | 1318 | writel(rx_trigger_level, port->membase + CDNS_UART_RXWM); |
4b47d9aa | 1319 | /* enable RX timeout interrupt */ |
a8df6a51 | 1320 | writel(CDNS_UART_IXR_TOUT, port->membase + CDNS_UART_IER); |
4b47d9aa SB |
1321 | spin_unlock_irqrestore(&port->lock, flags); |
1322 | } | |
1323 | ||
d9bb3fb1 | 1324 | return uart_resume_port(&cdns_uart_uart_driver, port); |
4b47d9aa SB |
1325 | } |
1326 | #endif /* ! CONFIG_PM_SLEEP */ | |
1327 | ||
d9bb3fb1 SB |
1328 | static SIMPLE_DEV_PM_OPS(cdns_uart_dev_pm_ops, cdns_uart_suspend, |
1329 | cdns_uart_resume); | |
4b47d9aa | 1330 | |
61ec9016 | 1331 | /** |
d9bb3fb1 | 1332 | * cdns_uart_probe - Platform driver probe |
61ec9016 JL |
1333 | * @pdev: Pointer to the platform device structure |
1334 | * | |
e555a211 | 1335 | * Return: 0 on success, negative errno otherwise |
489810a1 | 1336 | */ |
d9bb3fb1 | 1337 | static int cdns_uart_probe(struct platform_device *pdev) |
61ec9016 | 1338 | { |
5c90c07b | 1339 | int rc, id, irq; |
61ec9016 | 1340 | struct uart_port *port; |
5c90c07b | 1341 | struct resource *res; |
d9bb3fb1 | 1342 | struct cdns_uart *cdns_uart_data; |
61ec9016 | 1343 | |
d9bb3fb1 | 1344 | cdns_uart_data = devm_kzalloc(&pdev->dev, sizeof(*cdns_uart_data), |
c03cae17 | 1345 | GFP_KERNEL); |
d9bb3fb1 | 1346 | if (!cdns_uart_data) |
30e1e285 SB |
1347 | return -ENOMEM; |
1348 | ||
d9bb3fb1 SB |
1349 | cdns_uart_data->pclk = devm_clk_get(&pdev->dev, "pclk"); |
1350 | if (IS_ERR(cdns_uart_data->pclk)) { | |
1351 | cdns_uart_data->pclk = devm_clk_get(&pdev->dev, "aper_clk"); | |
1352 | if (!IS_ERR(cdns_uart_data->pclk)) | |
1353 | dev_err(&pdev->dev, "clock name 'aper_clk' is deprecated.\n"); | |
1354 | } | |
1355 | if (IS_ERR(cdns_uart_data->pclk)) { | |
1356 | dev_err(&pdev->dev, "pclk clock not found.\n"); | |
1357 | return PTR_ERR(cdns_uart_data->pclk); | |
1358 | } | |
1359 | ||
1360 | cdns_uart_data->uartclk = devm_clk_get(&pdev->dev, "uart_clk"); | |
1361 | if (IS_ERR(cdns_uart_data->uartclk)) { | |
1362 | cdns_uart_data->uartclk = devm_clk_get(&pdev->dev, "ref_clk"); | |
1363 | if (!IS_ERR(cdns_uart_data->uartclk)) | |
1364 | dev_err(&pdev->dev, "clock name 'ref_clk' is deprecated.\n"); | |
30e1e285 | 1365 | } |
d9bb3fb1 SB |
1366 | if (IS_ERR(cdns_uart_data->uartclk)) { |
1367 | dev_err(&pdev->dev, "uart_clk clock not found.\n"); | |
1368 | return PTR_ERR(cdns_uart_data->uartclk); | |
2326669c JC |
1369 | } |
1370 | ||
210417ce | 1371 | rc = clk_prepare(cdns_uart_data->pclk); |
30e1e285 | 1372 | if (rc) { |
d9bb3fb1 | 1373 | dev_err(&pdev->dev, "Unable to enable pclk clock.\n"); |
c03cae17 | 1374 | return rc; |
30e1e285 | 1375 | } |
210417ce | 1376 | rc = clk_prepare(cdns_uart_data->uartclk); |
2326669c | 1377 | if (rc) { |
30e1e285 | 1378 | dev_err(&pdev->dev, "Unable to enable device clock.\n"); |
d9bb3fb1 | 1379 | goto err_out_clk_dis_pclk; |
61ec9016 JL |
1380 | } |
1381 | ||
1382 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | |
30e1e285 SB |
1383 | if (!res) { |
1384 | rc = -ENODEV; | |
1385 | goto err_out_clk_disable; | |
1386 | } | |
61ec9016 | 1387 | |
5c90c07b MS |
1388 | irq = platform_get_irq(pdev, 0); |
1389 | if (irq <= 0) { | |
1390 | rc = -ENXIO; | |
30e1e285 SB |
1391 | goto err_out_clk_disable; |
1392 | } | |
61ec9016 | 1393 | |
7ac57347 | 1394 | #ifdef CONFIG_COMMON_CLK |
d9bb3fb1 SB |
1395 | cdns_uart_data->clk_rate_change_nb.notifier_call = |
1396 | cdns_uart_clk_notifier_cb; | |
1397 | if (clk_notifier_register(cdns_uart_data->uartclk, | |
1398 | &cdns_uart_data->clk_rate_change_nb)) | |
c4b0510c | 1399 | dev_warn(&pdev->dev, "Unable to register clock notifier.\n"); |
7ac57347 | 1400 | #endif |
928e9263 MS |
1401 | /* Look for a serialN alias */ |
1402 | id = of_alias_get_id(pdev->dev.of_node, "serial"); | |
1403 | if (id < 0) | |
1404 | id = 0; | |
c4b0510c | 1405 | |
61ec9016 | 1406 | /* Initialize the port structure */ |
d9bb3fb1 | 1407 | port = cdns_uart_get_port(id); |
61ec9016 JL |
1408 | |
1409 | if (!port) { | |
1410 | dev_err(&pdev->dev, "Cannot get uart_port structure\n"); | |
30e1e285 | 1411 | rc = -ENODEV; |
c4b0510c | 1412 | goto err_out_notif_unreg; |
61ec9016 | 1413 | } |
30e1e285 | 1414 | |
354fb1a7 SB |
1415 | /* |
1416 | * Register the port. | |
1417 | * This function also registers this device with the tty layer | |
1418 | * and triggers invocation of the config_port() entry point. | |
1419 | */ | |
1420 | port->mapbase = res->start; | |
1421 | port->irq = irq; | |
1422 | port->dev = &pdev->dev; | |
1423 | port->uartclk = clk_get_rate(cdns_uart_data->uartclk); | |
1424 | port->private_data = cdns_uart_data; | |
1425 | cdns_uart_data->port = port; | |
1426 | platform_set_drvdata(pdev, port); | |
1427 | ||
1428 | rc = uart_add_one_port(&cdns_uart_uart_driver, port); | |
1429 | if (rc) { | |
1430 | dev_err(&pdev->dev, | |
1431 | "uart_add_one_port() failed; err=%i\n", rc); | |
1432 | goto err_out_notif_unreg; | |
1433 | } | |
1434 | ||
1435 | return 0; | |
1436 | ||
c4b0510c | 1437 | err_out_notif_unreg: |
7ac57347 | 1438 | #ifdef CONFIG_COMMON_CLK |
d9bb3fb1 SB |
1439 | clk_notifier_unregister(cdns_uart_data->uartclk, |
1440 | &cdns_uart_data->clk_rate_change_nb); | |
7ac57347 | 1441 | #endif |
30e1e285 | 1442 | err_out_clk_disable: |
210417ce | 1443 | clk_unprepare(cdns_uart_data->uartclk); |
d9bb3fb1 | 1444 | err_out_clk_dis_pclk: |
210417ce | 1445 | clk_unprepare(cdns_uart_data->pclk); |
30e1e285 SB |
1446 | |
1447 | return rc; | |
61ec9016 JL |
1448 | } |
1449 | ||
1450 | /** | |
d9bb3fb1 | 1451 | * cdns_uart_remove - called when the platform driver is unregistered |
61ec9016 JL |
1452 | * @pdev: Pointer to the platform device structure |
1453 | * | |
e555a211 | 1454 | * Return: 0 on success, negative errno otherwise |
489810a1 | 1455 | */ |
d9bb3fb1 | 1456 | static int cdns_uart_remove(struct platform_device *pdev) |
61ec9016 | 1457 | { |
696faedd | 1458 | struct uart_port *port = platform_get_drvdata(pdev); |
d9bb3fb1 | 1459 | struct cdns_uart *cdns_uart_data = port->private_data; |
2326669c | 1460 | int rc; |
61ec9016 | 1461 | |
d9bb3fb1 | 1462 | /* Remove the cdns_uart port from the serial core */ |
7ac57347 | 1463 | #ifdef CONFIG_COMMON_CLK |
d9bb3fb1 SB |
1464 | clk_notifier_unregister(cdns_uart_data->uartclk, |
1465 | &cdns_uart_data->clk_rate_change_nb); | |
7ac57347 | 1466 | #endif |
d9bb3fb1 | 1467 | rc = uart_remove_one_port(&cdns_uart_uart_driver, port); |
2326669c | 1468 | port->mapbase = 0; |
210417ce SD |
1469 | clk_unprepare(cdns_uart_data->uartclk); |
1470 | clk_unprepare(cdns_uart_data->pclk); | |
61ec9016 JL |
1471 | return rc; |
1472 | } | |
1473 | ||
61ec9016 | 1474 | /* Match table for of_platform binding */ |
ed0bb232 | 1475 | static const struct of_device_id cdns_uart_of_match[] = { |
61ec9016 | 1476 | { .compatible = "xlnx,xuartps", }, |
d9bb3fb1 | 1477 | { .compatible = "cdns,uart-r1p8", }, |
61ec9016 JL |
1478 | {} |
1479 | }; | |
d9bb3fb1 | 1480 | MODULE_DEVICE_TABLE(of, cdns_uart_of_match); |
61ec9016 | 1481 | |
d9bb3fb1 SB |
1482 | static struct platform_driver cdns_uart_platform_driver = { |
1483 | .probe = cdns_uart_probe, | |
1484 | .remove = cdns_uart_remove, | |
61ec9016 | 1485 | .driver = { |
d9bb3fb1 SB |
1486 | .name = CDNS_UART_NAME, |
1487 | .of_match_table = cdns_uart_of_match, | |
1488 | .pm = &cdns_uart_dev_pm_ops, | |
61ec9016 JL |
1489 | }, |
1490 | }; | |
1491 | ||
d9bb3fb1 | 1492 | static int __init cdns_uart_init(void) |
61ec9016 JL |
1493 | { |
1494 | int retval = 0; | |
1495 | ||
d9bb3fb1 SB |
1496 | /* Register the cdns_uart driver with the serial core */ |
1497 | retval = uart_register_driver(&cdns_uart_uart_driver); | |
61ec9016 JL |
1498 | if (retval) |
1499 | return retval; | |
1500 | ||
1501 | /* Register the platform driver */ | |
d9bb3fb1 | 1502 | retval = platform_driver_register(&cdns_uart_platform_driver); |
61ec9016 | 1503 | if (retval) |
d9bb3fb1 | 1504 | uart_unregister_driver(&cdns_uart_uart_driver); |
61ec9016 JL |
1505 | |
1506 | return retval; | |
1507 | } | |
1508 | ||
d9bb3fb1 | 1509 | static void __exit cdns_uart_exit(void) |
61ec9016 | 1510 | { |
61ec9016 | 1511 | /* Unregister the platform driver */ |
d9bb3fb1 | 1512 | platform_driver_unregister(&cdns_uart_platform_driver); |
61ec9016 | 1513 | |
d9bb3fb1 SB |
1514 | /* Unregister the cdns_uart driver */ |
1515 | uart_unregister_driver(&cdns_uart_uart_driver); | |
61ec9016 JL |
1516 | } |
1517 | ||
d9bb3fb1 SB |
1518 | module_init(cdns_uart_init); |
1519 | module_exit(cdns_uart_exit); | |
61ec9016 | 1520 | |
d9bb3fb1 | 1521 | MODULE_DESCRIPTION("Driver for Cadence UART"); |
61ec9016 JL |
1522 | MODULE_AUTHOR("Xilinx Inc."); |
1523 | MODULE_LICENSE("GPL"); |