Commit | Line | Data |
---|---|---|
e3b3d0f5 | 1 | // SPDX-License-Identifier: GPL-2.0 |
9fcd66e5 | 2 | /* |
9fcd66e5 MB |
3 | * Derived from many drivers using generic_serial interface. |
4 | * | |
5 | * Copyright (C) 2008 Maxime Bizon <mbizon@freebox.fr> | |
6 | * | |
7 | * Serial driver for BCM63xx integrated UART. | |
8 | * | |
9 | * Hardware flow control was _not_ tested since I only have RX/TX on | |
10 | * my board. | |
11 | */ | |
12 | ||
9fcd66e5 MB |
13 | #include <linux/kernel.h> |
14 | #include <linux/platform_device.h> | |
15 | #include <linux/init.h> | |
16 | #include <linux/delay.h> | |
17 | #include <linux/module.h> | |
18 | #include <linux/console.h> | |
19 | #include <linux/clk.h> | |
20 | #include <linux/tty.h> | |
21 | #include <linux/tty_flip.h> | |
22 | #include <linux/sysrq.h> | |
23 | #include <linux/serial.h> | |
24 | #include <linux/serial_core.h> | |
d29e0d04 | 25 | #include <linux/serial_bcm63xx.h> |
f04430ce | 26 | #include <linux/io.h> |
9277285f | 27 | #include <linux/of.h> |
9fcd66e5 | 28 | |
6a2c7eab | 29 | #define BCM63XX_NR_UARTS 2 |
9fcd66e5 MB |
30 | |
31 | static struct uart_port ports[BCM63XX_NR_UARTS]; | |
32 | ||
33 | /* | |
34 | * rx interrupt mask / stat | |
35 | * | |
36 | * mask: | |
37 | * - rx fifo full | |
38 | * - rx fifo above threshold | |
39 | * - rx fifo not empty for too long | |
40 | */ | |
41 | #define UART_RX_INT_MASK (UART_IR_MASK(UART_IR_RXOVER) | \ | |
42 | UART_IR_MASK(UART_IR_RXTHRESH) | \ | |
43 | UART_IR_MASK(UART_IR_RXTIMEOUT)) | |
44 | ||
45 | #define UART_RX_INT_STAT (UART_IR_STAT(UART_IR_RXOVER) | \ | |
46 | UART_IR_STAT(UART_IR_RXTHRESH) | \ | |
47 | UART_IR_STAT(UART_IR_RXTIMEOUT)) | |
48 | ||
49 | /* | |
50 | * tx interrupt mask / stat | |
51 | * | |
52 | * mask: | |
53 | * - tx fifo empty | |
54 | * - tx fifo below threshold | |
55 | */ | |
56 | #define UART_TX_INT_MASK (UART_IR_MASK(UART_IR_TXEMPTY) | \ | |
57 | UART_IR_MASK(UART_IR_TXTRESH)) | |
58 | ||
59 | #define UART_TX_INT_STAT (UART_IR_STAT(UART_IR_TXEMPTY) | \ | |
60 | UART_IR_STAT(UART_IR_TXTRESH)) | |
61 | ||
62 | /* | |
63 | * external input interrupt | |
64 | * | |
65 | * mask: any edge on CTS, DCD | |
66 | */ | |
67 | #define UART_EXTINP_INT_MASK (UART_EXTINP_IRMASK(UART_EXTINP_IR_CTS) | \ | |
68 | UART_EXTINP_IRMASK(UART_EXTINP_IR_DCD)) | |
69 | ||
70 | /* | |
71 | * handy uart register accessor | |
72 | */ | |
73 | static inline unsigned int bcm_uart_readl(struct uart_port *port, | |
74 | unsigned int offset) | |
75 | { | |
99cf9790 | 76 | return __raw_readl(port->membase + offset); |
9fcd66e5 MB |
77 | } |
78 | ||
79 | static inline void bcm_uart_writel(struct uart_port *port, | |
80 | unsigned int value, unsigned int offset) | |
81 | { | |
99cf9790 | 82 | __raw_writel(value, port->membase + offset); |
9fcd66e5 MB |
83 | } |
84 | ||
85 | /* | |
86 | * serial core request to check if uart tx fifo is empty | |
87 | */ | |
88 | static unsigned int bcm_uart_tx_empty(struct uart_port *port) | |
89 | { | |
90 | unsigned int val; | |
91 | ||
92 | val = bcm_uart_readl(port, UART_IR_REG); | |
93 | return (val & UART_IR_STAT(UART_IR_TXEMPTY)) ? 1 : 0; | |
94 | } | |
95 | ||
96 | /* | |
97 | * serial core request to set RTS and DTR pin state and loopback mode | |
98 | */ | |
99 | static void bcm_uart_set_mctrl(struct uart_port *port, unsigned int mctrl) | |
100 | { | |
101 | unsigned int val; | |
102 | ||
103 | val = bcm_uart_readl(port, UART_MCTL_REG); | |
104 | val &= ~(UART_MCTL_DTR_MASK | UART_MCTL_RTS_MASK); | |
105 | /* invert of written value is reflected on the pin */ | |
106 | if (!(mctrl & TIOCM_DTR)) | |
107 | val |= UART_MCTL_DTR_MASK; | |
108 | if (!(mctrl & TIOCM_RTS)) | |
109 | val |= UART_MCTL_RTS_MASK; | |
110 | bcm_uart_writel(port, val, UART_MCTL_REG); | |
111 | ||
112 | val = bcm_uart_readl(port, UART_CTL_REG); | |
113 | if (mctrl & TIOCM_LOOP) | |
114 | val |= UART_CTL_LOOPBACK_MASK; | |
115 | else | |
116 | val &= ~UART_CTL_LOOPBACK_MASK; | |
117 | bcm_uart_writel(port, val, UART_CTL_REG); | |
118 | } | |
119 | ||
120 | /* | |
121 | * serial core request to return RI, CTS, DCD and DSR pin state | |
122 | */ | |
123 | static unsigned int bcm_uart_get_mctrl(struct uart_port *port) | |
124 | { | |
125 | unsigned int val, mctrl; | |
126 | ||
127 | mctrl = 0; | |
128 | val = bcm_uart_readl(port, UART_EXTINP_REG); | |
129 | if (val & UART_EXTINP_RI_MASK) | |
130 | mctrl |= TIOCM_RI; | |
131 | if (val & UART_EXTINP_CTS_MASK) | |
132 | mctrl |= TIOCM_CTS; | |
133 | if (val & UART_EXTINP_DCD_MASK) | |
134 | mctrl |= TIOCM_CD; | |
135 | if (val & UART_EXTINP_DSR_MASK) | |
136 | mctrl |= TIOCM_DSR; | |
137 | return mctrl; | |
138 | } | |
139 | ||
140 | /* | |
141 | * serial core request to disable tx ASAP (used for flow control) | |
142 | */ | |
143 | static void bcm_uart_stop_tx(struct uart_port *port) | |
144 | { | |
145 | unsigned int val; | |
146 | ||
147 | val = bcm_uart_readl(port, UART_CTL_REG); | |
148 | val &= ~(UART_CTL_TXEN_MASK); | |
149 | bcm_uart_writel(port, val, UART_CTL_REG); | |
150 | ||
151 | val = bcm_uart_readl(port, UART_IR_REG); | |
152 | val &= ~UART_TX_INT_MASK; | |
153 | bcm_uart_writel(port, val, UART_IR_REG); | |
154 | } | |
155 | ||
156 | /* | |
157 | * serial core request to (re)enable tx | |
158 | */ | |
159 | static void bcm_uart_start_tx(struct uart_port *port) | |
160 | { | |
161 | unsigned int val; | |
162 | ||
163 | val = bcm_uart_readl(port, UART_IR_REG); | |
164 | val |= UART_TX_INT_MASK; | |
165 | bcm_uart_writel(port, val, UART_IR_REG); | |
166 | ||
167 | val = bcm_uart_readl(port, UART_CTL_REG); | |
168 | val |= UART_CTL_TXEN_MASK; | |
169 | bcm_uart_writel(port, val, UART_CTL_REG); | |
170 | } | |
171 | ||
172 | /* | |
173 | * serial core request to stop rx, called before port shutdown | |
174 | */ | |
175 | static void bcm_uart_stop_rx(struct uart_port *port) | |
176 | { | |
177 | unsigned int val; | |
178 | ||
179 | val = bcm_uart_readl(port, UART_IR_REG); | |
180 | val &= ~UART_RX_INT_MASK; | |
181 | bcm_uart_writel(port, val, UART_IR_REG); | |
182 | } | |
183 | ||
184 | /* | |
185 | * serial core request to enable modem status interrupt reporting | |
186 | */ | |
187 | static void bcm_uart_enable_ms(struct uart_port *port) | |
188 | { | |
189 | unsigned int val; | |
190 | ||
191 | val = bcm_uart_readl(port, UART_IR_REG); | |
192 | val |= UART_IR_MASK(UART_IR_EXTIP); | |
193 | bcm_uart_writel(port, val, UART_IR_REG); | |
194 | } | |
195 | ||
196 | /* | |
197 | * serial core request to start/stop emitting break char | |
198 | */ | |
199 | static void bcm_uart_break_ctl(struct uart_port *port, int ctl) | |
200 | { | |
201 | unsigned long flags; | |
202 | unsigned int val; | |
203 | ||
778492b6 | 204 | uart_port_lock_irqsave(port, &flags); |
9fcd66e5 MB |
205 | |
206 | val = bcm_uart_readl(port, UART_CTL_REG); | |
207 | if (ctl) | |
208 | val |= UART_CTL_XMITBRK_MASK; | |
209 | else | |
210 | val &= ~UART_CTL_XMITBRK_MASK; | |
211 | bcm_uart_writel(port, val, UART_CTL_REG); | |
212 | ||
778492b6 | 213 | uart_port_unlock_irqrestore(port, flags); |
9fcd66e5 MB |
214 | } |
215 | ||
216 | /* | |
217 | * return port type in string format | |
218 | */ | |
219 | static const char *bcm_uart_type(struct uart_port *port) | |
220 | { | |
221 | return (port->type == PORT_BCM63XX) ? "bcm63xx_uart" : NULL; | |
222 | } | |
223 | ||
224 | /* | |
225 | * read all chars in rx fifo and send them to core | |
226 | */ | |
227 | static void bcm_uart_do_rx(struct uart_port *port) | |
228 | { | |
064256fe | 229 | struct tty_port *tty_port = &port->state->port; |
9fcd66e5 MB |
230 | unsigned int max_count; |
231 | ||
232 | /* limit number of char read in interrupt, should not be | |
233 | * higher than fifo size anyway since we're much faster than | |
234 | * serial port */ | |
235 | max_count = 32; | |
9fcd66e5 MB |
236 | do { |
237 | unsigned int iestat, c, cstat; | |
238 | char flag; | |
239 | ||
240 | /* get overrun/fifo empty information from ier | |
241 | * register */ | |
242 | iestat = bcm_uart_readl(port, UART_IR_REG); | |
3bc46b31 MB |
243 | |
244 | if (unlikely(iestat & UART_IR_STAT(UART_IR_RXOVER))) { | |
245 | unsigned int val; | |
246 | ||
247 | /* fifo reset is required to clear | |
248 | * interrupt */ | |
249 | val = bcm_uart_readl(port, UART_CTL_REG); | |
250 | val |= UART_CTL_RSTRXFIFO_MASK; | |
251 | bcm_uart_writel(port, val, UART_CTL_REG); | |
252 | ||
253 | port->icount.overrun++; | |
064256fe | 254 | tty_insert_flip_char(tty_port, 0, TTY_OVERRUN); |
3bc46b31 MB |
255 | } |
256 | ||
9fcd66e5 MB |
257 | if (!(iestat & UART_IR_STAT(UART_IR_RXNOTEMPTY))) |
258 | break; | |
259 | ||
260 | cstat = c = bcm_uart_readl(port, UART_FIFO_REG); | |
261 | port->icount.rx++; | |
262 | flag = TTY_NORMAL; | |
263 | c &= 0xff; | |
264 | ||
265 | if (unlikely((cstat & UART_FIFO_ANYERR_MASK))) { | |
266 | /* do stats first */ | |
267 | if (cstat & UART_FIFO_BRKDET_MASK) { | |
268 | port->icount.brk++; | |
269 | if (uart_handle_break(port)) | |
270 | continue; | |
271 | } | |
272 | ||
273 | if (cstat & UART_FIFO_PARERR_MASK) | |
274 | port->icount.parity++; | |
275 | if (cstat & UART_FIFO_FRAMEERR_MASK) | |
276 | port->icount.frame++; | |
277 | ||
278 | /* update flag wrt read_status_mask */ | |
279 | cstat &= port->read_status_mask; | |
280 | if (cstat & UART_FIFO_BRKDET_MASK) | |
281 | flag = TTY_BREAK; | |
282 | if (cstat & UART_FIFO_FRAMEERR_MASK) | |
283 | flag = TTY_FRAME; | |
284 | if (cstat & UART_FIFO_PARERR_MASK) | |
285 | flag = TTY_PARITY; | |
286 | } | |
287 | ||
8c1cbc5a | 288 | if (uart_prepare_sysrq_char(port, c)) |
9fcd66e5 MB |
289 | continue; |
290 | ||
9fcd66e5 | 291 | if ((cstat & port->ignore_status_mask) == 0) |
064256fe | 292 | tty_insert_flip_char(tty_port, c, flag); |
9fcd66e5 MB |
293 | |
294 | } while (--max_count); | |
295 | ||
064256fe | 296 | tty_flip_buffer_push(tty_port); |
9fcd66e5 MB |
297 | } |
298 | ||
299 | /* | |
300 | * fill tx fifo with chars to send, stop when fifo is about to be full | |
301 | * or when all chars have been sent. | |
302 | */ | |
303 | static void bcm_uart_do_tx(struct uart_port *port) | |
304 | { | |
d11cc8c3 JSS |
305 | unsigned int val; |
306 | bool pending; | |
307 | u8 ch; | |
9fcd66e5 MB |
308 | |
309 | val = bcm_uart_readl(port, UART_MCTL_REG); | |
310 | val = (val & UART_MCTL_TXFIFOFILL_MASK) >> UART_MCTL_TXFIFOFILL_SHIFT; | |
ea55c65d JG |
311 | pending = uart_port_tx_limited_flags(port, ch, UART_TX_NOSTOP, |
312 | port->fifosize - val, | |
d11cc8c3 JSS |
313 | true, |
314 | bcm_uart_writel(port, ch, UART_FIFO_REG), | |
315 | ({})); | |
316 | if (pending) | |
317 | return; | |
9fcd66e5 | 318 | |
9fcd66e5 MB |
319 | /* nothing to send, disable transmit interrupt */ |
320 | val = bcm_uart_readl(port, UART_IR_REG); | |
321 | val &= ~UART_TX_INT_MASK; | |
322 | bcm_uart_writel(port, val, UART_IR_REG); | |
ea55c65d JG |
323 | |
324 | if (uart_tx_stopped(port)) | |
325 | bcm_uart_stop_tx(port); | |
9fcd66e5 MB |
326 | } |
327 | ||
328 | /* | |
329 | * process uart interrupt | |
330 | */ | |
331 | static irqreturn_t bcm_uart_interrupt(int irq, void *dev_id) | |
332 | { | |
333 | struct uart_port *port; | |
334 | unsigned int irqstat; | |
335 | ||
336 | port = dev_id; | |
778492b6 | 337 | uart_port_lock(port); |
9fcd66e5 MB |
338 | |
339 | irqstat = bcm_uart_readl(port, UART_IR_REG); | |
340 | if (irqstat & UART_RX_INT_STAT) | |
341 | bcm_uart_do_rx(port); | |
342 | ||
343 | if (irqstat & UART_TX_INT_STAT) | |
344 | bcm_uart_do_tx(port); | |
345 | ||
346 | if (irqstat & UART_IR_MASK(UART_IR_EXTIP)) { | |
347 | unsigned int estat; | |
348 | ||
349 | estat = bcm_uart_readl(port, UART_EXTINP_REG); | |
350 | if (estat & UART_EXTINP_IRSTAT(UART_EXTINP_IR_CTS)) | |
351 | uart_handle_cts_change(port, | |
352 | estat & UART_EXTINP_CTS_MASK); | |
353 | if (estat & UART_EXTINP_IRSTAT(UART_EXTINP_IR_DCD)) | |
354 | uart_handle_dcd_change(port, | |
355 | estat & UART_EXTINP_DCD_MASK); | |
356 | } | |
357 | ||
8c1cbc5a | 358 | uart_unlock_and_check_sysrq(port); |
9fcd66e5 MB |
359 | return IRQ_HANDLED; |
360 | } | |
361 | ||
362 | /* | |
363 | * enable rx & tx operation on uart | |
364 | */ | |
365 | static void bcm_uart_enable(struct uart_port *port) | |
366 | { | |
367 | unsigned int val; | |
368 | ||
369 | val = bcm_uart_readl(port, UART_CTL_REG); | |
370 | val |= (UART_CTL_BRGEN_MASK | UART_CTL_TXEN_MASK | UART_CTL_RXEN_MASK); | |
371 | bcm_uart_writel(port, val, UART_CTL_REG); | |
372 | } | |
373 | ||
374 | /* | |
375 | * disable rx & tx operation on uart | |
376 | */ | |
377 | static void bcm_uart_disable(struct uart_port *port) | |
378 | { | |
379 | unsigned int val; | |
380 | ||
381 | val = bcm_uart_readl(port, UART_CTL_REG); | |
382 | val &= ~(UART_CTL_BRGEN_MASK | UART_CTL_TXEN_MASK | | |
383 | UART_CTL_RXEN_MASK); | |
384 | bcm_uart_writel(port, val, UART_CTL_REG); | |
385 | } | |
386 | ||
387 | /* | |
388 | * clear all unread data in rx fifo and unsent data in tx fifo | |
389 | */ | |
390 | static void bcm_uart_flush(struct uart_port *port) | |
391 | { | |
392 | unsigned int val; | |
393 | ||
394 | /* empty rx and tx fifo */ | |
395 | val = bcm_uart_readl(port, UART_CTL_REG); | |
396 | val |= UART_CTL_RSTRXFIFO_MASK | UART_CTL_RSTTXFIFO_MASK; | |
397 | bcm_uart_writel(port, val, UART_CTL_REG); | |
398 | ||
399 | /* read any pending char to make sure all irq status are | |
400 | * cleared */ | |
401 | (void)bcm_uart_readl(port, UART_FIFO_REG); | |
402 | } | |
403 | ||
404 | /* | |
405 | * serial core request to initialize uart and start rx operation | |
406 | */ | |
407 | static int bcm_uart_startup(struct uart_port *port) | |
408 | { | |
409 | unsigned int val; | |
410 | int ret; | |
411 | ||
412 | /* mask all irq and flush port */ | |
413 | bcm_uart_disable(port); | |
414 | bcm_uart_writel(port, 0, UART_IR_REG); | |
415 | bcm_uart_flush(port); | |
416 | ||
417 | /* clear any pending external input interrupt */ | |
418 | (void)bcm_uart_readl(port, UART_EXTINP_REG); | |
419 | ||
420 | /* set rx/tx fifo thresh to fifo half size */ | |
421 | val = bcm_uart_readl(port, UART_MCTL_REG); | |
422 | val &= ~(UART_MCTL_RXFIFOTHRESH_MASK | UART_MCTL_TXFIFOTHRESH_MASK); | |
423 | val |= (port->fifosize / 2) << UART_MCTL_RXFIFOTHRESH_SHIFT; | |
424 | val |= (port->fifosize / 2) << UART_MCTL_TXFIFOTHRESH_SHIFT; | |
425 | bcm_uart_writel(port, val, UART_MCTL_REG); | |
426 | ||
427 | /* set rx fifo timeout to 1 char time */ | |
428 | val = bcm_uart_readl(port, UART_CTL_REG); | |
429 | val &= ~UART_CTL_RXTMOUTCNT_MASK; | |
430 | val |= 1 << UART_CTL_RXTMOUTCNT_SHIFT; | |
431 | bcm_uart_writel(port, val, UART_CTL_REG); | |
432 | ||
433 | /* report any edge on dcd and cts */ | |
434 | val = UART_EXTINP_INT_MASK; | |
435 | val |= UART_EXTINP_DCD_NOSENSE_MASK; | |
436 | val |= UART_EXTINP_CTS_NOSENSE_MASK; | |
437 | bcm_uart_writel(port, val, UART_EXTINP_REG); | |
438 | ||
439 | /* register irq and enable rx interrupts */ | |
440 | ret = request_irq(port->irq, bcm_uart_interrupt, 0, | |
83f7fa6c | 441 | dev_name(port->dev), port); |
9fcd66e5 MB |
442 | if (ret) |
443 | return ret; | |
444 | bcm_uart_writel(port, UART_RX_INT_MASK, UART_IR_REG); | |
445 | bcm_uart_enable(port); | |
446 | return 0; | |
447 | } | |
448 | ||
449 | /* | |
450 | * serial core request to flush & disable uart | |
451 | */ | |
452 | static void bcm_uart_shutdown(struct uart_port *port) | |
453 | { | |
454 | unsigned long flags; | |
455 | ||
778492b6 | 456 | uart_port_lock_irqsave(port, &flags); |
9fcd66e5 | 457 | bcm_uart_writel(port, 0, UART_IR_REG); |
778492b6 | 458 | uart_port_unlock_irqrestore(port, flags); |
9fcd66e5 MB |
459 | |
460 | bcm_uart_disable(port); | |
461 | bcm_uart_flush(port); | |
462 | free_irq(port->irq, port); | |
463 | } | |
464 | ||
465 | /* | |
466 | * serial core request to change current uart setting | |
467 | */ | |
bec5b814 IJ |
468 | static void bcm_uart_set_termios(struct uart_port *port, struct ktermios *new, |
469 | const struct ktermios *old) | |
9fcd66e5 MB |
470 | { |
471 | unsigned int ctl, baud, quot, ier; | |
472 | unsigned long flags; | |
0e5ec414 | 473 | int tries; |
9fcd66e5 | 474 | |
778492b6 | 475 | uart_port_lock_irqsave(port, &flags); |
9fcd66e5 | 476 | |
0e5ec414 RE |
477 | /* Drain the hot tub fully before we power it off for the winter. */ |
478 | for (tries = 3; !bcm_uart_tx_empty(port) && tries; tries--) | |
479 | mdelay(10); | |
480 | ||
9fcd66e5 MB |
481 | /* disable uart while changing speed */ |
482 | bcm_uart_disable(port); | |
483 | bcm_uart_flush(port); | |
484 | ||
485 | /* update Control register */ | |
486 | ctl = bcm_uart_readl(port, UART_CTL_REG); | |
487 | ctl &= ~UART_CTL_BITSPERSYM_MASK; | |
488 | ||
489 | switch (new->c_cflag & CSIZE) { | |
490 | case CS5: | |
491 | ctl |= (0 << UART_CTL_BITSPERSYM_SHIFT); | |
492 | break; | |
493 | case CS6: | |
494 | ctl |= (1 << UART_CTL_BITSPERSYM_SHIFT); | |
495 | break; | |
496 | case CS7: | |
497 | ctl |= (2 << UART_CTL_BITSPERSYM_SHIFT); | |
498 | break; | |
499 | default: | |
500 | ctl |= (3 << UART_CTL_BITSPERSYM_SHIFT); | |
501 | break; | |
502 | } | |
503 | ||
504 | ctl &= ~UART_CTL_STOPBITS_MASK; | |
505 | if (new->c_cflag & CSTOPB) | |
506 | ctl |= UART_CTL_STOPBITS_2; | |
507 | else | |
508 | ctl |= UART_CTL_STOPBITS_1; | |
509 | ||
510 | ctl &= ~(UART_CTL_RXPAREN_MASK | UART_CTL_TXPAREN_MASK); | |
511 | if (new->c_cflag & PARENB) | |
512 | ctl |= (UART_CTL_RXPAREN_MASK | UART_CTL_TXPAREN_MASK); | |
513 | ctl &= ~(UART_CTL_RXPAREVEN_MASK | UART_CTL_TXPAREVEN_MASK); | |
514 | if (new->c_cflag & PARODD) | |
515 | ctl |= (UART_CTL_RXPAREVEN_MASK | UART_CTL_TXPAREVEN_MASK); | |
516 | bcm_uart_writel(port, ctl, UART_CTL_REG); | |
517 | ||
518 | /* update Baudword register */ | |
519 | baud = uart_get_baud_rate(port, new, old, 0, port->uartclk / 16); | |
520 | quot = uart_get_divisor(port, baud) - 1; | |
521 | bcm_uart_writel(port, quot, UART_BAUD_REG); | |
522 | ||
523 | /* update Interrupt register */ | |
524 | ier = bcm_uart_readl(port, UART_IR_REG); | |
525 | ||
526 | ier &= ~UART_IR_MASK(UART_IR_EXTIP); | |
527 | if (UART_ENABLE_MS(port, new->c_cflag)) | |
528 | ier |= UART_IR_MASK(UART_IR_EXTIP); | |
529 | ||
530 | bcm_uart_writel(port, ier, UART_IR_REG); | |
531 | ||
532 | /* update read/ignore mask */ | |
533 | port->read_status_mask = UART_FIFO_VALID_MASK; | |
534 | if (new->c_iflag & INPCK) { | |
535 | port->read_status_mask |= UART_FIFO_FRAMEERR_MASK; | |
536 | port->read_status_mask |= UART_FIFO_PARERR_MASK; | |
537 | } | |
ef8b9ddc | 538 | if (new->c_iflag & (IGNBRK | BRKINT)) |
9fcd66e5 MB |
539 | port->read_status_mask |= UART_FIFO_BRKDET_MASK; |
540 | ||
541 | port->ignore_status_mask = 0; | |
542 | if (new->c_iflag & IGNPAR) | |
543 | port->ignore_status_mask |= UART_FIFO_PARERR_MASK; | |
544 | if (new->c_iflag & IGNBRK) | |
545 | port->ignore_status_mask |= UART_FIFO_BRKDET_MASK; | |
546 | if (!(new->c_cflag & CREAD)) | |
547 | port->ignore_status_mask |= UART_FIFO_VALID_MASK; | |
548 | ||
549 | uart_update_timeout(port, new->c_cflag, baud); | |
550 | bcm_uart_enable(port); | |
778492b6 | 551 | uart_port_unlock_irqrestore(port, flags); |
9fcd66e5 MB |
552 | } |
553 | ||
554 | /* | |
555 | * serial core request to claim uart iomem | |
556 | */ | |
557 | static int bcm_uart_request_port(struct uart_port *port) | |
558 | { | |
e979f3b7 | 559 | /* UARTs always present */ |
9fcd66e5 MB |
560 | return 0; |
561 | } | |
562 | ||
563 | /* | |
564 | * serial core request to release uart iomem | |
565 | */ | |
566 | static void bcm_uart_release_port(struct uart_port *port) | |
567 | { | |
e979f3b7 | 568 | /* Nothing to release ... */ |
9fcd66e5 MB |
569 | } |
570 | ||
571 | /* | |
572 | * serial core request to do any port required autoconfiguration | |
573 | */ | |
574 | static void bcm_uart_config_port(struct uart_port *port, int flags) | |
575 | { | |
576 | if (flags & UART_CONFIG_TYPE) { | |
577 | if (bcm_uart_request_port(port)) | |
578 | return; | |
579 | port->type = PORT_BCM63XX; | |
580 | } | |
581 | } | |
582 | ||
583 | /* | |
584 | * serial core request to check that port information in serinfo are | |
585 | * suitable | |
586 | */ | |
587 | static int bcm_uart_verify_port(struct uart_port *port, | |
588 | struct serial_struct *serinfo) | |
589 | { | |
590 | if (port->type != PORT_BCM63XX) | |
591 | return -EINVAL; | |
592 | if (port->irq != serinfo->irq) | |
593 | return -EINVAL; | |
594 | if (port->iotype != serinfo->io_type) | |
595 | return -EINVAL; | |
596 | if (port->mapbase != (unsigned long)serinfo->iomem_base) | |
597 | return -EINVAL; | |
598 | return 0; | |
599 | } | |
600 | ||
945de8be AS |
601 | #ifdef CONFIG_CONSOLE_POLL |
602 | /* | |
603 | * return true when outstanding tx equals fifo size | |
604 | */ | |
605 | static bool bcm_uart_tx_full(struct uart_port *port) | |
606 | { | |
607 | unsigned int val; | |
608 | ||
609 | val = bcm_uart_readl(port, UART_MCTL_REG); | |
610 | val = (val & UART_MCTL_TXFIFOFILL_MASK) >> UART_MCTL_TXFIFOFILL_SHIFT; | |
611 | return !(port->fifosize - val); | |
612 | } | |
613 | ||
614 | static int bcm_uart_poll_get_char(struct uart_port *port) | |
615 | { | |
616 | unsigned int iestat; | |
617 | ||
618 | iestat = bcm_uart_readl(port, UART_IR_REG); | |
619 | if (!(iestat & UART_IR_STAT(UART_IR_RXNOTEMPTY))) | |
620 | return NO_POLL_CHAR; | |
621 | ||
622 | return bcm_uart_readl(port, UART_FIFO_REG); | |
623 | } | |
624 | ||
625 | static void bcm_uart_poll_put_char(struct uart_port *port, unsigned char c) | |
626 | { | |
627 | while (bcm_uart_tx_full(port)) { | |
628 | cpu_relax(); | |
629 | } | |
630 | ||
631 | bcm_uart_writel(port, c, UART_FIFO_REG); | |
632 | } | |
633 | #endif | |
634 | ||
9fcd66e5 | 635 | /* serial core callbacks */ |
1f1d8703 | 636 | static const struct uart_ops bcm_uart_ops = { |
9fcd66e5 MB |
637 | .tx_empty = bcm_uart_tx_empty, |
638 | .get_mctrl = bcm_uart_get_mctrl, | |
639 | .set_mctrl = bcm_uart_set_mctrl, | |
640 | .start_tx = bcm_uart_start_tx, | |
641 | .stop_tx = bcm_uart_stop_tx, | |
642 | .stop_rx = bcm_uart_stop_rx, | |
643 | .enable_ms = bcm_uart_enable_ms, | |
644 | .break_ctl = bcm_uart_break_ctl, | |
645 | .startup = bcm_uart_startup, | |
646 | .shutdown = bcm_uart_shutdown, | |
647 | .set_termios = bcm_uart_set_termios, | |
648 | .type = bcm_uart_type, | |
649 | .release_port = bcm_uart_release_port, | |
650 | .request_port = bcm_uart_request_port, | |
651 | .config_port = bcm_uart_config_port, | |
652 | .verify_port = bcm_uart_verify_port, | |
945de8be AS |
653 | #ifdef CONFIG_CONSOLE_POLL |
654 | .poll_get_char = bcm_uart_poll_get_char, | |
655 | .poll_put_char = bcm_uart_poll_put_char, | |
656 | #endif | |
9fcd66e5 MB |
657 | }; |
658 | ||
659 | ||
660 | ||
661 | #ifdef CONFIG_SERIAL_BCM63XX_CONSOLE | |
1a5b34eb | 662 | static void wait_for_xmitr(struct uart_port *port) |
9fcd66e5 MB |
663 | { |
664 | unsigned int tmout; | |
665 | ||
666 | /* Wait up to 10ms for the character(s) to be sent. */ | |
667 | tmout = 10000; | |
668 | while (--tmout) { | |
669 | unsigned int val; | |
670 | ||
671 | val = bcm_uart_readl(port, UART_IR_REG); | |
672 | if (val & UART_IR_STAT(UART_IR_TXEMPTY)) | |
673 | break; | |
674 | udelay(1); | |
675 | } | |
676 | ||
677 | /* Wait up to 1s for flow control if necessary */ | |
678 | if (port->flags & UPF_CONS_FLOW) { | |
679 | tmout = 1000000; | |
680 | while (--tmout) { | |
681 | unsigned int val; | |
682 | ||
683 | val = bcm_uart_readl(port, UART_EXTINP_REG); | |
684 | if (val & UART_EXTINP_CTS_MASK) | |
685 | break; | |
686 | udelay(1); | |
687 | } | |
688 | } | |
689 | } | |
690 | ||
691 | /* | |
692 | * output given char | |
693 | */ | |
3f8bab17 | 694 | static void bcm_console_putchar(struct uart_port *port, unsigned char ch) |
9fcd66e5 MB |
695 | { |
696 | wait_for_xmitr(port); | |
697 | bcm_uart_writel(port, ch, UART_FIFO_REG); | |
698 | } | |
699 | ||
700 | /* | |
701 | * console core request to output given string | |
702 | */ | |
703 | static void bcm_console_write(struct console *co, const char *s, | |
704 | unsigned int count) | |
705 | { | |
706 | struct uart_port *port; | |
707 | unsigned long flags; | |
8c1cbc5a | 708 | int locked = 1; |
9fcd66e5 MB |
709 | |
710 | port = &ports[co->index]; | |
711 | ||
8c1cbc5a SAS |
712 | if (oops_in_progress) |
713 | locked = uart_port_trylock_irqsave(port, &flags); | |
714 | else | |
715 | uart_port_lock_irqsave(port, &flags); | |
9fcd66e5 MB |
716 | |
717 | /* call helper to deal with \r\n */ | |
718 | uart_console_write(port, s, count, bcm_console_putchar); | |
719 | ||
720 | /* and wait for char to be transmitted */ | |
721 | wait_for_xmitr(port); | |
722 | ||
723 | if (locked) | |
8c1cbc5a | 724 | uart_port_unlock_irqrestore(port, flags); |
9fcd66e5 MB |
725 | } |
726 | ||
727 | /* | |
728 | * console core request to setup given console, find matching uart | |
729 | * port and setup it. | |
730 | */ | |
731 | static int bcm_console_setup(struct console *co, char *options) | |
732 | { | |
733 | struct uart_port *port; | |
734 | int baud = 9600; | |
735 | int bits = 8; | |
736 | int parity = 'n'; | |
737 | int flow = 'n'; | |
738 | ||
739 | if (co->index < 0 || co->index >= BCM63XX_NR_UARTS) | |
740 | return -EINVAL; | |
741 | port = &ports[co->index]; | |
742 | if (!port->membase) | |
743 | return -ENODEV; | |
744 | if (options) | |
745 | uart_parse_options(options, &baud, &parity, &bits, &flow); | |
746 | ||
747 | return uart_set_options(port, co, baud, parity, bits, flow); | |
748 | } | |
749 | ||
750 | static struct uart_driver bcm_uart_driver; | |
751 | ||
752 | static struct console bcm63xx_console = { | |
753 | .name = "ttyS", | |
754 | .write = bcm_console_write, | |
755 | .device = uart_console_device, | |
756 | .setup = bcm_console_setup, | |
757 | .flags = CON_PRINTBUFFER, | |
758 | .index = -1, | |
759 | .data = &bcm_uart_driver, | |
760 | }; | |
761 | ||
762 | static int __init bcm63xx_console_init(void) | |
763 | { | |
764 | register_console(&bcm63xx_console); | |
765 | return 0; | |
766 | } | |
767 | ||
768 | console_initcall(bcm63xx_console_init); | |
769 | ||
1ab8e4b1 KC |
770 | static void bcm_early_write(struct console *con, const char *s, unsigned n) |
771 | { | |
772 | struct earlycon_device *dev = con->data; | |
773 | ||
774 | uart_console_write(&dev->port, s, n, bcm_console_putchar); | |
775 | wait_for_xmitr(&dev->port); | |
776 | } | |
777 | ||
778 | static int __init bcm_early_console_setup(struct earlycon_device *device, | |
779 | const char *opt) | |
780 | { | |
781 | if (!device->port.membase) | |
782 | return -ENODEV; | |
783 | ||
784 | device->con->write = bcm_early_write; | |
785 | return 0; | |
786 | } | |
787 | ||
788 | OF_EARLYCON_DECLARE(bcm63xx_uart, "brcm,bcm6345-uart", bcm_early_console_setup); | |
789 | ||
9fcd66e5 MB |
790 | #define BCM63XX_CONSOLE (&bcm63xx_console) |
791 | #else | |
792 | #define BCM63XX_CONSOLE NULL | |
793 | #endif /* CONFIG_SERIAL_BCM63XX_CONSOLE */ | |
794 | ||
795 | static struct uart_driver bcm_uart_driver = { | |
796 | .owner = THIS_MODULE, | |
797 | .driver_name = "bcm63xx_uart", | |
798 | .dev_name = "ttyS", | |
799 | .major = TTY_MAJOR, | |
800 | .minor = 64, | |
6a2c7eab | 801 | .nr = BCM63XX_NR_UARTS, |
9fcd66e5 MB |
802 | .cons = BCM63XX_CONSOLE, |
803 | }; | |
804 | ||
805 | /* | |
806 | * platform driver probe/remove callback | |
807 | */ | |
9671f099 | 808 | static int bcm_uart_probe(struct platform_device *pdev) |
9fcd66e5 | 809 | { |
fc67c913 | 810 | struct resource *res_mem; |
9fcd66e5 MB |
811 | struct uart_port *port; |
812 | struct clk *clk; | |
813 | int ret; | |
814 | ||
a5938866 JG |
815 | if (pdev->dev.of_node) { |
816 | pdev->id = of_alias_get_id(pdev->dev.of_node, "serial"); | |
817 | ||
818 | if (pdev->id < 0) | |
819 | pdev->id = of_alias_get_id(pdev->dev.of_node, "uart"); | |
820 | } | |
9277285f | 821 | |
9fcd66e5 MB |
822 | if (pdev->id < 0 || pdev->id >= BCM63XX_NR_UARTS) |
823 | return -EINVAL; | |
824 | ||
e979f3b7 KC |
825 | port = &ports[pdev->id]; |
826 | if (port->membase) | |
9fcd66e5 | 827 | return -EBUSY; |
e979f3b7 | 828 | memset(port, 0, sizeof(*port)); |
9fcd66e5 | 829 | |
b03a4ecb | 830 | port->membase = devm_platform_get_and_ioremap_resource(pdev, 0, &res_mem); |
e979f3b7 KC |
831 | if (IS_ERR(port->membase)) |
832 | return PTR_ERR(port->membase); | |
b03a4ecb | 833 | port->mapbase = res_mem->start; |
e979f3b7 | 834 | |
fc67c913 LP |
835 | ret = platform_get_irq(pdev, 0); |
836 | if (ret < 0) | |
837 | return ret; | |
838 | port->irq = ret; | |
9fcd66e5 | 839 | |
fa1e6a8a JG |
840 | clk = clk_get(&pdev->dev, "refclk"); |
841 | if (IS_ERR(clk) && pdev->dev.of_node) | |
842 | clk = of_clk_get(pdev->dev.of_node, 0); | |
843 | ||
092a9f59 | 844 | if (IS_ERR(clk)) |
9fcd66e5 MB |
845 | return -ENODEV; |
846 | ||
9fcd66e5 | 847 | port->iotype = UPIO_MEM; |
9fcd66e5 MB |
848 | port->ops = &bcm_uart_ops; |
849 | port->flags = UPF_BOOT_AUTOCONF; | |
850 | port->dev = &pdev->dev; | |
851 | port->fifosize = 16; | |
852 | port->uartclk = clk_get_rate(clk) / 2; | |
6a2c7eab | 853 | port->line = pdev->id; |
24036fb7 | 854 | port->has_sysrq = IS_ENABLED(CONFIG_SERIAL_BCM63XX_CONSOLE); |
9fcd66e5 MB |
855 | clk_put(clk); |
856 | ||
857 | ret = uart_add_one_port(&bcm_uart_driver, port); | |
858 | if (ret) { | |
16420ad0 | 859 | ports[pdev->id].membase = NULL; |
9fcd66e5 MB |
860 | return ret; |
861 | } | |
862 | platform_set_drvdata(pdev, port); | |
863 | return 0; | |
864 | } | |
865 | ||
69b1a039 | 866 | static void bcm_uart_remove(struct platform_device *pdev) |
9fcd66e5 MB |
867 | { |
868 | struct uart_port *port; | |
869 | ||
870 | port = platform_get_drvdata(pdev); | |
871 | uart_remove_one_port(&bcm_uart_driver, port); | |
9fcd66e5 | 872 | /* mark port as free */ |
16420ad0 | 873 | ports[pdev->id].membase = NULL; |
9fcd66e5 MB |
874 | } |
875 | ||
9277285f FF |
876 | static const struct of_device_id bcm63xx_of_match[] = { |
877 | { .compatible = "brcm,bcm6345-uart" }, | |
878 | { /* sentinel */ } | |
879 | }; | |
880 | MODULE_DEVICE_TABLE(of, bcm63xx_of_match); | |
881 | ||
9fcd66e5 MB |
882 | /* |
883 | * platform driver stuff | |
884 | */ | |
885 | static struct platform_driver bcm_uart_platform_driver = { | |
886 | .probe = bcm_uart_probe, | |
69b1a039 | 887 | .remove_new = bcm_uart_remove, |
9fcd66e5 | 888 | .driver = { |
9fcd66e5 | 889 | .name = "bcm63xx_uart", |
9277285f | 890 | .of_match_table = bcm63xx_of_match, |
9fcd66e5 MB |
891 | }, |
892 | }; | |
893 | ||
894 | static int __init bcm_uart_init(void) | |
895 | { | |
896 | int ret; | |
897 | ||
898 | ret = uart_register_driver(&bcm_uart_driver); | |
899 | if (ret) | |
900 | return ret; | |
901 | ||
902 | ret = platform_driver_register(&bcm_uart_platform_driver); | |
903 | if (ret) | |
904 | uart_unregister_driver(&bcm_uart_driver); | |
905 | ||
906 | return ret; | |
907 | } | |
908 | ||
909 | static void __exit bcm_uart_exit(void) | |
910 | { | |
911 | platform_driver_unregister(&bcm_uart_platform_driver); | |
912 | uart_unregister_driver(&bcm_uart_driver); | |
913 | } | |
914 | ||
915 | module_init(bcm_uart_init); | |
916 | module_exit(bcm_uart_exit); | |
917 | ||
918 | MODULE_AUTHOR("Maxime Bizon <mbizon@freebox.fr>"); | |
4137cd9b | 919 | MODULE_DESCRIPTION("Broadcom 63xx integrated uart driver"); |
9fcd66e5 | 920 | MODULE_LICENSE("GPL"); |