📄 bfin_5xx.c
字号:
/* * File: drivers/serial/bfin_5xx.c * Based on: Based on drivers/serial/sa1100.c * Author: Aubrey Li <aubrey.li@analog.com> * * Created: * Description: Driver for blackfin 5xx serial ports * * Modified: * Copyright 2006 Analog Devices Inc. * * 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 */#if defined(CONFIG_SERIAL_BFIN_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)#define SUPPORT_SYSRQ#endif#include <linux/module.h>#include <linux/ioport.h>#include <linux/init.h>#include <linux/console.h>#include <linux/sysrq.h>#include <linux/platform_device.h>#include <linux/tty.h>#include <linux/tty_flip.h>#include <linux/serial_core.h>#ifdef CONFIG_KGDB_UART#include <linux/kgdb.h>#include <asm/irq_regs.h>#endif#include <asm/gpio.h>#include <asm/mach/bfin_serial_5xx.h>#ifdef CONFIG_SERIAL_BFIN_DMA#include <linux/dma-mapping.h>#include <asm/io.h>#include <asm/irq.h>#include <asm/cacheflush.h>#endif/* UART name and device definitions */#define BFIN_SERIAL_NAME "ttyBF"#define BFIN_SERIAL_MAJOR 204#define BFIN_SERIAL_MINOR 64/* * Setup for console. Argument comes from the menuconfig */#define DMA_RX_XCOUNT 512#define DMA_RX_YCOUNT (PAGE_SIZE / DMA_RX_XCOUNT)#define DMA_RX_FLUSH_JIFFIES 5#ifdef CONFIG_SERIAL_BFIN_DMAstatic void bfin_serial_dma_tx_chars(struct bfin_serial_port *uart);#elsestatic void bfin_serial_do_work(struct work_struct *work);static void bfin_serial_tx_chars(struct bfin_serial_port *uart);static void local_put_char(struct bfin_serial_port *uart, char ch);#endifstatic void bfin_serial_mctrl_check(struct bfin_serial_port *uart);/* * interrupts are disabled on entry */static void bfin_serial_stop_tx(struct uart_port *port){ struct bfin_serial_port *uart = (struct bfin_serial_port *)port; while (!(UART_GET_LSR(uart) & TEMT)) continue;#ifdef CONFIG_SERIAL_BFIN_DMA disable_dma(uart->tx_dma_channel);#else#ifdef CONFIG_BF54x /* Waiting for Transmission Finished */ while (!(UART_GET_LSR(uart) & TFI)) continue; /* Clear TFI bit */ UART_PUT_LSR(uart, TFI); UART_CLEAR_IER(uart, ETBEI);#else unsigned short ier; ier = UART_GET_IER(uart); ier &= ~ETBEI; UART_PUT_IER(uart, ier);#endif#endif}/* * port is locked and interrupts are disabled */static void bfin_serial_start_tx(struct uart_port *port){ struct bfin_serial_port *uart = (struct bfin_serial_port *)port;#ifdef CONFIG_SERIAL_BFIN_DMA 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}/* * Interrupts are enabled */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) {#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}/* * Set the modem control timer to fire immediately. */static void bfin_serial_enable_ms(struct uart_port *port){}#ifdef CONFIG_KGDB_UARTstatic int kgdb_entry_state;void kgdb_put_debug_char(int chr){ struct bfin_serial_port *uart; if (CONFIG_KGDB_UART_PORT<0 || CONFIG_KGDB_UART_PORT>=NR_PORTS) uart = &bfin_serial_ports[0]; else uart = &bfin_serial_ports[CONFIG_KGDB_UART_PORT]; while (!(UART_GET_LSR(uart) & THRE)) { SSYNC(); }#ifndef CONFIG_BF54x UART_PUT_LCR(uart, UART_GET_LCR(uart)&(~DLAB)); SSYNC();#endif UART_PUT_CHAR(uart, (unsigned char)chr); SSYNC();}int kgdb_get_debug_char(void){ struct bfin_serial_port *uart; unsigned char chr; if (CONFIG_KGDB_UART_PORT<0 || CONFIG_KGDB_UART_PORT>=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 chr = UART_GET_CHAR(uart); SSYNC(); return chr;}#endif#ifdef CONFIG_SERIAL_BFIN_PIOstatic void local_put_char(struct bfin_serial_port *uart, char ch){ unsigned short status; int flags = 0; spin_lock_irqsave(&uart->port.lock, flags); do { status = UART_GET_LSR(uart); } while (!(status & THRE)); UART_PUT_CHAR(uart, ch); SSYNC(); spin_unlock_irqrestore(&uart->port.lock, flags);}static void bfin_serial_rx_chars(struct bfin_serial_port *uart){ struct tty_struct *tty = uart->port.info->tty; unsigned int status, ch, flg; static int in_break = 0;#ifdef CONFIG_KGDB_UART struct pt_regs *regs = get_irq_regs();#endif status = UART_GET_LSR(uart); ch = UART_GET_CHAR(uart); uart->port.icount.rx++;#ifdef CONFIG_KGDB_UART if (uart->port.line == CONFIG_KGDB_UART_PORT) { if (uart->port.cons->index == CONFIG_KGDB_UART_PORT && ch == 0x1) { /* Ctrl + A */ kgdb_breakkey_pressed(regs); return; } else if (kgdb_entry_state == 0 && ch == '$') {/* connection from KGDB */ kgdb_entry_state = 1; } else if (kgdb_entry_state == 1 && ch == 'q') { kgdb_entry_state = 0; kgdb_breakkey_pressed(regs); return; } else if (ch == 0x3) {/* Ctrl + C */ kgdb_entry_state = 0; kgdb_breakkey_pressed(regs); return; } else { kgdb_entry_state = 0; } }#endif if (ANOMALY_05000230) { /* The BF533 family of processors have a nice misbehavior where * they continuously generate characters for a "single" break. * We have to basically ignore this flood until the "next" valid * character comes across. All other Blackfin families operate * properly though. * Note: While Anomaly 05000230 does not directly address this, * the changes that went in for it also fixed this issue. */ if (in_break) { if (ch != 0) { in_break = 0; ch = UART_GET_CHAR(uart); if (bfin_revid() < 5) return; } else return; } } if (status & BI) { if (ANOMALY_05000230) in_break = 1; uart->port.icount.brk++; if (uart_handle_break(&uart->port)) goto ignore_char; status &= ~(PE | FE); } if (status & PE) uart->port.icount.parity++; if (status & OE) uart->port.icount.overrun++; if (status & FE) uart->port.icount.frame++; status &= uart->port.read_status_mask; if (status & BI) flg = TTY_BREAK; else if (status & PE) flg = TTY_PARITY; else if (status & FE) flg = TTY_FRAME; else flg = TTY_NORMAL; if (uart_handle_sysrq_char(&uart->port, ch)) goto ignore_char; uart_insert_char(&uart->port, status, OE, ch, flg); ignore_char: tty_flip_buffer_push(tty);}static void bfin_serial_tx_chars(struct bfin_serial_port *uart){ struct circ_buf *xmit = &uart->port.info->xmit; if (uart->port.x_char) { UART_PUT_CHAR(uart, uart->port.x_char); uart->port.icount.tx++; uart->port.x_char = 0; return; } /* * Check the modem control lines before * transmitting anything. */ bfin_serial_mctrl_check(uart); if (uart_circ_empty(xmit) || uart_tx_stopped(&uart->port)) { bfin_serial_stop_tx(&uart->port); return; } local_put_char(uart, xmit->buf[xmit->tail]); xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); uart->port.icount.tx++; if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) uart_write_wakeup(&uart->port); if (uart_circ_empty(xmit)) bfin_serial_stop_tx(&uart->port);}static irqreturn_t bfin_serial_rx_int(int irq, void *dev_id){ struct bfin_serial_port *uart = dev_id;#ifdef CONFIG_BF54x unsigned short status; spin_lock(&uart->port.lock); status = UART_GET_LSR(uart); while ((UART_GET_IER(uart) & ERBFI) && (status & DR)) { bfin_serial_rx_chars(uart); status = UART_GET_LSR(uart); } spin_unlock(&uart->port.lock);#else spin_lock(&uart->port.lock); while ((UART_GET_IIR(uart) & IIR_STATUS) == IIR_RX_READY) bfin_serial_rx_chars(uart); spin_unlock(&uart->port.lock);#endif return IRQ_HANDLED;}static irqreturn_t bfin_serial_tx_int(int irq, void *dev_id){ struct bfin_serial_port *uart = dev_id;#ifdef CONFIG_BF54x unsigned short status; spin_lock(&uart->port.lock); status = UART_GET_LSR(uart); while ((UART_GET_IER(uart) & ETBEI) && (status & THRE)) { bfin_serial_tx_chars(uart); status = UART_GET_LSR(uart); } spin_unlock(&uart->port.lock);#else spin_lock(&uart->port.lock); while ((UART_GET_IIR(uart) & IIR_STATUS) == IIR_TX_READY) bfin_serial_tx_chars(uart); spin_unlock(&uart->port.lock);#endif return IRQ_HANDLED;}static void bfin_serial_do_work(struct work_struct *work){ struct bfin_serial_port *uart = container_of(work, struct bfin_serial_port, cts_workqueue); bfin_serial_mctrl_check(uart);}#endif#ifdef CONFIG_SERIAL_BFIN_DMAstatic 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->port.x_char) { UART_PUT_CHAR(uart, uart->port.x_char); uart->port.icount.tx++; uart->port.x_char = 0; uart->tx_done = 1; return; } /* * Check the modem control lines before * transmitting anything. */ bfin_serial_mctrl_check(uart); if (uart_circ_empty(xmit) || uart_tx_stopped(&uart->port)) { bfin_serial_stop_tx(&uart->port); uart->tx_done = 1; return; } 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))
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -