1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * timbuart.c timberdale FPGA UART driver |
4 | * Copyright (c) 2009 Intel Corporation |
5 | */ |
6 | |
7 | /* Supports: |
8 | * Timberdale FPGA UART |
9 | */ |
10 | |
11 | #include <linux/pci.h> |
12 | #include <linux/interrupt.h> |
13 | #include <linux/serial_core.h> |
14 | #include <linux/tty.h> |
15 | #include <linux/tty_flip.h> |
16 | #include <linux/kernel.h> |
17 | #include <linux/platform_device.h> |
18 | #include <linux/ioport.h> |
19 | #include <linux/slab.h> |
20 | #include <linux/module.h> |
21 | |
22 | #include "timbuart.h" |
23 | |
24 | struct timbuart_port { |
25 | struct uart_port port; |
26 | struct tasklet_struct tasklet; |
27 | int usedma; |
28 | u32 last_ier; |
29 | struct platform_device *dev; |
30 | }; |
31 | |
32 | static int baudrates[] = {9600, 19200, 38400, 57600, 115200, 230400, 460800, |
33 | 921600, 1843200, 3250000}; |
34 | |
35 | static void timbuart_mctrl_check(struct uart_port *port, u32 isr, u32 *ier); |
36 | |
37 | static irqreturn_t timbuart_handleinterrupt(int irq, void *devid); |
38 | |
39 | static void timbuart_stop_rx(struct uart_port *port) |
40 | { |
41 | /* spin lock held by upper layer, disable all RX interrupts */ |
42 | u32 ier = ioread32(port->membase + TIMBUART_IER) & ~RXFLAGS; |
43 | iowrite32(ier, port->membase + TIMBUART_IER); |
44 | } |
45 | |
46 | static void timbuart_stop_tx(struct uart_port *port) |
47 | { |
48 | /* spinlock held by upper layer, disable TX interrupt */ |
49 | u32 ier = ioread32(port->membase + TIMBUART_IER) & ~TXBAE; |
50 | iowrite32(ier, port->membase + TIMBUART_IER); |
51 | } |
52 | |
53 | static void timbuart_start_tx(struct uart_port *port) |
54 | { |
55 | struct timbuart_port *uart = |
56 | container_of(port, struct timbuart_port, port); |
57 | |
58 | /* do not transfer anything here -> fire off the tasklet */ |
59 | tasklet_schedule(t: &uart->tasklet); |
60 | } |
61 | |
62 | static unsigned int timbuart_tx_empty(struct uart_port *port) |
63 | { |
64 | u32 isr = ioread32(port->membase + TIMBUART_ISR); |
65 | |
66 | return (isr & TXBE) ? TIOCSER_TEMT : 0; |
67 | } |
68 | |
69 | static void timbuart_flush_buffer(struct uart_port *port) |
70 | { |
71 | if (!timbuart_tx_empty(port)) { |
72 | u8 ctl = ioread8(port->membase + TIMBUART_CTRL) | |
73 | TIMBUART_CTRL_FLSHTX; |
74 | |
75 | iowrite8(ctl, port->membase + TIMBUART_CTRL); |
76 | iowrite32(TXBF, port->membase + TIMBUART_ISR); |
77 | } |
78 | } |
79 | |
80 | static void timbuart_rx_chars(struct uart_port *port) |
81 | { |
82 | struct tty_port *tport = &port->state->port; |
83 | |
84 | while (ioread32(port->membase + TIMBUART_ISR) & RXDP) { |
85 | u8 ch = ioread8(port->membase + TIMBUART_RXFIFO); |
86 | port->icount.rx++; |
87 | tty_insert_flip_char(port: tport, ch, TTY_NORMAL); |
88 | } |
89 | |
90 | tty_flip_buffer_push(port: tport); |
91 | |
92 | dev_dbg(port->dev, "%s - total read %d bytes\n" , |
93 | __func__, port->icount.rx); |
94 | } |
95 | |
96 | static void timbuart_tx_chars(struct uart_port *port) |
97 | { |
98 | struct circ_buf *xmit = &port->state->xmit; |
99 | |
100 | while (!(ioread32(port->membase + TIMBUART_ISR) & TXBF) && |
101 | !uart_circ_empty(xmit)) { |
102 | iowrite8(xmit->buf[xmit->tail], |
103 | port->membase + TIMBUART_TXFIFO); |
104 | uart_xmit_advance(up: port, chars: 1); |
105 | } |
106 | |
107 | dev_dbg(port->dev, |
108 | "%s - total written %d bytes, CTL: %x, RTS: %x, baud: %x\n" , |
109 | __func__, |
110 | port->icount.tx, |
111 | ioread8(port->membase + TIMBUART_CTRL), |
112 | port->mctrl & TIOCM_RTS, |
113 | ioread8(port->membase + TIMBUART_BAUDRATE)); |
114 | } |
115 | |
116 | static void timbuart_handle_tx_port(struct uart_port *port, u32 isr, u32 *ier) |
117 | { |
118 | struct timbuart_port *uart = |
119 | container_of(port, struct timbuart_port, port); |
120 | struct circ_buf *xmit = &port->state->xmit; |
121 | |
122 | if (uart_circ_empty(xmit) || uart_tx_stopped(port)) |
123 | return; |
124 | |
125 | if (port->x_char) |
126 | return; |
127 | |
128 | if (isr & TXFLAGS) { |
129 | timbuart_tx_chars(port); |
130 | /* clear all TX interrupts */ |
131 | iowrite32(TXFLAGS, port->membase + TIMBUART_ISR); |
132 | |
133 | if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) |
134 | uart_write_wakeup(port); |
135 | } else |
136 | /* Re-enable any tx interrupt */ |
137 | *ier |= uart->last_ier & TXFLAGS; |
138 | |
139 | /* enable interrupts if there are chars in the transmit buffer, |
140 | * Or if we delivered some bytes and want the almost empty interrupt |
141 | * we wake up the upper layer later when we got the interrupt |
142 | * to give it some time to go out... |
143 | */ |
144 | if (!uart_circ_empty(xmit)) |
145 | *ier |= TXBAE; |
146 | |
147 | dev_dbg(port->dev, "%s - leaving\n" , __func__); |
148 | } |
149 | |
150 | static void timbuart_handle_rx_port(struct uart_port *port, u32 isr, u32 *ier) |
151 | { |
152 | if (isr & RXFLAGS) { |
153 | /* Some RX status is set */ |
154 | if (isr & RXBF) { |
155 | u8 ctl = ioread8(port->membase + TIMBUART_CTRL) | |
156 | TIMBUART_CTRL_FLSHRX; |
157 | iowrite8(ctl, port->membase + TIMBUART_CTRL); |
158 | port->icount.overrun++; |
159 | } else if (isr & (RXDP)) |
160 | timbuart_rx_chars(port); |
161 | |
162 | /* ack all RX interrupts */ |
163 | iowrite32(RXFLAGS, port->membase + TIMBUART_ISR); |
164 | } |
165 | |
166 | /* always have the RX interrupts enabled */ |
167 | *ier |= RXBAF | RXBF | RXTT; |
168 | |
169 | dev_dbg(port->dev, "%s - leaving\n" , __func__); |
170 | } |
171 | |
172 | static void timbuart_tasklet(struct tasklet_struct *t) |
173 | { |
174 | struct timbuart_port *uart = from_tasklet(uart, t, tasklet); |
175 | u32 isr, ier = 0; |
176 | |
177 | uart_port_lock(up: &uart->port); |
178 | |
179 | isr = ioread32(uart->port.membase + TIMBUART_ISR); |
180 | dev_dbg(uart->port.dev, "%s ISR: %x\n" , __func__, isr); |
181 | |
182 | if (!uart->usedma) |
183 | timbuart_handle_tx_port(port: &uart->port, isr, ier: &ier); |
184 | |
185 | timbuart_mctrl_check(port: &uart->port, isr, ier: &ier); |
186 | |
187 | if (!uart->usedma) |
188 | timbuart_handle_rx_port(port: &uart->port, isr, ier: &ier); |
189 | |
190 | iowrite32(ier, uart->port.membase + TIMBUART_IER); |
191 | |
192 | uart_port_unlock(up: &uart->port); |
193 | dev_dbg(uart->port.dev, "%s leaving\n" , __func__); |
194 | } |
195 | |
196 | static unsigned int timbuart_get_mctrl(struct uart_port *port) |
197 | { |
198 | u8 cts = ioread8(port->membase + TIMBUART_CTRL); |
199 | dev_dbg(port->dev, "%s - cts %x\n" , __func__, cts); |
200 | |
201 | if (cts & TIMBUART_CTRL_CTS) |
202 | return TIOCM_CTS | TIOCM_DSR | TIOCM_CAR; |
203 | else |
204 | return TIOCM_DSR | TIOCM_CAR; |
205 | } |
206 | |
207 | static void timbuart_set_mctrl(struct uart_port *port, unsigned int mctrl) |
208 | { |
209 | dev_dbg(port->dev, "%s - %x\n" , __func__, mctrl); |
210 | |
211 | if (mctrl & TIOCM_RTS) |
212 | iowrite8(TIMBUART_CTRL_RTS, port->membase + TIMBUART_CTRL); |
213 | else |
214 | iowrite8(0, port->membase + TIMBUART_CTRL); |
215 | } |
216 | |
217 | static void timbuart_mctrl_check(struct uart_port *port, u32 isr, u32 *ier) |
218 | { |
219 | unsigned int cts; |
220 | |
221 | if (isr & CTS_DELTA) { |
222 | /* ack */ |
223 | iowrite32(CTS_DELTA, port->membase + TIMBUART_ISR); |
224 | cts = timbuart_get_mctrl(port); |
225 | uart_handle_cts_change(uport: port, active: cts & TIOCM_CTS); |
226 | wake_up_interruptible(&port->state->port.delta_msr_wait); |
227 | } |
228 | |
229 | *ier |= CTS_DELTA; |
230 | } |
231 | |
232 | static void timbuart_break_ctl(struct uart_port *port, int ctl) |
233 | { |
234 | /* N/A */ |
235 | } |
236 | |
237 | static int timbuart_startup(struct uart_port *port) |
238 | { |
239 | struct timbuart_port *uart = |
240 | container_of(port, struct timbuart_port, port); |
241 | |
242 | dev_dbg(port->dev, "%s\n" , __func__); |
243 | |
244 | iowrite8(TIMBUART_CTRL_FLSHRX, port->membase + TIMBUART_CTRL); |
245 | iowrite32(0x1ff, port->membase + TIMBUART_ISR); |
246 | /* Enable all but TX interrupts */ |
247 | iowrite32(RXBAF | RXBF | RXTT | CTS_DELTA, |
248 | port->membase + TIMBUART_IER); |
249 | |
250 | return request_irq(irq: port->irq, handler: timbuart_handleinterrupt, IRQF_SHARED, |
251 | name: "timb-uart" , dev: uart); |
252 | } |
253 | |
254 | static void timbuart_shutdown(struct uart_port *port) |
255 | { |
256 | struct timbuart_port *uart = |
257 | container_of(port, struct timbuart_port, port); |
258 | dev_dbg(port->dev, "%s\n" , __func__); |
259 | free_irq(port->irq, uart); |
260 | iowrite32(0, port->membase + TIMBUART_IER); |
261 | |
262 | timbuart_flush_buffer(port); |
263 | } |
264 | |
265 | static int get_bindex(int baud) |
266 | { |
267 | int i; |
268 | |
269 | for (i = 0; i < ARRAY_SIZE(baudrates); i++) |
270 | if (baud <= baudrates[i]) |
271 | return i; |
272 | |
273 | return -1; |
274 | } |
275 | |
276 | static void timbuart_set_termios(struct uart_port *port, |
277 | struct ktermios *termios, |
278 | const struct ktermios *old) |
279 | { |
280 | unsigned int baud; |
281 | short bindex; |
282 | unsigned long flags; |
283 | |
284 | baud = uart_get_baud_rate(port, termios, old, min: 0, max: port->uartclk / 16); |
285 | bindex = get_bindex(baud); |
286 | dev_dbg(port->dev, "%s - bindex %d\n" , __func__, bindex); |
287 | |
288 | if (bindex < 0) |
289 | bindex = 0; |
290 | baud = baudrates[bindex]; |
291 | |
292 | /* The serial layer calls into this once with old = NULL when setting |
293 | up initially */ |
294 | if (old) |
295 | tty_termios_copy_hw(new: termios, old); |
296 | tty_termios_encode_baud_rate(termios, ibaud: baud, obaud: baud); |
297 | |
298 | uart_port_lock_irqsave(up: port, flags: &flags); |
299 | iowrite8((u8)bindex, port->membase + TIMBUART_BAUDRATE); |
300 | uart_update_timeout(port, cflag: termios->c_cflag, baud); |
301 | uart_port_unlock_irqrestore(up: port, flags); |
302 | } |
303 | |
304 | static const char *timbuart_type(struct uart_port *port) |
305 | { |
306 | return port->type == PORT_UNKNOWN ? "timbuart" : NULL; |
307 | } |
308 | |
309 | /* We do not request/release mappings of the registers here, |
310 | * currently it's done in the proble function. |
311 | */ |
312 | static void timbuart_release_port(struct uart_port *port) |
313 | { |
314 | struct platform_device *pdev = to_platform_device(port->dev); |
315 | int size = |
316 | resource_size(res: platform_get_resource(pdev, IORESOURCE_MEM, 0)); |
317 | |
318 | if (port->flags & UPF_IOREMAP) { |
319 | iounmap(addr: port->membase); |
320 | port->membase = NULL; |
321 | } |
322 | |
323 | release_mem_region(port->mapbase, size); |
324 | } |
325 | |
326 | static int timbuart_request_port(struct uart_port *port) |
327 | { |
328 | struct platform_device *pdev = to_platform_device(port->dev); |
329 | int size = |
330 | resource_size(res: platform_get_resource(pdev, IORESOURCE_MEM, 0)); |
331 | |
332 | if (!request_mem_region(port->mapbase, size, "timb-uart" )) |
333 | return -EBUSY; |
334 | |
335 | if (port->flags & UPF_IOREMAP) { |
336 | port->membase = ioremap(offset: port->mapbase, size); |
337 | if (port->membase == NULL) { |
338 | release_mem_region(port->mapbase, size); |
339 | return -ENOMEM; |
340 | } |
341 | } |
342 | |
343 | return 0; |
344 | } |
345 | |
346 | static irqreturn_t timbuart_handleinterrupt(int irq, void *devid) |
347 | { |
348 | struct timbuart_port *uart = (struct timbuart_port *)devid; |
349 | |
350 | if (ioread8(uart->port.membase + TIMBUART_IPR)) { |
351 | uart->last_ier = ioread32(uart->port.membase + TIMBUART_IER); |
352 | |
353 | /* disable interrupts, the tasklet enables them again */ |
354 | iowrite32(0, uart->port.membase + TIMBUART_IER); |
355 | |
356 | /* fire off bottom half */ |
357 | tasklet_schedule(t: &uart->tasklet); |
358 | |
359 | return IRQ_HANDLED; |
360 | } else |
361 | return IRQ_NONE; |
362 | } |
363 | |
364 | /* |
365 | * Configure/autoconfigure the port. |
366 | */ |
367 | static void timbuart_config_port(struct uart_port *port, int flags) |
368 | { |
369 | if (flags & UART_CONFIG_TYPE) { |
370 | port->type = PORT_TIMBUART; |
371 | timbuart_request_port(port); |
372 | } |
373 | } |
374 | |
375 | static int timbuart_verify_port(struct uart_port *port, |
376 | struct serial_struct *ser) |
377 | { |
378 | /* we don't want the core code to modify any port params */ |
379 | return -EINVAL; |
380 | } |
381 | |
382 | static const struct uart_ops timbuart_ops = { |
383 | .tx_empty = timbuart_tx_empty, |
384 | .set_mctrl = timbuart_set_mctrl, |
385 | .get_mctrl = timbuart_get_mctrl, |
386 | .stop_tx = timbuart_stop_tx, |
387 | .start_tx = timbuart_start_tx, |
388 | .flush_buffer = timbuart_flush_buffer, |
389 | .stop_rx = timbuart_stop_rx, |
390 | .break_ctl = timbuart_break_ctl, |
391 | .startup = timbuart_startup, |
392 | .shutdown = timbuart_shutdown, |
393 | .set_termios = timbuart_set_termios, |
394 | .type = timbuart_type, |
395 | .release_port = timbuart_release_port, |
396 | .request_port = timbuart_request_port, |
397 | .config_port = timbuart_config_port, |
398 | .verify_port = timbuart_verify_port |
399 | }; |
400 | |
401 | static struct uart_driver timbuart_driver = { |
402 | .owner = THIS_MODULE, |
403 | .driver_name = "timberdale_uart" , |
404 | .dev_name = "ttyTU" , |
405 | .major = TIMBUART_MAJOR, |
406 | .minor = TIMBUART_MINOR, |
407 | .nr = 1 |
408 | }; |
409 | |
410 | static int timbuart_probe(struct platform_device *dev) |
411 | { |
412 | int err, irq; |
413 | struct timbuart_port *uart; |
414 | struct resource *iomem; |
415 | |
416 | dev_dbg(&dev->dev, "%s\n" , __func__); |
417 | |
418 | uart = kzalloc(size: sizeof(*uart), GFP_KERNEL); |
419 | if (!uart) { |
420 | err = -EINVAL; |
421 | goto err_mem; |
422 | } |
423 | |
424 | uart->usedma = 0; |
425 | |
426 | uart->port.uartclk = 3250000 * 16; |
427 | uart->port.fifosize = TIMBUART_FIFO_SIZE; |
428 | uart->port.regshift = 2; |
429 | uart->port.iotype = UPIO_MEM; |
430 | uart->port.ops = &timbuart_ops; |
431 | uart->port.irq = 0; |
432 | uart->port.flags = UPF_BOOT_AUTOCONF | UPF_IOREMAP; |
433 | uart->port.line = 0; |
434 | uart->port.dev = &dev->dev; |
435 | |
436 | iomem = platform_get_resource(dev, IORESOURCE_MEM, 0); |
437 | if (!iomem) { |
438 | err = -ENOMEM; |
439 | goto err_register; |
440 | } |
441 | uart->port.mapbase = iomem->start; |
442 | uart->port.membase = NULL; |
443 | |
444 | irq = platform_get_irq(dev, 0); |
445 | if (irq < 0) { |
446 | err = -EINVAL; |
447 | goto err_register; |
448 | } |
449 | uart->port.irq = irq; |
450 | |
451 | tasklet_setup(t: &uart->tasklet, callback: timbuart_tasklet); |
452 | |
453 | err = uart_register_driver(uart: &timbuart_driver); |
454 | if (err) |
455 | goto err_register; |
456 | |
457 | err = uart_add_one_port(reg: &timbuart_driver, port: &uart->port); |
458 | if (err) |
459 | goto err_add_port; |
460 | |
461 | platform_set_drvdata(pdev: dev, data: uart); |
462 | |
463 | return 0; |
464 | |
465 | err_add_port: |
466 | uart_unregister_driver(uart: &timbuart_driver); |
467 | err_register: |
468 | kfree(objp: uart); |
469 | err_mem: |
470 | printk(KERN_ERR "timberdale: Failed to register Timberdale UART: %d\n" , |
471 | err); |
472 | |
473 | return err; |
474 | } |
475 | |
476 | static int timbuart_remove(struct platform_device *dev) |
477 | { |
478 | struct timbuart_port *uart = platform_get_drvdata(pdev: dev); |
479 | |
480 | tasklet_kill(t: &uart->tasklet); |
481 | uart_remove_one_port(reg: &timbuart_driver, port: &uart->port); |
482 | uart_unregister_driver(uart: &timbuart_driver); |
483 | kfree(objp: uart); |
484 | |
485 | return 0; |
486 | } |
487 | |
488 | static struct platform_driver timbuart_platform_driver = { |
489 | .driver = { |
490 | .name = "timb-uart" , |
491 | }, |
492 | .probe = timbuart_probe, |
493 | .remove = timbuart_remove, |
494 | }; |
495 | |
496 | module_platform_driver(timbuart_platform_driver); |
497 | |
498 | MODULE_DESCRIPTION("Timberdale UART driver" ); |
499 | MODULE_LICENSE("GPL v2" ); |
500 | MODULE_ALIAS("platform:timb-uart" ); |
501 | |
502 | |