brintos

brintos / linux-shallow public Read only

0
0
Text · 12.0 KiB · 43fa093 Raw
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