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