1ab4382d2SGreg Kroah-Hartman /* 2ab4382d2SGreg Kroah-Hartman * linux/drivers/char/amba.c 3ab4382d2SGreg Kroah-Hartman * 4ab4382d2SGreg Kroah-Hartman * Driver for AMBA serial ports 5ab4382d2SGreg Kroah-Hartman * 6ab4382d2SGreg Kroah-Hartman * Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o. 7ab4382d2SGreg Kroah-Hartman * 8ab4382d2SGreg Kroah-Hartman * Copyright 1999 ARM Limited 9ab4382d2SGreg Kroah-Hartman * Copyright (C) 2000 Deep Blue Solutions Ltd. 10ab4382d2SGreg Kroah-Hartman * 11ab4382d2SGreg Kroah-Hartman * This program is free software; you can redistribute it and/or modify 12ab4382d2SGreg Kroah-Hartman * it under the terms of the GNU General Public License as published by 13ab4382d2SGreg Kroah-Hartman * the Free Software Foundation; either version 2 of the License, or 14ab4382d2SGreg Kroah-Hartman * (at your option) any later version. 15ab4382d2SGreg Kroah-Hartman * 16ab4382d2SGreg Kroah-Hartman * This program is distributed in the hope that it will be useful, 17ab4382d2SGreg Kroah-Hartman * but WITHOUT ANY WARRANTY; without even the implied warranty of 18ab4382d2SGreg Kroah-Hartman * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19ab4382d2SGreg Kroah-Hartman * GNU General Public License for more details. 20ab4382d2SGreg Kroah-Hartman * 21ab4382d2SGreg Kroah-Hartman * You should have received a copy of the GNU General Public License 22ab4382d2SGreg Kroah-Hartman * along with this program; if not, write to the Free Software 23ab4382d2SGreg Kroah-Hartman * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 24ab4382d2SGreg Kroah-Hartman * 25ab4382d2SGreg Kroah-Hartman * This is a generic driver for ARM AMBA-type serial ports. They 26ab4382d2SGreg Kroah-Hartman * have a lot of 16550-like features, but are not register compatible. 27ab4382d2SGreg Kroah-Hartman * Note that although they do have CTS, DCD and DSR inputs, they do 28ab4382d2SGreg Kroah-Hartman * not have an RI input, nor do they have DTR or RTS outputs. If 29ab4382d2SGreg Kroah-Hartman * required, these have to be supplied via some other means (eg, GPIO) 30ab4382d2SGreg Kroah-Hartman * and hooked into this driver. 31ab4382d2SGreg Kroah-Hartman */ 32ab4382d2SGreg Kroah-Hartman 33ab4382d2SGreg Kroah-Hartman #if defined(CONFIG_SERIAL_AMBA_PL010_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) 34ab4382d2SGreg Kroah-Hartman #define SUPPORT_SYSRQ 35ab4382d2SGreg Kroah-Hartman #endif 36ab4382d2SGreg Kroah-Hartman 37ab4382d2SGreg Kroah-Hartman #include <linux/module.h> 38ab4382d2SGreg Kroah-Hartman #include <linux/ioport.h> 39ab4382d2SGreg Kroah-Hartman #include <linux/init.h> 40ab4382d2SGreg Kroah-Hartman #include <linux/console.h> 41ab4382d2SGreg Kroah-Hartman #include <linux/sysrq.h> 42ab4382d2SGreg Kroah-Hartman #include <linux/device.h> 43ab4382d2SGreg Kroah-Hartman #include <linux/tty.h> 44ab4382d2SGreg Kroah-Hartman #include <linux/tty_flip.h> 45ab4382d2SGreg Kroah-Hartman #include <linux/serial_core.h> 46ab4382d2SGreg Kroah-Hartman #include <linux/serial.h> 47ab4382d2SGreg Kroah-Hartman #include <linux/amba/bus.h> 48ab4382d2SGreg Kroah-Hartman #include <linux/amba/serial.h> 49ab4382d2SGreg Kroah-Hartman #include <linux/clk.h> 50ab4382d2SGreg Kroah-Hartman #include <linux/slab.h> 51ab4382d2SGreg Kroah-Hartman 52ab4382d2SGreg Kroah-Hartman #include <asm/io.h> 53ab4382d2SGreg Kroah-Hartman 54ab4382d2SGreg Kroah-Hartman #define UART_NR 8 55ab4382d2SGreg Kroah-Hartman 56ab4382d2SGreg Kroah-Hartman #define SERIAL_AMBA_MAJOR 204 57ab4382d2SGreg Kroah-Hartman #define SERIAL_AMBA_MINOR 16 58ab4382d2SGreg Kroah-Hartman #define SERIAL_AMBA_NR UART_NR 59ab4382d2SGreg Kroah-Hartman 60ab4382d2SGreg Kroah-Hartman #define AMBA_ISR_PASS_LIMIT 256 61ab4382d2SGreg Kroah-Hartman 62ab4382d2SGreg Kroah-Hartman #define UART_RX_DATA(s) (((s) & UART01x_FR_RXFE) == 0) 63ab4382d2SGreg Kroah-Hartman #define UART_TX_READY(s) (((s) & UART01x_FR_TXFF) == 0) 64ab4382d2SGreg Kroah-Hartman 65ab4382d2SGreg Kroah-Hartman #define UART_DUMMY_RSR_RX 256 66ab4382d2SGreg Kroah-Hartman #define UART_PORT_SIZE 64 67ab4382d2SGreg Kroah-Hartman 68ab4382d2SGreg Kroah-Hartman /* 69ab4382d2SGreg Kroah-Hartman * We wrap our port structure around the generic uart_port. 70ab4382d2SGreg Kroah-Hartman */ 71ab4382d2SGreg Kroah-Hartman struct uart_amba_port { 72ab4382d2SGreg Kroah-Hartman struct uart_port port; 73ab4382d2SGreg Kroah-Hartman struct clk *clk; 74ab4382d2SGreg Kroah-Hartman struct amba_device *dev; 75ab4382d2SGreg Kroah-Hartman struct amba_pl010_data *data; 76ab4382d2SGreg Kroah-Hartman unsigned int old_status; 77ab4382d2SGreg Kroah-Hartman }; 78ab4382d2SGreg Kroah-Hartman 79ab4382d2SGreg Kroah-Hartman static void pl010_stop_tx(struct uart_port *port) 80ab4382d2SGreg Kroah-Hartman { 81ab4382d2SGreg Kroah-Hartman struct uart_amba_port *uap = (struct uart_amba_port *)port; 82ab4382d2SGreg Kroah-Hartman unsigned int cr; 83ab4382d2SGreg Kroah-Hartman 84ab4382d2SGreg Kroah-Hartman cr = readb(uap->port.membase + UART010_CR); 85ab4382d2SGreg Kroah-Hartman cr &= ~UART010_CR_TIE; 86ab4382d2SGreg Kroah-Hartman writel(cr, uap->port.membase + UART010_CR); 87ab4382d2SGreg Kroah-Hartman } 88ab4382d2SGreg Kroah-Hartman 89ab4382d2SGreg Kroah-Hartman static void pl010_start_tx(struct uart_port *port) 90ab4382d2SGreg Kroah-Hartman { 91ab4382d2SGreg Kroah-Hartman struct uart_amba_port *uap = (struct uart_amba_port *)port; 92ab4382d2SGreg Kroah-Hartman unsigned int cr; 93ab4382d2SGreg Kroah-Hartman 94ab4382d2SGreg Kroah-Hartman cr = readb(uap->port.membase + UART010_CR); 95ab4382d2SGreg Kroah-Hartman cr |= UART010_CR_TIE; 96ab4382d2SGreg Kroah-Hartman writel(cr, uap->port.membase + UART010_CR); 97ab4382d2SGreg Kroah-Hartman } 98ab4382d2SGreg Kroah-Hartman 99ab4382d2SGreg Kroah-Hartman static void pl010_stop_rx(struct uart_port *port) 100ab4382d2SGreg Kroah-Hartman { 101ab4382d2SGreg Kroah-Hartman struct uart_amba_port *uap = (struct uart_amba_port *)port; 102ab4382d2SGreg Kroah-Hartman unsigned int cr; 103ab4382d2SGreg Kroah-Hartman 104ab4382d2SGreg Kroah-Hartman cr = readb(uap->port.membase + UART010_CR); 105ab4382d2SGreg Kroah-Hartman cr &= ~(UART010_CR_RIE | UART010_CR_RTIE); 106ab4382d2SGreg Kroah-Hartman writel(cr, uap->port.membase + UART010_CR); 107ab4382d2SGreg Kroah-Hartman } 108ab4382d2SGreg Kroah-Hartman 109ab4382d2SGreg Kroah-Hartman static void pl010_enable_ms(struct uart_port *port) 110ab4382d2SGreg Kroah-Hartman { 111ab4382d2SGreg Kroah-Hartman struct uart_amba_port *uap = (struct uart_amba_port *)port; 112ab4382d2SGreg Kroah-Hartman unsigned int cr; 113ab4382d2SGreg Kroah-Hartman 114ab4382d2SGreg Kroah-Hartman cr = readb(uap->port.membase + UART010_CR); 115ab4382d2SGreg Kroah-Hartman cr |= UART010_CR_MSIE; 116ab4382d2SGreg Kroah-Hartman writel(cr, uap->port.membase + UART010_CR); 117ab4382d2SGreg Kroah-Hartman } 118ab4382d2SGreg Kroah-Hartman 119ab4382d2SGreg Kroah-Hartman static void pl010_rx_chars(struct uart_amba_port *uap) 120ab4382d2SGreg Kroah-Hartman { 121ab4382d2SGreg Kroah-Hartman struct tty_struct *tty = uap->port.state->port.tty; 122ab4382d2SGreg Kroah-Hartman unsigned int status, ch, flag, rsr, max_count = 256; 123ab4382d2SGreg Kroah-Hartman 124ab4382d2SGreg Kroah-Hartman status = readb(uap->port.membase + UART01x_FR); 125ab4382d2SGreg Kroah-Hartman while (UART_RX_DATA(status) && max_count--) { 126ab4382d2SGreg Kroah-Hartman ch = readb(uap->port.membase + UART01x_DR); 127ab4382d2SGreg Kroah-Hartman flag = TTY_NORMAL; 128ab4382d2SGreg Kroah-Hartman 129ab4382d2SGreg Kroah-Hartman uap->port.icount.rx++; 130ab4382d2SGreg Kroah-Hartman 131ab4382d2SGreg Kroah-Hartman /* 132ab4382d2SGreg Kroah-Hartman * Note that the error handling code is 133ab4382d2SGreg Kroah-Hartman * out of the main execution path 134ab4382d2SGreg Kroah-Hartman */ 135ab4382d2SGreg Kroah-Hartman rsr = readb(uap->port.membase + UART01x_RSR) | UART_DUMMY_RSR_RX; 136ab4382d2SGreg Kroah-Hartman if (unlikely(rsr & UART01x_RSR_ANY)) { 137ab4382d2SGreg Kroah-Hartman writel(0, uap->port.membase + UART01x_ECR); 138ab4382d2SGreg Kroah-Hartman 139ab4382d2SGreg Kroah-Hartman if (rsr & UART01x_RSR_BE) { 140ab4382d2SGreg Kroah-Hartman rsr &= ~(UART01x_RSR_FE | UART01x_RSR_PE); 141ab4382d2SGreg Kroah-Hartman uap->port.icount.brk++; 142ab4382d2SGreg Kroah-Hartman if (uart_handle_break(&uap->port)) 143ab4382d2SGreg Kroah-Hartman goto ignore_char; 144ab4382d2SGreg Kroah-Hartman } else if (rsr & UART01x_RSR_PE) 145ab4382d2SGreg Kroah-Hartman uap->port.icount.parity++; 146ab4382d2SGreg Kroah-Hartman else if (rsr & UART01x_RSR_FE) 147ab4382d2SGreg Kroah-Hartman uap->port.icount.frame++; 148ab4382d2SGreg Kroah-Hartman if (rsr & UART01x_RSR_OE) 149ab4382d2SGreg Kroah-Hartman uap->port.icount.overrun++; 150ab4382d2SGreg Kroah-Hartman 151ab4382d2SGreg Kroah-Hartman rsr &= uap->port.read_status_mask; 152ab4382d2SGreg Kroah-Hartman 153ab4382d2SGreg Kroah-Hartman if (rsr & UART01x_RSR_BE) 154ab4382d2SGreg Kroah-Hartman flag = TTY_BREAK; 155ab4382d2SGreg Kroah-Hartman else if (rsr & UART01x_RSR_PE) 156ab4382d2SGreg Kroah-Hartman flag = TTY_PARITY; 157ab4382d2SGreg Kroah-Hartman else if (rsr & UART01x_RSR_FE) 158ab4382d2SGreg Kroah-Hartman flag = TTY_FRAME; 159ab4382d2SGreg Kroah-Hartman } 160ab4382d2SGreg Kroah-Hartman 161ab4382d2SGreg Kroah-Hartman if (uart_handle_sysrq_char(&uap->port, ch)) 162ab4382d2SGreg Kroah-Hartman goto ignore_char; 163ab4382d2SGreg Kroah-Hartman 164ab4382d2SGreg Kroah-Hartman uart_insert_char(&uap->port, rsr, UART01x_RSR_OE, ch, flag); 165ab4382d2SGreg Kroah-Hartman 166ab4382d2SGreg Kroah-Hartman ignore_char: 167ab4382d2SGreg Kroah-Hartman status = readb(uap->port.membase + UART01x_FR); 168ab4382d2SGreg Kroah-Hartman } 169ab4382d2SGreg Kroah-Hartman spin_unlock(&uap->port.lock); 170ab4382d2SGreg Kroah-Hartman tty_flip_buffer_push(tty); 171ab4382d2SGreg Kroah-Hartman spin_lock(&uap->port.lock); 172ab4382d2SGreg Kroah-Hartman } 173ab4382d2SGreg Kroah-Hartman 174ab4382d2SGreg Kroah-Hartman static void pl010_tx_chars(struct uart_amba_port *uap) 175ab4382d2SGreg Kroah-Hartman { 176ab4382d2SGreg Kroah-Hartman struct circ_buf *xmit = &uap->port.state->xmit; 177ab4382d2SGreg Kroah-Hartman int count; 178ab4382d2SGreg Kroah-Hartman 179ab4382d2SGreg Kroah-Hartman if (uap->port.x_char) { 180ab4382d2SGreg Kroah-Hartman writel(uap->port.x_char, uap->port.membase + UART01x_DR); 181ab4382d2SGreg Kroah-Hartman uap->port.icount.tx++; 182ab4382d2SGreg Kroah-Hartman uap->port.x_char = 0; 183ab4382d2SGreg Kroah-Hartman return; 184ab4382d2SGreg Kroah-Hartman } 185ab4382d2SGreg Kroah-Hartman if (uart_circ_empty(xmit) || uart_tx_stopped(&uap->port)) { 186ab4382d2SGreg Kroah-Hartman pl010_stop_tx(&uap->port); 187ab4382d2SGreg Kroah-Hartman return; 188ab4382d2SGreg Kroah-Hartman } 189ab4382d2SGreg Kroah-Hartman 190ab4382d2SGreg Kroah-Hartman count = uap->port.fifosize >> 1; 191ab4382d2SGreg Kroah-Hartman do { 192ab4382d2SGreg Kroah-Hartman writel(xmit->buf[xmit->tail], uap->port.membase + UART01x_DR); 193ab4382d2SGreg Kroah-Hartman xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); 194ab4382d2SGreg Kroah-Hartman uap->port.icount.tx++; 195ab4382d2SGreg Kroah-Hartman if (uart_circ_empty(xmit)) 196ab4382d2SGreg Kroah-Hartman break; 197ab4382d2SGreg Kroah-Hartman } while (--count > 0); 198ab4382d2SGreg Kroah-Hartman 199ab4382d2SGreg Kroah-Hartman if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) 200ab4382d2SGreg Kroah-Hartman uart_write_wakeup(&uap->port); 201ab4382d2SGreg Kroah-Hartman 202ab4382d2SGreg Kroah-Hartman if (uart_circ_empty(xmit)) 203ab4382d2SGreg Kroah-Hartman pl010_stop_tx(&uap->port); 204ab4382d2SGreg Kroah-Hartman } 205ab4382d2SGreg Kroah-Hartman 206ab4382d2SGreg Kroah-Hartman static void pl010_modem_status(struct uart_amba_port *uap) 207ab4382d2SGreg Kroah-Hartman { 208ab4382d2SGreg Kroah-Hartman unsigned int status, delta; 209ab4382d2SGreg Kroah-Hartman 210ab4382d2SGreg Kroah-Hartman writel(0, uap->port.membase + UART010_ICR); 211ab4382d2SGreg Kroah-Hartman 212ab4382d2SGreg Kroah-Hartman status = readb(uap->port.membase + UART01x_FR) & UART01x_FR_MODEM_ANY; 213ab4382d2SGreg Kroah-Hartman 214ab4382d2SGreg Kroah-Hartman delta = status ^ uap->old_status; 215ab4382d2SGreg Kroah-Hartman uap->old_status = status; 216ab4382d2SGreg Kroah-Hartman 217ab4382d2SGreg Kroah-Hartman if (!delta) 218ab4382d2SGreg Kroah-Hartman return; 219ab4382d2SGreg Kroah-Hartman 220ab4382d2SGreg Kroah-Hartman if (delta & UART01x_FR_DCD) 221ab4382d2SGreg Kroah-Hartman uart_handle_dcd_change(&uap->port, status & UART01x_FR_DCD); 222ab4382d2SGreg Kroah-Hartman 223ab4382d2SGreg Kroah-Hartman if (delta & UART01x_FR_DSR) 224ab4382d2SGreg Kroah-Hartman uap->port.icount.dsr++; 225ab4382d2SGreg Kroah-Hartman 226ab4382d2SGreg Kroah-Hartman if (delta & UART01x_FR_CTS) 227ab4382d2SGreg Kroah-Hartman uart_handle_cts_change(&uap->port, status & UART01x_FR_CTS); 228ab4382d2SGreg Kroah-Hartman 229ab4382d2SGreg Kroah-Hartman wake_up_interruptible(&uap->port.state->port.delta_msr_wait); 230ab4382d2SGreg Kroah-Hartman } 231ab4382d2SGreg Kroah-Hartman 232ab4382d2SGreg Kroah-Hartman static irqreturn_t pl010_int(int irq, void *dev_id) 233ab4382d2SGreg Kroah-Hartman { 234ab4382d2SGreg Kroah-Hartman struct uart_amba_port *uap = dev_id; 235ab4382d2SGreg Kroah-Hartman unsigned int status, pass_counter = AMBA_ISR_PASS_LIMIT; 236ab4382d2SGreg Kroah-Hartman int handled = 0; 237ab4382d2SGreg Kroah-Hartman 238ab4382d2SGreg Kroah-Hartman spin_lock(&uap->port.lock); 239ab4382d2SGreg Kroah-Hartman 240ab4382d2SGreg Kroah-Hartman status = readb(uap->port.membase + UART010_IIR); 241ab4382d2SGreg Kroah-Hartman if (status) { 242ab4382d2SGreg Kroah-Hartman do { 243ab4382d2SGreg Kroah-Hartman if (status & (UART010_IIR_RTIS | UART010_IIR_RIS)) 244ab4382d2SGreg Kroah-Hartman pl010_rx_chars(uap); 245ab4382d2SGreg Kroah-Hartman if (status & UART010_IIR_MIS) 246ab4382d2SGreg Kroah-Hartman pl010_modem_status(uap); 247ab4382d2SGreg Kroah-Hartman if (status & UART010_IIR_TIS) 248ab4382d2SGreg Kroah-Hartman pl010_tx_chars(uap); 249ab4382d2SGreg Kroah-Hartman 250ab4382d2SGreg Kroah-Hartman if (pass_counter-- == 0) 251ab4382d2SGreg Kroah-Hartman break; 252ab4382d2SGreg Kroah-Hartman 253ab4382d2SGreg Kroah-Hartman status = readb(uap->port.membase + UART010_IIR); 254ab4382d2SGreg Kroah-Hartman } while (status & (UART010_IIR_RTIS | UART010_IIR_RIS | 255ab4382d2SGreg Kroah-Hartman UART010_IIR_TIS)); 256ab4382d2SGreg Kroah-Hartman handled = 1; 257ab4382d2SGreg Kroah-Hartman } 258ab4382d2SGreg Kroah-Hartman 259ab4382d2SGreg Kroah-Hartman spin_unlock(&uap->port.lock); 260ab4382d2SGreg Kroah-Hartman 261ab4382d2SGreg Kroah-Hartman return IRQ_RETVAL(handled); 262ab4382d2SGreg Kroah-Hartman } 263ab4382d2SGreg Kroah-Hartman 264ab4382d2SGreg Kroah-Hartman static unsigned int pl010_tx_empty(struct uart_port *port) 265ab4382d2SGreg Kroah-Hartman { 266ab4382d2SGreg Kroah-Hartman struct uart_amba_port *uap = (struct uart_amba_port *)port; 267ab4382d2SGreg Kroah-Hartman unsigned int status = readb(uap->port.membase + UART01x_FR); 268ab4382d2SGreg Kroah-Hartman return status & UART01x_FR_BUSY ? 0 : TIOCSER_TEMT; 269ab4382d2SGreg Kroah-Hartman } 270ab4382d2SGreg Kroah-Hartman 271ab4382d2SGreg Kroah-Hartman static unsigned int pl010_get_mctrl(struct uart_port *port) 272ab4382d2SGreg Kroah-Hartman { 273ab4382d2SGreg Kroah-Hartman struct uart_amba_port *uap = (struct uart_amba_port *)port; 274ab4382d2SGreg Kroah-Hartman unsigned int result = 0; 275ab4382d2SGreg Kroah-Hartman unsigned int status; 276ab4382d2SGreg Kroah-Hartman 277ab4382d2SGreg Kroah-Hartman status = readb(uap->port.membase + UART01x_FR); 278ab4382d2SGreg Kroah-Hartman if (status & UART01x_FR_DCD) 279ab4382d2SGreg Kroah-Hartman result |= TIOCM_CAR; 280ab4382d2SGreg Kroah-Hartman if (status & UART01x_FR_DSR) 281ab4382d2SGreg Kroah-Hartman result |= TIOCM_DSR; 282ab4382d2SGreg Kroah-Hartman if (status & UART01x_FR_CTS) 283ab4382d2SGreg Kroah-Hartman result |= TIOCM_CTS; 284ab4382d2SGreg Kroah-Hartman 285ab4382d2SGreg Kroah-Hartman return result; 286ab4382d2SGreg Kroah-Hartman } 287ab4382d2SGreg Kroah-Hartman 288ab4382d2SGreg Kroah-Hartman static void pl010_set_mctrl(struct uart_port *port, unsigned int mctrl) 289ab4382d2SGreg Kroah-Hartman { 290ab4382d2SGreg Kroah-Hartman struct uart_amba_port *uap = (struct uart_amba_port *)port; 291ab4382d2SGreg Kroah-Hartman 292ab4382d2SGreg Kroah-Hartman if (uap->data) 293ab4382d2SGreg Kroah-Hartman uap->data->set_mctrl(uap->dev, uap->port.membase, mctrl); 294ab4382d2SGreg Kroah-Hartman } 295ab4382d2SGreg Kroah-Hartman 296ab4382d2SGreg Kroah-Hartman static void pl010_break_ctl(struct uart_port *port, int break_state) 297ab4382d2SGreg Kroah-Hartman { 298ab4382d2SGreg Kroah-Hartman struct uart_amba_port *uap = (struct uart_amba_port *)port; 299ab4382d2SGreg Kroah-Hartman unsigned long flags; 300ab4382d2SGreg Kroah-Hartman unsigned int lcr_h; 301ab4382d2SGreg Kroah-Hartman 302ab4382d2SGreg Kroah-Hartman spin_lock_irqsave(&uap->port.lock, flags); 303ab4382d2SGreg Kroah-Hartman lcr_h = readb(uap->port.membase + UART010_LCRH); 304ab4382d2SGreg Kroah-Hartman if (break_state == -1) 305ab4382d2SGreg Kroah-Hartman lcr_h |= UART01x_LCRH_BRK; 306ab4382d2SGreg Kroah-Hartman else 307ab4382d2SGreg Kroah-Hartman lcr_h &= ~UART01x_LCRH_BRK; 308ab4382d2SGreg Kroah-Hartman writel(lcr_h, uap->port.membase + UART010_LCRH); 309ab4382d2SGreg Kroah-Hartman spin_unlock_irqrestore(&uap->port.lock, flags); 310ab4382d2SGreg Kroah-Hartman } 311ab4382d2SGreg Kroah-Hartman 312ab4382d2SGreg Kroah-Hartman static int pl010_startup(struct uart_port *port) 313ab4382d2SGreg Kroah-Hartman { 314ab4382d2SGreg Kroah-Hartman struct uart_amba_port *uap = (struct uart_amba_port *)port; 315ab4382d2SGreg Kroah-Hartman int retval; 316ab4382d2SGreg Kroah-Hartman 317ab4382d2SGreg Kroah-Hartman /* 318ab4382d2SGreg Kroah-Hartman * Try to enable the clock producer. 319ab4382d2SGreg Kroah-Hartman */ 320ab4382d2SGreg Kroah-Hartman retval = clk_enable(uap->clk); 321ab4382d2SGreg Kroah-Hartman if (retval) 322ab4382d2SGreg Kroah-Hartman goto out; 323ab4382d2SGreg Kroah-Hartman 324ab4382d2SGreg Kroah-Hartman uap->port.uartclk = clk_get_rate(uap->clk); 325ab4382d2SGreg Kroah-Hartman 326ab4382d2SGreg Kroah-Hartman /* 327ab4382d2SGreg Kroah-Hartman * Allocate the IRQ 328ab4382d2SGreg Kroah-Hartman */ 329ab4382d2SGreg Kroah-Hartman retval = request_irq(uap->port.irq, pl010_int, 0, "uart-pl010", uap); 330ab4382d2SGreg Kroah-Hartman if (retval) 331ab4382d2SGreg Kroah-Hartman goto clk_dis; 332ab4382d2SGreg Kroah-Hartman 333ab4382d2SGreg Kroah-Hartman /* 334ab4382d2SGreg Kroah-Hartman * initialise the old status of the modem signals 335ab4382d2SGreg Kroah-Hartman */ 336ab4382d2SGreg Kroah-Hartman uap->old_status = readb(uap->port.membase + UART01x_FR) & UART01x_FR_MODEM_ANY; 337ab4382d2SGreg Kroah-Hartman 338ab4382d2SGreg Kroah-Hartman /* 339ab4382d2SGreg Kroah-Hartman * Finally, enable interrupts 340ab4382d2SGreg Kroah-Hartman */ 341ab4382d2SGreg Kroah-Hartman writel(UART01x_CR_UARTEN | UART010_CR_RIE | UART010_CR_RTIE, 342ab4382d2SGreg Kroah-Hartman uap->port.membase + UART010_CR); 343ab4382d2SGreg Kroah-Hartman 344ab4382d2SGreg Kroah-Hartman return 0; 345ab4382d2SGreg Kroah-Hartman 346ab4382d2SGreg Kroah-Hartman clk_dis: 347ab4382d2SGreg Kroah-Hartman clk_disable(uap->clk); 348ab4382d2SGreg Kroah-Hartman out: 349ab4382d2SGreg Kroah-Hartman return retval; 350ab4382d2SGreg Kroah-Hartman } 351ab4382d2SGreg Kroah-Hartman 352ab4382d2SGreg Kroah-Hartman static void pl010_shutdown(struct uart_port *port) 353ab4382d2SGreg Kroah-Hartman { 354ab4382d2SGreg Kroah-Hartman struct uart_amba_port *uap = (struct uart_amba_port *)port; 355ab4382d2SGreg Kroah-Hartman 356ab4382d2SGreg Kroah-Hartman /* 357ab4382d2SGreg Kroah-Hartman * Free the interrupt 358ab4382d2SGreg Kroah-Hartman */ 359ab4382d2SGreg Kroah-Hartman free_irq(uap->port.irq, uap); 360ab4382d2SGreg Kroah-Hartman 361ab4382d2SGreg Kroah-Hartman /* 362ab4382d2SGreg Kroah-Hartman * disable all interrupts, disable the port 363ab4382d2SGreg Kroah-Hartman */ 364ab4382d2SGreg Kroah-Hartman writel(0, uap->port.membase + UART010_CR); 365ab4382d2SGreg Kroah-Hartman 366ab4382d2SGreg Kroah-Hartman /* disable break condition and fifos */ 367ab4382d2SGreg Kroah-Hartman writel(readb(uap->port.membase + UART010_LCRH) & 368ab4382d2SGreg Kroah-Hartman ~(UART01x_LCRH_BRK | UART01x_LCRH_FEN), 369ab4382d2SGreg Kroah-Hartman uap->port.membase + UART010_LCRH); 370ab4382d2SGreg Kroah-Hartman 371ab4382d2SGreg Kroah-Hartman /* 372ab4382d2SGreg Kroah-Hartman * Shut down the clock producer 373ab4382d2SGreg Kroah-Hartman */ 374ab4382d2SGreg Kroah-Hartman clk_disable(uap->clk); 375ab4382d2SGreg Kroah-Hartman } 376ab4382d2SGreg Kroah-Hartman 377ab4382d2SGreg Kroah-Hartman static void 378ab4382d2SGreg Kroah-Hartman pl010_set_termios(struct uart_port *port, struct ktermios *termios, 379ab4382d2SGreg Kroah-Hartman struct ktermios *old) 380ab4382d2SGreg Kroah-Hartman { 381ab4382d2SGreg Kroah-Hartman struct uart_amba_port *uap = (struct uart_amba_port *)port; 382ab4382d2SGreg Kroah-Hartman unsigned int lcr_h, old_cr; 383ab4382d2SGreg Kroah-Hartman unsigned long flags; 384ab4382d2SGreg Kroah-Hartman unsigned int baud, quot; 385ab4382d2SGreg Kroah-Hartman 386ab4382d2SGreg Kroah-Hartman /* 387ab4382d2SGreg Kroah-Hartman * Ask the core to calculate the divisor for us. 388ab4382d2SGreg Kroah-Hartman */ 389ab4382d2SGreg Kroah-Hartman baud = uart_get_baud_rate(port, termios, old, 0, uap->port.uartclk/16); 390ab4382d2SGreg Kroah-Hartman quot = uart_get_divisor(port, baud); 391ab4382d2SGreg Kroah-Hartman 392ab4382d2SGreg Kroah-Hartman switch (termios->c_cflag & CSIZE) { 393ab4382d2SGreg Kroah-Hartman case CS5: 394ab4382d2SGreg Kroah-Hartman lcr_h = UART01x_LCRH_WLEN_5; 395ab4382d2SGreg Kroah-Hartman break; 396ab4382d2SGreg Kroah-Hartman case CS6: 397ab4382d2SGreg Kroah-Hartman lcr_h = UART01x_LCRH_WLEN_6; 398ab4382d2SGreg Kroah-Hartman break; 399ab4382d2SGreg Kroah-Hartman case CS7: 400ab4382d2SGreg Kroah-Hartman lcr_h = UART01x_LCRH_WLEN_7; 401ab4382d2SGreg Kroah-Hartman break; 402ab4382d2SGreg Kroah-Hartman default: // CS8 403ab4382d2SGreg Kroah-Hartman lcr_h = UART01x_LCRH_WLEN_8; 404ab4382d2SGreg Kroah-Hartman break; 405ab4382d2SGreg Kroah-Hartman } 406ab4382d2SGreg Kroah-Hartman if (termios->c_cflag & CSTOPB) 407ab4382d2SGreg Kroah-Hartman lcr_h |= UART01x_LCRH_STP2; 408ab4382d2SGreg Kroah-Hartman if (termios->c_cflag & PARENB) { 409ab4382d2SGreg Kroah-Hartman lcr_h |= UART01x_LCRH_PEN; 410ab4382d2SGreg Kroah-Hartman if (!(termios->c_cflag & PARODD)) 411ab4382d2SGreg Kroah-Hartman lcr_h |= UART01x_LCRH_EPS; 412ab4382d2SGreg Kroah-Hartman } 413ab4382d2SGreg Kroah-Hartman if (uap->port.fifosize > 1) 414ab4382d2SGreg Kroah-Hartman lcr_h |= UART01x_LCRH_FEN; 415ab4382d2SGreg Kroah-Hartman 416ab4382d2SGreg Kroah-Hartman spin_lock_irqsave(&uap->port.lock, flags); 417ab4382d2SGreg Kroah-Hartman 418ab4382d2SGreg Kroah-Hartman /* 419ab4382d2SGreg Kroah-Hartman * Update the per-port timeout. 420ab4382d2SGreg Kroah-Hartman */ 421ab4382d2SGreg Kroah-Hartman uart_update_timeout(port, termios->c_cflag, baud); 422ab4382d2SGreg Kroah-Hartman 423ab4382d2SGreg Kroah-Hartman uap->port.read_status_mask = UART01x_RSR_OE; 424ab4382d2SGreg Kroah-Hartman if (termios->c_iflag & INPCK) 425ab4382d2SGreg Kroah-Hartman uap->port.read_status_mask |= UART01x_RSR_FE | UART01x_RSR_PE; 426ab4382d2SGreg Kroah-Hartman if (termios->c_iflag & (BRKINT | PARMRK)) 427ab4382d2SGreg Kroah-Hartman uap->port.read_status_mask |= UART01x_RSR_BE; 428ab4382d2SGreg Kroah-Hartman 429ab4382d2SGreg Kroah-Hartman /* 430ab4382d2SGreg Kroah-Hartman * Characters to ignore 431ab4382d2SGreg Kroah-Hartman */ 432ab4382d2SGreg Kroah-Hartman uap->port.ignore_status_mask = 0; 433ab4382d2SGreg Kroah-Hartman if (termios->c_iflag & IGNPAR) 434ab4382d2SGreg Kroah-Hartman uap->port.ignore_status_mask |= UART01x_RSR_FE | UART01x_RSR_PE; 435ab4382d2SGreg Kroah-Hartman if (termios->c_iflag & IGNBRK) { 436ab4382d2SGreg Kroah-Hartman uap->port.ignore_status_mask |= UART01x_RSR_BE; 437ab4382d2SGreg Kroah-Hartman /* 438ab4382d2SGreg Kroah-Hartman * If we're ignoring parity and break indicators, 439ab4382d2SGreg Kroah-Hartman * ignore overruns too (for real raw support). 440ab4382d2SGreg Kroah-Hartman */ 441ab4382d2SGreg Kroah-Hartman if (termios->c_iflag & IGNPAR) 442ab4382d2SGreg Kroah-Hartman uap->port.ignore_status_mask |= UART01x_RSR_OE; 443ab4382d2SGreg Kroah-Hartman } 444ab4382d2SGreg Kroah-Hartman 445ab4382d2SGreg Kroah-Hartman /* 446ab4382d2SGreg Kroah-Hartman * Ignore all characters if CREAD is not set. 447ab4382d2SGreg Kroah-Hartman */ 448ab4382d2SGreg Kroah-Hartman if ((termios->c_cflag & CREAD) == 0) 449ab4382d2SGreg Kroah-Hartman uap->port.ignore_status_mask |= UART_DUMMY_RSR_RX; 450ab4382d2SGreg Kroah-Hartman 451ab4382d2SGreg Kroah-Hartman /* first, disable everything */ 452ab4382d2SGreg Kroah-Hartman old_cr = readb(uap->port.membase + UART010_CR) & ~UART010_CR_MSIE; 453ab4382d2SGreg Kroah-Hartman 454ab4382d2SGreg Kroah-Hartman if (UART_ENABLE_MS(port, termios->c_cflag)) 455ab4382d2SGreg Kroah-Hartman old_cr |= UART010_CR_MSIE; 456ab4382d2SGreg Kroah-Hartman 457ab4382d2SGreg Kroah-Hartman writel(0, uap->port.membase + UART010_CR); 458ab4382d2SGreg Kroah-Hartman 459ab4382d2SGreg Kroah-Hartman /* Set baud rate */ 460ab4382d2SGreg Kroah-Hartman quot -= 1; 461ab4382d2SGreg Kroah-Hartman writel((quot & 0xf00) >> 8, uap->port.membase + UART010_LCRM); 462ab4382d2SGreg Kroah-Hartman writel(quot & 0xff, uap->port.membase + UART010_LCRL); 463ab4382d2SGreg Kroah-Hartman 464ab4382d2SGreg Kroah-Hartman /* 465ab4382d2SGreg Kroah-Hartman * ----------v----------v----------v----------v----- 466ab4382d2SGreg Kroah-Hartman * NOTE: MUST BE WRITTEN AFTER UARTLCR_M & UARTLCR_L 467ab4382d2SGreg Kroah-Hartman * ----------^----------^----------^----------^----- 468ab4382d2SGreg Kroah-Hartman */ 469ab4382d2SGreg Kroah-Hartman writel(lcr_h, uap->port.membase + UART010_LCRH); 470ab4382d2SGreg Kroah-Hartman writel(old_cr, uap->port.membase + UART010_CR); 471ab4382d2SGreg Kroah-Hartman 472ab4382d2SGreg Kroah-Hartman spin_unlock_irqrestore(&uap->port.lock, flags); 473ab4382d2SGreg Kroah-Hartman } 474ab4382d2SGreg Kroah-Hartman 475ab4382d2SGreg Kroah-Hartman static void pl010_set_ldisc(struct uart_port *port, int new) 476ab4382d2SGreg Kroah-Hartman { 477ab4382d2SGreg Kroah-Hartman if (new == N_PPS) { 478ab4382d2SGreg Kroah-Hartman port->flags |= UPF_HARDPPS_CD; 479ab4382d2SGreg Kroah-Hartman pl010_enable_ms(port); 480ab4382d2SGreg Kroah-Hartman } else 481ab4382d2SGreg Kroah-Hartman port->flags &= ~UPF_HARDPPS_CD; 482ab4382d2SGreg Kroah-Hartman } 483ab4382d2SGreg Kroah-Hartman 484ab4382d2SGreg Kroah-Hartman static const char *pl010_type(struct uart_port *port) 485ab4382d2SGreg Kroah-Hartman { 486ab4382d2SGreg Kroah-Hartman return port->type == PORT_AMBA ? "AMBA" : NULL; 487ab4382d2SGreg Kroah-Hartman } 488ab4382d2SGreg Kroah-Hartman 489ab4382d2SGreg Kroah-Hartman /* 490ab4382d2SGreg Kroah-Hartman * Release the memory region(s) being used by 'port' 491ab4382d2SGreg Kroah-Hartman */ 492ab4382d2SGreg Kroah-Hartman static void pl010_release_port(struct uart_port *port) 493ab4382d2SGreg Kroah-Hartman { 494ab4382d2SGreg Kroah-Hartman release_mem_region(port->mapbase, UART_PORT_SIZE); 495ab4382d2SGreg Kroah-Hartman } 496ab4382d2SGreg Kroah-Hartman 497ab4382d2SGreg Kroah-Hartman /* 498ab4382d2SGreg Kroah-Hartman * Request the memory region(s) being used by 'port' 499ab4382d2SGreg Kroah-Hartman */ 500ab4382d2SGreg Kroah-Hartman static int pl010_request_port(struct uart_port *port) 501ab4382d2SGreg Kroah-Hartman { 502ab4382d2SGreg Kroah-Hartman return request_mem_region(port->mapbase, UART_PORT_SIZE, "uart-pl010") 503ab4382d2SGreg Kroah-Hartman != NULL ? 0 : -EBUSY; 504ab4382d2SGreg Kroah-Hartman } 505ab4382d2SGreg Kroah-Hartman 506ab4382d2SGreg Kroah-Hartman /* 507ab4382d2SGreg Kroah-Hartman * Configure/autoconfigure the port. 508ab4382d2SGreg Kroah-Hartman */ 509ab4382d2SGreg Kroah-Hartman static void pl010_config_port(struct uart_port *port, int flags) 510ab4382d2SGreg Kroah-Hartman { 511ab4382d2SGreg Kroah-Hartman if (flags & UART_CONFIG_TYPE) { 512ab4382d2SGreg Kroah-Hartman port->type = PORT_AMBA; 513ab4382d2SGreg Kroah-Hartman pl010_request_port(port); 514ab4382d2SGreg Kroah-Hartman } 515ab4382d2SGreg Kroah-Hartman } 516ab4382d2SGreg Kroah-Hartman 517ab4382d2SGreg Kroah-Hartman /* 518ab4382d2SGreg Kroah-Hartman * verify the new serial_struct (for TIOCSSERIAL). 519ab4382d2SGreg Kroah-Hartman */ 520ab4382d2SGreg Kroah-Hartman static int pl010_verify_port(struct uart_port *port, struct serial_struct *ser) 521ab4382d2SGreg Kroah-Hartman { 522ab4382d2SGreg Kroah-Hartman int ret = 0; 523ab4382d2SGreg Kroah-Hartman if (ser->type != PORT_UNKNOWN && ser->type != PORT_AMBA) 524ab4382d2SGreg Kroah-Hartman ret = -EINVAL; 525ab4382d2SGreg Kroah-Hartman if (ser->irq < 0 || ser->irq >= nr_irqs) 526ab4382d2SGreg Kroah-Hartman ret = -EINVAL; 527ab4382d2SGreg Kroah-Hartman if (ser->baud_base < 9600) 528ab4382d2SGreg Kroah-Hartman ret = -EINVAL; 529ab4382d2SGreg Kroah-Hartman return ret; 530ab4382d2SGreg Kroah-Hartman } 531ab4382d2SGreg Kroah-Hartman 532ab4382d2SGreg Kroah-Hartman static struct uart_ops amba_pl010_pops = { 533ab4382d2SGreg Kroah-Hartman .tx_empty = pl010_tx_empty, 534ab4382d2SGreg Kroah-Hartman .set_mctrl = pl010_set_mctrl, 535ab4382d2SGreg Kroah-Hartman .get_mctrl = pl010_get_mctrl, 536ab4382d2SGreg Kroah-Hartman .stop_tx = pl010_stop_tx, 537ab4382d2SGreg Kroah-Hartman .start_tx = pl010_start_tx, 538ab4382d2SGreg Kroah-Hartman .stop_rx = pl010_stop_rx, 539ab4382d2SGreg Kroah-Hartman .enable_ms = pl010_enable_ms, 540ab4382d2SGreg Kroah-Hartman .break_ctl = pl010_break_ctl, 541ab4382d2SGreg Kroah-Hartman .startup = pl010_startup, 542ab4382d2SGreg Kroah-Hartman .shutdown = pl010_shutdown, 543ab4382d2SGreg Kroah-Hartman .set_termios = pl010_set_termios, 544ab4382d2SGreg Kroah-Hartman .set_ldisc = pl010_set_ldisc, 545ab4382d2SGreg Kroah-Hartman .type = pl010_type, 546ab4382d2SGreg Kroah-Hartman .release_port = pl010_release_port, 547ab4382d2SGreg Kroah-Hartman .request_port = pl010_request_port, 548ab4382d2SGreg Kroah-Hartman .config_port = pl010_config_port, 549ab4382d2SGreg Kroah-Hartman .verify_port = pl010_verify_port, 550ab4382d2SGreg Kroah-Hartman }; 551ab4382d2SGreg Kroah-Hartman 552ab4382d2SGreg Kroah-Hartman static struct uart_amba_port *amba_ports[UART_NR]; 553ab4382d2SGreg Kroah-Hartman 554ab4382d2SGreg Kroah-Hartman #ifdef CONFIG_SERIAL_AMBA_PL010_CONSOLE 555ab4382d2SGreg Kroah-Hartman 556ab4382d2SGreg Kroah-Hartman static void pl010_console_putchar(struct uart_port *port, int ch) 557ab4382d2SGreg Kroah-Hartman { 558ab4382d2SGreg Kroah-Hartman struct uart_amba_port *uap = (struct uart_amba_port *)port; 559ab4382d2SGreg Kroah-Hartman unsigned int status; 560ab4382d2SGreg Kroah-Hartman 561ab4382d2SGreg Kroah-Hartman do { 562ab4382d2SGreg Kroah-Hartman status = readb(uap->port.membase + UART01x_FR); 563ab4382d2SGreg Kroah-Hartman barrier(); 564ab4382d2SGreg Kroah-Hartman } while (!UART_TX_READY(status)); 565ab4382d2SGreg Kroah-Hartman writel(ch, uap->port.membase + UART01x_DR); 566ab4382d2SGreg Kroah-Hartman } 567ab4382d2SGreg Kroah-Hartman 568ab4382d2SGreg Kroah-Hartman static void 569ab4382d2SGreg Kroah-Hartman pl010_console_write(struct console *co, const char *s, unsigned int count) 570ab4382d2SGreg Kroah-Hartman { 571ab4382d2SGreg Kroah-Hartman struct uart_amba_port *uap = amba_ports[co->index]; 572ab4382d2SGreg Kroah-Hartman unsigned int status, old_cr; 573ab4382d2SGreg Kroah-Hartman 574ab4382d2SGreg Kroah-Hartman clk_enable(uap->clk); 575ab4382d2SGreg Kroah-Hartman 576ab4382d2SGreg Kroah-Hartman /* 577ab4382d2SGreg Kroah-Hartman * First save the CR then disable the interrupts 578ab4382d2SGreg Kroah-Hartman */ 579ab4382d2SGreg Kroah-Hartman old_cr = readb(uap->port.membase + UART010_CR); 580ab4382d2SGreg Kroah-Hartman writel(UART01x_CR_UARTEN, uap->port.membase + UART010_CR); 581ab4382d2SGreg Kroah-Hartman 582ab4382d2SGreg Kroah-Hartman uart_console_write(&uap->port, s, count, pl010_console_putchar); 583ab4382d2SGreg Kroah-Hartman 584ab4382d2SGreg Kroah-Hartman /* 585ab4382d2SGreg Kroah-Hartman * Finally, wait for transmitter to become empty 586ab4382d2SGreg Kroah-Hartman * and restore the TCR 587ab4382d2SGreg Kroah-Hartman */ 588ab4382d2SGreg Kroah-Hartman do { 589ab4382d2SGreg Kroah-Hartman status = readb(uap->port.membase + UART01x_FR); 590ab4382d2SGreg Kroah-Hartman barrier(); 591ab4382d2SGreg Kroah-Hartman } while (status & UART01x_FR_BUSY); 592ab4382d2SGreg Kroah-Hartman writel(old_cr, uap->port.membase + UART010_CR); 593ab4382d2SGreg Kroah-Hartman 594ab4382d2SGreg Kroah-Hartman clk_disable(uap->clk); 595ab4382d2SGreg Kroah-Hartman } 596ab4382d2SGreg Kroah-Hartman 597ab4382d2SGreg Kroah-Hartman static void __init 598ab4382d2SGreg Kroah-Hartman pl010_console_get_options(struct uart_amba_port *uap, int *baud, 599ab4382d2SGreg Kroah-Hartman int *parity, int *bits) 600ab4382d2SGreg Kroah-Hartman { 601ab4382d2SGreg Kroah-Hartman if (readb(uap->port.membase + UART010_CR) & UART01x_CR_UARTEN) { 602ab4382d2SGreg Kroah-Hartman unsigned int lcr_h, quot; 603ab4382d2SGreg Kroah-Hartman lcr_h = readb(uap->port.membase + UART010_LCRH); 604ab4382d2SGreg Kroah-Hartman 605ab4382d2SGreg Kroah-Hartman *parity = 'n'; 606ab4382d2SGreg Kroah-Hartman if (lcr_h & UART01x_LCRH_PEN) { 607ab4382d2SGreg Kroah-Hartman if (lcr_h & UART01x_LCRH_EPS) 608ab4382d2SGreg Kroah-Hartman *parity = 'e'; 609ab4382d2SGreg Kroah-Hartman else 610ab4382d2SGreg Kroah-Hartman *parity = 'o'; 611ab4382d2SGreg Kroah-Hartman } 612ab4382d2SGreg Kroah-Hartman 613ab4382d2SGreg Kroah-Hartman if ((lcr_h & 0x60) == UART01x_LCRH_WLEN_7) 614ab4382d2SGreg Kroah-Hartman *bits = 7; 615ab4382d2SGreg Kroah-Hartman else 616ab4382d2SGreg Kroah-Hartman *bits = 8; 617ab4382d2SGreg Kroah-Hartman 618ab4382d2SGreg Kroah-Hartman quot = readb(uap->port.membase + UART010_LCRL) | 619ab4382d2SGreg Kroah-Hartman readb(uap->port.membase + UART010_LCRM) << 8; 620ab4382d2SGreg Kroah-Hartman *baud = uap->port.uartclk / (16 * (quot + 1)); 621ab4382d2SGreg Kroah-Hartman } 622ab4382d2SGreg Kroah-Hartman } 623ab4382d2SGreg Kroah-Hartman 624ab4382d2SGreg Kroah-Hartman static int __init pl010_console_setup(struct console *co, char *options) 625ab4382d2SGreg Kroah-Hartman { 626ab4382d2SGreg Kroah-Hartman struct uart_amba_port *uap; 627ab4382d2SGreg Kroah-Hartman int baud = 38400; 628ab4382d2SGreg Kroah-Hartman int bits = 8; 629ab4382d2SGreg Kroah-Hartman int parity = 'n'; 630ab4382d2SGreg Kroah-Hartman int flow = 'n'; 631ab4382d2SGreg Kroah-Hartman 632ab4382d2SGreg Kroah-Hartman /* 633ab4382d2SGreg Kroah-Hartman * Check whether an invalid uart number has been specified, and 634ab4382d2SGreg Kroah-Hartman * if so, search for the first available port that does have 635ab4382d2SGreg Kroah-Hartman * console support. 636ab4382d2SGreg Kroah-Hartman */ 637ab4382d2SGreg Kroah-Hartman if (co->index >= UART_NR) 638ab4382d2SGreg Kroah-Hartman co->index = 0; 639ab4382d2SGreg Kroah-Hartman uap = amba_ports[co->index]; 640ab4382d2SGreg Kroah-Hartman if (!uap) 641ab4382d2SGreg Kroah-Hartman return -ENODEV; 642ab4382d2SGreg Kroah-Hartman 643ab4382d2SGreg Kroah-Hartman uap->port.uartclk = clk_get_rate(uap->clk); 644ab4382d2SGreg Kroah-Hartman 645ab4382d2SGreg Kroah-Hartman if (options) 646ab4382d2SGreg Kroah-Hartman uart_parse_options(options, &baud, &parity, &bits, &flow); 647ab4382d2SGreg Kroah-Hartman else 648ab4382d2SGreg Kroah-Hartman pl010_console_get_options(uap, &baud, &parity, &bits); 649ab4382d2SGreg Kroah-Hartman 650ab4382d2SGreg Kroah-Hartman return uart_set_options(&uap->port, co, baud, parity, bits, flow); 651ab4382d2SGreg Kroah-Hartman } 652ab4382d2SGreg Kroah-Hartman 653ab4382d2SGreg Kroah-Hartman static struct uart_driver amba_reg; 654ab4382d2SGreg Kroah-Hartman static struct console amba_console = { 655ab4382d2SGreg Kroah-Hartman .name = "ttyAM", 656ab4382d2SGreg Kroah-Hartman .write = pl010_console_write, 657ab4382d2SGreg Kroah-Hartman .device = uart_console_device, 658ab4382d2SGreg Kroah-Hartman .setup = pl010_console_setup, 659ab4382d2SGreg Kroah-Hartman .flags = CON_PRINTBUFFER, 660ab4382d2SGreg Kroah-Hartman .index = -1, 661ab4382d2SGreg Kroah-Hartman .data = &amba_reg, 662ab4382d2SGreg Kroah-Hartman }; 663ab4382d2SGreg Kroah-Hartman 664ab4382d2SGreg Kroah-Hartman #define AMBA_CONSOLE &amba_console 665ab4382d2SGreg Kroah-Hartman #else 666ab4382d2SGreg Kroah-Hartman #define AMBA_CONSOLE NULL 667ab4382d2SGreg Kroah-Hartman #endif 668ab4382d2SGreg Kroah-Hartman 669ab4382d2SGreg Kroah-Hartman static struct uart_driver amba_reg = { 670ab4382d2SGreg Kroah-Hartman .owner = THIS_MODULE, 671ab4382d2SGreg Kroah-Hartman .driver_name = "ttyAM", 672ab4382d2SGreg Kroah-Hartman .dev_name = "ttyAM", 673ab4382d2SGreg Kroah-Hartman .major = SERIAL_AMBA_MAJOR, 674ab4382d2SGreg Kroah-Hartman .minor = SERIAL_AMBA_MINOR, 675ab4382d2SGreg Kroah-Hartman .nr = UART_NR, 676ab4382d2SGreg Kroah-Hartman .cons = AMBA_CONSOLE, 677ab4382d2SGreg Kroah-Hartman }; 678ab4382d2SGreg Kroah-Hartman 679aa25afadSRussell King static int pl010_probe(struct amba_device *dev, const struct amba_id *id) 680ab4382d2SGreg Kroah-Hartman { 681ab4382d2SGreg Kroah-Hartman struct uart_amba_port *uap; 682ab4382d2SGreg Kroah-Hartman void __iomem *base; 683ab4382d2SGreg Kroah-Hartman int i, ret; 684ab4382d2SGreg Kroah-Hartman 685ab4382d2SGreg Kroah-Hartman for (i = 0; i < ARRAY_SIZE(amba_ports); i++) 686ab4382d2SGreg Kroah-Hartman if (amba_ports[i] == NULL) 687ab4382d2SGreg Kroah-Hartman break; 688ab4382d2SGreg Kroah-Hartman 689ab4382d2SGreg Kroah-Hartman if (i == ARRAY_SIZE(amba_ports)) { 690ab4382d2SGreg Kroah-Hartman ret = -EBUSY; 691ab4382d2SGreg Kroah-Hartman goto out; 692ab4382d2SGreg Kroah-Hartman } 693ab4382d2SGreg Kroah-Hartman 694ab4382d2SGreg Kroah-Hartman uap = kzalloc(sizeof(struct uart_amba_port), GFP_KERNEL); 695ab4382d2SGreg Kroah-Hartman if (!uap) { 696ab4382d2SGreg Kroah-Hartman ret = -ENOMEM; 697ab4382d2SGreg Kroah-Hartman goto out; 698ab4382d2SGreg Kroah-Hartman } 699ab4382d2SGreg Kroah-Hartman 700ab4382d2SGreg Kroah-Hartman base = ioremap(dev->res.start, resource_size(&dev->res)); 701ab4382d2SGreg Kroah-Hartman if (!base) { 702ab4382d2SGreg Kroah-Hartman ret = -ENOMEM; 703ab4382d2SGreg Kroah-Hartman goto free; 704ab4382d2SGreg Kroah-Hartman } 705ab4382d2SGreg Kroah-Hartman 706ab4382d2SGreg Kroah-Hartman uap->clk = clk_get(&dev->dev, NULL); 707ab4382d2SGreg Kroah-Hartman if (IS_ERR(uap->clk)) { 708ab4382d2SGreg Kroah-Hartman ret = PTR_ERR(uap->clk); 709ab4382d2SGreg Kroah-Hartman goto unmap; 710ab4382d2SGreg Kroah-Hartman } 711ab4382d2SGreg Kroah-Hartman 712ab4382d2SGreg Kroah-Hartman uap->port.dev = &dev->dev; 713ab4382d2SGreg Kroah-Hartman uap->port.mapbase = dev->res.start; 714ab4382d2SGreg Kroah-Hartman uap->port.membase = base; 715ab4382d2SGreg Kroah-Hartman uap->port.iotype = UPIO_MEM; 716ab4382d2SGreg Kroah-Hartman uap->port.irq = dev->irq[0]; 717ab4382d2SGreg Kroah-Hartman uap->port.fifosize = 16; 718ab4382d2SGreg Kroah-Hartman uap->port.ops = &amba_pl010_pops; 719ab4382d2SGreg Kroah-Hartman uap->port.flags = UPF_BOOT_AUTOCONF; 720ab4382d2SGreg Kroah-Hartman uap->port.line = i; 721ab4382d2SGreg Kroah-Hartman uap->dev = dev; 722ab4382d2SGreg Kroah-Hartman uap->data = dev->dev.platform_data; 723ab4382d2SGreg Kroah-Hartman 724ab4382d2SGreg Kroah-Hartman amba_ports[i] = uap; 725ab4382d2SGreg Kroah-Hartman 726ab4382d2SGreg Kroah-Hartman amba_set_drvdata(dev, uap); 727ab4382d2SGreg Kroah-Hartman ret = uart_add_one_port(&amba_reg, &uap->port); 728ab4382d2SGreg Kroah-Hartman if (ret) { 729ab4382d2SGreg Kroah-Hartman amba_set_drvdata(dev, NULL); 730ab4382d2SGreg Kroah-Hartman amba_ports[i] = NULL; 731ab4382d2SGreg Kroah-Hartman clk_put(uap->clk); 732ab4382d2SGreg Kroah-Hartman unmap: 733ab4382d2SGreg Kroah-Hartman iounmap(base); 734ab4382d2SGreg Kroah-Hartman free: 735ab4382d2SGreg Kroah-Hartman kfree(uap); 736ab4382d2SGreg Kroah-Hartman } 737ab4382d2SGreg Kroah-Hartman out: 738ab4382d2SGreg Kroah-Hartman return ret; 739ab4382d2SGreg Kroah-Hartman } 740ab4382d2SGreg Kroah-Hartman 741ab4382d2SGreg Kroah-Hartman static int pl010_remove(struct amba_device *dev) 742ab4382d2SGreg Kroah-Hartman { 743ab4382d2SGreg Kroah-Hartman struct uart_amba_port *uap = amba_get_drvdata(dev); 744ab4382d2SGreg Kroah-Hartman int i; 745ab4382d2SGreg Kroah-Hartman 746ab4382d2SGreg Kroah-Hartman amba_set_drvdata(dev, NULL); 747ab4382d2SGreg Kroah-Hartman 748ab4382d2SGreg Kroah-Hartman uart_remove_one_port(&amba_reg, &uap->port); 749ab4382d2SGreg Kroah-Hartman 750ab4382d2SGreg Kroah-Hartman for (i = 0; i < ARRAY_SIZE(amba_ports); i++) 751ab4382d2SGreg Kroah-Hartman if (amba_ports[i] == uap) 752ab4382d2SGreg Kroah-Hartman amba_ports[i] = NULL; 753ab4382d2SGreg Kroah-Hartman 754ab4382d2SGreg Kroah-Hartman iounmap(uap->port.membase); 755ab4382d2SGreg Kroah-Hartman clk_put(uap->clk); 756ab4382d2SGreg Kroah-Hartman kfree(uap); 757ab4382d2SGreg Kroah-Hartman return 0; 758ab4382d2SGreg Kroah-Hartman } 759ab4382d2SGreg Kroah-Hartman 760ab4382d2SGreg Kroah-Hartman static int pl010_suspend(struct amba_device *dev, pm_message_t state) 761ab4382d2SGreg Kroah-Hartman { 762ab4382d2SGreg Kroah-Hartman struct uart_amba_port *uap = amba_get_drvdata(dev); 763ab4382d2SGreg Kroah-Hartman 764ab4382d2SGreg Kroah-Hartman if (uap) 765ab4382d2SGreg Kroah-Hartman uart_suspend_port(&amba_reg, &uap->port); 766ab4382d2SGreg Kroah-Hartman 767ab4382d2SGreg Kroah-Hartman return 0; 768ab4382d2SGreg Kroah-Hartman } 769ab4382d2SGreg Kroah-Hartman 770ab4382d2SGreg Kroah-Hartman static int pl010_resume(struct amba_device *dev) 771ab4382d2SGreg Kroah-Hartman { 772ab4382d2SGreg Kroah-Hartman struct uart_amba_port *uap = amba_get_drvdata(dev); 773ab4382d2SGreg Kroah-Hartman 774ab4382d2SGreg Kroah-Hartman if (uap) 775ab4382d2SGreg Kroah-Hartman uart_resume_port(&amba_reg, &uap->port); 776ab4382d2SGreg Kroah-Hartman 777ab4382d2SGreg Kroah-Hartman return 0; 778ab4382d2SGreg Kroah-Hartman } 779ab4382d2SGreg Kroah-Hartman 780ab4382d2SGreg Kroah-Hartman static struct amba_id pl010_ids[] = { 781ab4382d2SGreg Kroah-Hartman { 782ab4382d2SGreg Kroah-Hartman .id = 0x00041010, 783ab4382d2SGreg Kroah-Hartman .mask = 0x000fffff, 784ab4382d2SGreg Kroah-Hartman }, 785ab4382d2SGreg Kroah-Hartman { 0, 0 }, 786ab4382d2SGreg Kroah-Hartman }; 787ab4382d2SGreg Kroah-Hartman 788ab4382d2SGreg Kroah-Hartman static struct amba_driver pl010_driver = { 789ab4382d2SGreg Kroah-Hartman .drv = { 790ab4382d2SGreg Kroah-Hartman .name = "uart-pl010", 791ab4382d2SGreg Kroah-Hartman }, 792ab4382d2SGreg Kroah-Hartman .id_table = pl010_ids, 793ab4382d2SGreg Kroah-Hartman .probe = pl010_probe, 794ab4382d2SGreg Kroah-Hartman .remove = pl010_remove, 795ab4382d2SGreg Kroah-Hartman .suspend = pl010_suspend, 796ab4382d2SGreg Kroah-Hartman .resume = pl010_resume, 797ab4382d2SGreg Kroah-Hartman }; 798ab4382d2SGreg Kroah-Hartman 799ab4382d2SGreg Kroah-Hartman static int __init pl010_init(void) 800ab4382d2SGreg Kroah-Hartman { 801ab4382d2SGreg Kroah-Hartman int ret; 802ab4382d2SGreg Kroah-Hartman 803ab4382d2SGreg Kroah-Hartman printk(KERN_INFO "Serial: AMBA driver\n"); 804ab4382d2SGreg Kroah-Hartman 805ab4382d2SGreg Kroah-Hartman ret = uart_register_driver(&amba_reg); 806ab4382d2SGreg Kroah-Hartman if (ret == 0) { 807ab4382d2SGreg Kroah-Hartman ret = amba_driver_register(&pl010_driver); 808ab4382d2SGreg Kroah-Hartman if (ret) 809ab4382d2SGreg Kroah-Hartman uart_unregister_driver(&amba_reg); 810ab4382d2SGreg Kroah-Hartman } 811ab4382d2SGreg Kroah-Hartman return ret; 812ab4382d2SGreg Kroah-Hartman } 813ab4382d2SGreg Kroah-Hartman 814ab4382d2SGreg Kroah-Hartman static void __exit pl010_exit(void) 815ab4382d2SGreg Kroah-Hartman { 816ab4382d2SGreg Kroah-Hartman amba_driver_unregister(&pl010_driver); 817ab4382d2SGreg Kroah-Hartman uart_unregister_driver(&amba_reg); 818ab4382d2SGreg Kroah-Hartman } 819ab4382d2SGreg Kroah-Hartman 820ab4382d2SGreg Kroah-Hartman module_init(pl010_init); 821ab4382d2SGreg Kroah-Hartman module_exit(pl010_exit); 822ab4382d2SGreg Kroah-Hartman 823ab4382d2SGreg Kroah-Hartman MODULE_AUTHOR("ARM Ltd/Deep Blue Solutions Ltd"); 824ab4382d2SGreg Kroah-Hartman MODULE_DESCRIPTION("ARM AMBA serial port driver"); 825ab4382d2SGreg Kroah-Hartman MODULE_LICENSE("GPL"); 826