/*
- * File: drivers/serial/bfin_5xx.c
- * Based on: Based on drivers/serial/sa1100.c
- * Author: Aubrey Li <aubrey.li@analog.com>
+ * Blackfin On-Chip Serial Driver
*
- * Created:
- * Description: Driver for blackfin 5xx serial ports
+ * Copyright 2006-2007 Analog Devices Inc.
*
- * Modified:
- * Copyright 2006 Analog Devices Inc.
+ * Enter bugs at http://blackfin.uclinux.org/
*
- * Bugs: Enter bugs at http://blackfin.uclinux.org/
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, see the file COPYING, or write
- * to the Free Software Foundation, Inc.,
- * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ * Licensed under the GPL-2 or later.
*/
#if defined(CONFIG_SERIAL_BFIN_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
#define DMA_RX_XCOUNT 512
#define DMA_RX_YCOUNT (PAGE_SIZE / DMA_RX_XCOUNT)
-#define DMA_RX_FLUSH_JIFFIES 5
+#define DMA_RX_FLUSH_JIFFIES (HZ / 50)
#ifdef CONFIG_SERIAL_BFIN_DMA
static void bfin_serial_dma_tx_chars(struct bfin_serial_port *uart);
#else
-static void bfin_serial_do_work(struct work_struct *work);
static void bfin_serial_tx_chars(struct bfin_serial_port *uart);
#endif
static void bfin_serial_stop_tx(struct uart_port *port)
{
struct bfin_serial_port *uart = (struct bfin_serial_port *)port;
-#if !defined(CONFIG_BF54x) && !defined(CONFIG_SERIAL_BFIN_DMA)
- unsigned short ier;
-#endif
+ struct circ_buf *xmit = &uart->port.info->xmit;
while (!(UART_GET_LSR(uart) & TEMT))
- continue;
+ cpu_relax();
#ifdef CONFIG_SERIAL_BFIN_DMA
disable_dma(uart->tx_dma_channel);
+ xmit->tail = (xmit->tail + uart->tx_count) & (UART_XMIT_SIZE - 1);
+ uart->port.icount.tx += uart->tx_count;
+ uart->tx_count = 0;
+ uart->tx_done = 1;
#else
#ifdef CONFIG_BF54x
/* Clear TFI bit */
UART_PUT_LSR(uart, TFI);
- UART_CLEAR_IER(uart, ETBEI);
-#else
- ier = UART_GET_IER(uart);
- ier &= ~ETBEI;
- UART_PUT_IER(uart, ier);
#endif
+ UART_CLEAR_IER(uart, ETBEI);
#endif
}
struct bfin_serial_port *uart = (struct bfin_serial_port *)port;
#ifdef CONFIG_SERIAL_BFIN_DMA
- bfin_serial_dma_tx_chars(uart);
+ if (uart->tx_done)
+ bfin_serial_dma_tx_chars(uart);
#else
-#ifdef CONFIG_BF54x
UART_SET_IER(uart, ETBEI);
-#else
- unsigned short ier;
- ier = UART_GET_IER(uart);
- ier |= ETBEI;
- UART_PUT_IER(uart, ier);
-#endif
bfin_serial_tx_chars(uart);
#endif
}
static void bfin_serial_stop_rx(struct uart_port *port)
{
struct bfin_serial_port *uart = (struct bfin_serial_port *)port;
-#ifdef CONFIG_KGDB_UART
- if (uart->port.line != CONFIG_KGDB_UART_PORT) {
+#ifdef CONFIG_KGDB_UART
+ if (uart->port.line != CONFIG_KGDB_UART_PORT)
#endif
-#ifdef CONFIG_BF54x
UART_CLEAR_IER(uart, ERBFI);
-#else
- unsigned short ier;
-
- ier = UART_GET_IER(uart);
- ier &= ~ERBFI;
- UART_PUT_IER(uart, ier);
-#endif
-#ifdef CONFIG_KGDB_UART
- }
-#endif
}
/*
{
struct bfin_serial_port *uart;
- if (CONFIG_KGDB_UART_PORT<0 || CONFIG_KGDB_UART_PORT>=NR_PORTS)
+ if (CONFIG_KGDB_UART_PORT < 0
+ || CONFIG_KGDB_UART_PORT >= BFIN_UART_NR_PORTS)
uart = &bfin_serial_ports[0];
else
uart = &bfin_serial_ports[CONFIG_KGDB_UART_PORT];
SSYNC();
}
-#ifndef CONFIG_BF54x
- UART_PUT_LCR(uart, UART_GET_LCR(uart)&(~DLAB));
- SSYNC();
-#endif
+ UART_CLEAR_DLAB(uart);
UART_PUT_CHAR(uart, (unsigned char)chr);
SSYNC();
}
struct bfin_serial_port *uart;
unsigned char chr;
- if (CONFIG_KGDB_UART_PORT<0 || CONFIG_KGDB_UART_PORT>=NR_PORTS)
+ if (CONFIG_KGDB_UART_PORT < 0
+ || CONFIG_KGDB_UART_PORT >= BFIN_UART_NR_PORTS)
uart = &bfin_serial_ports[0];
else
uart = &bfin_serial_ports[CONFIG_KGDB_UART_PORT];
while(!(UART_GET_LSR(uart) & DR)) {
SSYNC();
}
-#ifndef CONFIG_BF54x
- UART_PUT_LCR(uart, UART_GET_LCR(uart)&(~DLAB));
- SSYNC();
-#endif
+ UART_CLEAR_DLAB(uart);
chr = UART_GET_CHAR(uart);
SSYNC();
}
#endif
-#if ANOMALY_05000230 && defined(CONFIG_SERIAL_BFIN_PIO)
+#if ANOMALY_05000363 && defined(CONFIG_SERIAL_BFIN_PIO)
# define UART_GET_ANOMALY_THRESHOLD(uart) ((uart)->anomaly_threshold)
# define UART_SET_ANOMALY_THRESHOLD(uart, v) ((uart)->anomaly_threshold = (v))
#else
struct tty_struct *tty = uart->port.info->tty;
unsigned int status, ch, flg;
static struct timeval anomaly_start = { .tv_sec = 0 };
-#ifdef CONFIG_KGDB_UART
- struct pt_regs *regs = get_irq_regs();
-#endif
status = UART_GET_LSR(uart);
UART_CLEAR_LSR(uart);
#ifdef CONFIG_KGDB_UART
if (uart->port.line == CONFIG_KGDB_UART_PORT) {
+ struct pt_regs *regs = get_irq_regs();
if (uart->port.cons->index == CONFIG_KGDB_UART_PORT && ch == 0x1) { /* Ctrl + A */
kgdb_breakkey_pressed(regs);
return;
}
#endif
- if (ANOMALY_05000230) {
+ if (ANOMALY_05000363) {
/* The BF533 (and BF561) family of processors have a nice anomaly
* where they continuously generate characters for a "single" break.
* We have to basically ignore this flood until the "next" valid
* timeout was picked as it must absolutely be larger than 1
* character time +/- some percent. So 1.5 sounds good. All other
* Blackfin families operate properly. Woo.
- * Note: While Anomaly 05000230 does not directly address this,
- * the changes that went in for it also fixed this issue.
- * That anomaly was fixed in 0.5+ silicon. I like bunnies.
*/
if (anomaly_start.tv_sec) {
struct timeval curr;
}
if (status & BI) {
- if (ANOMALY_05000230)
+ if (ANOMALY_05000363)
if (bfin_revid() < 5)
do_gettimeofday(&anomaly_start);
uart->port.icount.brk++;
return IRQ_HANDLED;
}
+#endif
-
+#ifdef CONFIG_SERIAL_BFIN_CTSRTS
static void bfin_serial_do_work(struct work_struct *work)
{
struct bfin_serial_port *uart = container_of(work, struct bfin_serial_port, cts_workqueue);
static void bfin_serial_dma_tx_chars(struct bfin_serial_port *uart)
{
struct circ_buf *xmit = &uart->port.info->xmit;
- unsigned short ier;
- int flags = 0;
- if (!uart->tx_done)
- return;
uart->tx_done = 0;
if (uart_circ_empty(xmit) || uart_tx_stopped(&uart->port)) {
- bfin_serial_stop_tx(&uart->port);
+ uart->tx_count = 0;
uart->tx_done = 1;
return;
}
*/
bfin_serial_mctrl_check(uart);
- spin_lock_irqsave(&uart->port.lock, flags);
uart->tx_count = CIRC_CNT(xmit->head, xmit->tail, UART_XMIT_SIZE);
if (uart->tx_count > (UART_XMIT_SIZE - xmit->tail))
uart->tx_count = UART_XMIT_SIZE - xmit->tail;
set_dma_x_modify(uart->tx_dma_channel, 1);
enable_dma(uart->tx_dma_channel);
- xmit->tail = (xmit->tail + uart->tx_count) & (UART_XMIT_SIZE - 1);
- uart->port.icount.tx += uart->tx_count;
-
-#ifdef CONFIG_BF54x
UART_SET_IER(uart, ETBEI);
-#else
- ier = UART_GET_IER(uart);
- ier |= ETBEI;
- UART_PUT_IER(uart, ier);
-#endif
- spin_unlock_irqrestore(&uart->port.lock, flags);
}
static void bfin_serial_dma_rx_chars(struct bfin_serial_port *uart)
status = UART_GET_LSR(uart);
UART_CLEAR_LSR(uart);
- uart->port.icount.rx += CIRC_CNT(uart->rx_dma_buf.head, uart->rx_dma_buf.tail, UART_XMIT_SIZE);;
+ uart->port.icount.rx +=
+ CIRC_CNT(uart->rx_dma_buf.head, uart->rx_dma_buf.tail,
+ UART_XMIT_SIZE);
if (status & BI) {
uart->port.icount.brk++;
else
flg = TTY_NORMAL;
- for (i = uart->rx_dma_buf.head; i < uart->rx_dma_buf.tail; i++) {
- if (uart_handle_sysrq_char(&uart->port, uart->rx_dma_buf.buf[i]))
- goto dma_ignore_char;
- uart_insert_char(&uart->port, status, OE, uart->rx_dma_buf.buf[i], flg);
+ for (i = uart->rx_dma_buf.tail; i != uart->rx_dma_buf.head; i++) {
+ if (i >= UART_XMIT_SIZE)
+ i = 0;
+ if (!uart_handle_sysrq_char(&uart->port, uart->rx_dma_buf.buf[i]))
+ uart_insert_char(&uart->port, status, OE,
+ uart->rx_dma_buf.buf[i], flg);
}
dma_ignore_char:
void bfin_serial_rx_dma_timeout(struct bfin_serial_port *uart)
{
int x_pos, pos;
- int flags = 0;
- spin_lock_irqsave(&uart->port.lock, flags);
- x_pos = DMA_RX_XCOUNT - get_dma_curr_xcount(uart->rx_dma_channel);
+ uart->rx_dma_nrows = get_dma_curr_ycount(uart->rx_dma_channel);
+ x_pos = get_dma_curr_xcount(uart->rx_dma_channel);
+ uart->rx_dma_nrows = DMA_RX_YCOUNT - uart->rx_dma_nrows;
+ if (uart->rx_dma_nrows == DMA_RX_YCOUNT)
+ uart->rx_dma_nrows = 0;
+ x_pos = DMA_RX_XCOUNT - x_pos;
if (x_pos == DMA_RX_XCOUNT)
x_pos = 0;
pos = uart->rx_dma_nrows * DMA_RX_XCOUNT + x_pos;
-
- if (pos>uart->rx_dma_buf.tail) {
- uart->rx_dma_buf.tail = pos;
+ if (pos != uart->rx_dma_buf.tail) {
+ uart->rx_dma_buf.head = pos;
bfin_serial_dma_rx_chars(uart);
- uart->rx_dma_buf.head = uart->rx_dma_buf.tail;
+ uart->rx_dma_buf.tail = uart->rx_dma_buf.head;
}
- spin_unlock_irqrestore(&uart->port.lock, flags);
- uart->rx_dma_timer.expires = jiffies + DMA_RX_FLUSH_JIFFIES;
- add_timer(&(uart->rx_dma_timer));
+
+ mod_timer(&(uart->rx_dma_timer), jiffies + DMA_RX_FLUSH_JIFFIES);
}
static irqreturn_t bfin_serial_dma_tx_int(int irq, void *dev_id)
{
struct bfin_serial_port *uart = dev_id;
struct circ_buf *xmit = &uart->port.info->xmit;
- unsigned short ier;
spin_lock(&uart->port.lock);
if (!(get_dma_curr_irqstat(uart->tx_dma_channel)&DMA_RUN)) {
- clear_dma_irqstat(uart->tx_dma_channel);
disable_dma(uart->tx_dma_channel);
-#ifdef CONFIG_BF54x
+ clear_dma_irqstat(uart->tx_dma_channel);
UART_CLEAR_IER(uart, ETBEI);
-#else
- ier = UART_GET_IER(uart);
- ier &= ~ETBEI;
- UART_PUT_IER(uart, ier);
-#endif
+ xmit->tail = (xmit->tail + uart->tx_count) & (UART_XMIT_SIZE - 1);
+ uart->port.icount.tx += uart->tx_count;
+
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
uart_write_wakeup(&uart->port);
- uart->tx_done = 1;
-
bfin_serial_dma_tx_chars(uart);
}
struct bfin_serial_port *uart = dev_id;
unsigned short irqstat;
- uart->rx_dma_nrows++;
- if (uart->rx_dma_nrows == DMA_RX_YCOUNT) {
- uart->rx_dma_nrows = 0;
- uart->rx_dma_buf.tail = DMA_RX_XCOUNT*DMA_RX_YCOUNT;
- bfin_serial_dma_rx_chars(uart);
- uart->rx_dma_buf.head = uart->rx_dma_buf.tail = 0;
- }
spin_lock(&uart->port.lock);
irqstat = get_dma_curr_irqstat(uart->rx_dma_channel);
clear_dma_irqstat(uart->rx_dma_channel);
-
spin_unlock(&uart->port.lock);
+
+ mod_timer(&(uart->rx_dma_timer), jiffies);
+
return IRQ_HANDLED;
}
#endif
if (uart->cts_pin < 0)
return TIOCM_CTS | TIOCM_DSR | TIOCM_CAR;
- if (gpio_get_value(uart->cts_pin))
+ if (UART_GET_CTS(uart))
return TIOCM_DSR | TIOCM_CAR;
else
#endif
return;
if (mctrl & TIOCM_RTS)
- gpio_set_value(uart->rts_pin, 0);
+ UART_CLEAR_RTS(uart);
else
- gpio_set_value(uart->rts_pin, 1);
+ UART_SET_RTS(uart);
#endif
}
{
#ifdef CONFIG_SERIAL_BFIN_CTSRTS
unsigned int status;
-# ifdef CONFIG_SERIAL_BFIN_DMA
struct uart_info *info = uart->port.info;
struct tty_struct *tty = info->tty;
status = bfin_serial_get_mctrl(&uart->port);
+ uart_handle_cts_change(&uart->port, status & TIOCM_CTS);
if (!(status & TIOCM_CTS)) {
tty->hw_stopped = 1;
+ schedule_work(&uart->cts_workqueue);
} else {
tty->hw_stopped = 0;
}
-# else
- status = bfin_serial_get_mctrl(&uart->port);
- uart_handle_cts_change(&uart->port, status & TIOCM_CTS);
- if (!(status & TIOCM_CTS))
- schedule_work(&uart->cts_workqueue);
-# endif
#endif
}
# endif
}
-
if (request_irq
(uart->port.irq+1, bfin_serial_tx_int, IRQF_DISABLED,
"BFIN_UART_TX", uart)) {
return -EBUSY;
}
#endif
-#ifdef CONFIG_BF54x
UART_SET_IER(uart, ERBFI);
-#else
- UART_PUT_IER(uart, UART_GET_IER(uart) | ERBFI);
-#endif
return 0;
}
struct bfin_serial_port *uart = (struct bfin_serial_port *)port;
unsigned long flags;
unsigned int baud, quot;
- unsigned short val, ier, lsr, lcr = 0;
+ unsigned short val, ier, lcr = 0;
switch (termios->c_cflag & CSIZE) {
case CS8:
break;
default:
printk(KERN_ERR "%s: word lengh not supported\n",
- __FUNCTION__);
+ __func__);
}
if (termios->c_cflag & CSTOPB)
UART_SET_ANOMALY_THRESHOLD(uart, USEC_PER_SEC / baud * 15);
- do {
- lsr = UART_GET_LSR(uart);
- } while (!(lsr & TEMT));
-
/* Disable UART */
ier = UART_GET_IER(uart);
-#ifdef CONFIG_BF54x
- UART_CLEAR_IER(uart, 0xF);
-#else
- UART_PUT_IER(uart, 0);
-#endif
+ UART_DISABLE_INTS(uart);
-#ifndef CONFIG_BF54x
/* Set DLAB in LCR to Access DLL and DLH */
- val = UART_GET_LCR(uart);
- val |= DLAB;
- UART_PUT_LCR(uart, val);
- SSYNC();
-#endif
+ UART_SET_DLAB(uart);
UART_PUT_DLL(uart, quot & 0xFF);
- SSYNC();
UART_PUT_DLH(uart, (quot >> 8) & 0xFF);
SSYNC();
-#ifndef CONFIG_BF54x
/* Clear DLAB in LCR to Access THR RBR IER */
- val = UART_GET_LCR(uart);
- val &= ~DLAB;
- UART_PUT_LCR(uart, val);
- SSYNC();
-#endif
+ UART_CLEAR_DLAB(uart);
UART_PUT_LCR(uart, lcr);
/* Enable UART */
-#ifdef CONFIG_BF54x
- UART_SET_IER(uart, ier);
-#else
- UART_PUT_IER(uart, ier);
-#endif
+ UART_ENABLE_INTS(uart, ier);
val = UART_GET_GCTL(uart);
val |= UCEN;
return 0;
}
+/*
+ * Enable the IrDA function if tty->ldisc.num is N_IRDA.
+ * In other cases, disable IrDA function.
+ */
+static void bfin_serial_set_ldisc(struct uart_port *port)
+{
+ int line = port->line;
+ unsigned short val;
+
+ if (line >= port->info->tty->driver->num)
+ return;
+
+ switch (port->info->tty->ldisc.num) {
+ case N_IRDA:
+ val = UART_GET_GCTL(&bfin_serial_ports[line]);
+ val |= (IREN | RPOLC);
+ UART_PUT_GCTL(&bfin_serial_ports[line], val);
+ break;
+ default:
+ val = UART_GET_GCTL(&bfin_serial_ports[line]);
+ val &= ~(IREN | RPOLC);
+ UART_PUT_GCTL(&bfin_serial_ports[line], val);
+ }
+}
+
static struct uart_ops bfin_serial_pops = {
.tx_empty = bfin_serial_tx_empty,
.set_mctrl = bfin_serial_set_mctrl,
.startup = bfin_serial_startup,
.shutdown = bfin_serial_shutdown,
.set_termios = bfin_serial_set_termios,
+ .set_ldisc = bfin_serial_set_ldisc,
.type = bfin_serial_type,
.release_port = bfin_serial_release_port,
.request_port = bfin_serial_request_port,
bfin_serial_ports[i].rx_dma_channel =
bfin_serial_resource[i].uart_rx_dma_channel;
init_timer(&(bfin_serial_ports[i].rx_dma_timer));
-#else
- INIT_WORK(&bfin_serial_ports[i].cts_workqueue, bfin_serial_do_work);
#endif
#ifdef CONFIG_SERIAL_BFIN_CTSRTS
+ INIT_WORK(&bfin_serial_ports[i].cts_workqueue, bfin_serial_do_work);
bfin_serial_ports[i].cts_pin =
bfin_serial_resource[i].uart_cts_pin;
bfin_serial_ports[i].rts_pin =
status = UART_GET_IER(uart) & (ERBFI | ETBEI);
if (status == (ERBFI | ETBEI)) {
/* ok, the port was enabled */
- unsigned short lcr, val;
- unsigned short dlh, dll;
+ u16 lcr, dlh, dll;
lcr = UART_GET_LCR(uart);
case 2: *bits = 7; break;
case 3: *bits = 8; break;
}
-#ifndef CONFIG_BF54x
/* Set DLAB in LCR to Access DLL and DLH */
- val = UART_GET_LCR(uart);
- val |= DLAB;
- UART_PUT_LCR(uart, val);
-#endif
+ UART_SET_DLAB(uart);
dll = UART_GET_DLL(uart);
dlh = UART_GET_DLH(uart);
-#ifndef CONFIG_BF54x
/* Clear DLAB in LCR to Access THR RBR IER */
- val = UART_GET_LCR(uart);
- val &= ~DLAB;
- UART_PUT_LCR(uart, val);
-#endif
+ UART_CLEAR_DLAB(uart);
*baud = get_sclk() / (16*(dll | dlh << 8));
}
- pr_debug("%s:baud = %d, parity = %c, bits= %d\n", __FUNCTION__, *baud, *parity, *bits);
+ pr_debug("%s:baud = %d, parity = %c, bits= %d\n", __func__, *baud, *parity, *bits);
}
#endif
.dev_name = BFIN_SERIAL_NAME,
.major = BFIN_SERIAL_MAJOR,
.minor = BFIN_SERIAL_MINOR,
- .nr = NR_PORTS,
+ .nr = BFIN_UART_NR_PORTS,
.cons = BFIN_SERIAL_CONSOLE,
};
.resume = bfin_serial_resume,
.driver = {
.name = "bfin-uart",
+ .owner = THIS_MODULE,
},
};
request_irq(uart->port.irq, bfin_serial_rx_int,
IRQF_DISABLED, "BFIN_UART_RX", uart);
pr_info("Request irq for kgdb uart port\n");
-#ifdef CONFIG_BF54x
UART_SET_IER(uart, ERBFI);
-#else
- UART_PUT_IER(uart, UART_GET_IER(uart) | ERBFI);
-#endif
SSYNC();
t.c_cflag = CS8|B57600;
t.c_iflag = 0;
MODULE_DESCRIPTION("Blackfin generic serial port driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS_CHARDEV_MAJOR(BFIN_SERIAL_MAJOR);
+MODULE_ALIAS("platform:bfin-uart");