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