2006-09-21 03:33:12

by Luke Yang

[permalink] [raw]
Subject: [PATCH 2/4] Blackfin: Serial driver for Blackfin arch on 2.6.18

Hi,

This is the serial driver for Blackfin. It is designed for the serial
core framework.

As to other drivers, I'll send them one by one later.

Signed-off-by: Luke Yang <[email protected]>

drivers/serial/Kconfig | 35 +
drivers/serial/Makefile | 3
drivers/serial/bfin_5xx.c | 903 ++++++++++++++++++++++++++++++++++++++++++++
include/linux/serial_core.h | 3
4 files changed, 943 insertions(+), 1 deletion(-)

diff -urN linux-2.6.18.patch1/drivers/serial/Kconfig
linux-2.6.18.patch2/drivers/serial/Kconfig
--- linux-2.6.18.patch1/drivers/serial/Kconfig 2006-09-21
09:14:42.000000000 +0800
+++ linux-2.6.18.patch2/drivers/serial/Kconfig 2006-09-21
09:38:17.000000000 +0800
@@ -488,6 +488,41 @@
your boot loader (lilo or loadlin) about how to pass options to the
kernel at boot time.)

+config SERIAL_BFIN
+ bool "Blackfin serial port support (EXPERIMENTAL)"
+ depends on BFIN && EXPERIMENTAL
+ select SERIAL_CORE
+
+config SERIAL_BFIN_CONSOLE
+ bool "Console on Blackfin serial port"
+ depends on SERIAL_BFIN
+ select SERIAL_CORE_CONSOLE
+
+choice
+ prompt "Blackfin UART Mode"
+ depends on SERIAL_BFIN
+ default SERIAL_BFIN_DMA
+ ---help---
+ This driver supports the built-in serial ports of the
Blackfin family of CPUs
+
+config SERIAL_BFIN_DMA
+ bool "Blackfin UART DMA mode"
+ depends on DMA_UNCACHED_1M
+ help
+ This driver works under DMA mode. If this option is
selected, the blackfin simple dma driver is also enabled.
+
+config SERIAL_BFIN_PIO
+ bool "Blackfin UART PIO mode"
+ help
+ This driver works under PIO mode.
+endchoice
+
+config SERIAL_BFIN_CTSRTS
+ bool "Enable hardware flow control"
+ depends on SERIAL_BFIN
+ help
+ Enable hardware flow control in the driver. Using GPIO emulate the
CTS/RTS signal.
+
config SERIAL_IMX
bool "IMX serial port support"
depends on ARM && ARCH_IMX
diff -urN linux-2.6.18.patch1/drivers/serial/Makefile
linux-2.6.18.patch2/drivers/serial/Makefile
--- linux-2.6.18.patch1/drivers/serial/Makefile 2006-09-21
09:14:42.000000000 +0800
+++ linux-2.6.18.patch2/drivers/serial/Makefile 2006-09-21
09:38:17.000000000 +0800
@@ -25,6 +25,7 @@
obj-$(CONFIG_SERIAL_CLPS711X) += clps711x.o
obj-$(CONFIG_SERIAL_PXA) += pxa.o
obj-$(CONFIG_SERIAL_SA1100) += sa1100.o
+obj-$(CONFIG_SERIAL_BFIN) += bfin_5xx.o
obj-$(CONFIG_SERIAL_S3C2410) += s3c2410.o
obj-$(CONFIG_SERIAL_SUNCORE) += suncore.o
obj-$(CONFIG_SERIAL_SUNHV) += sunhv.o
@@ -55,4 +56,4 @@
obj-$(CONFIG_SERIAL_SGI_IOC4) += ioc4_serial.o
obj-$(CONFIG_SERIAL_SGI_IOC3) += ioc3_serial.o
obj-$(CONFIG_SERIAL_AT91) += at91_serial.o
-obj-$(CONFIG_SERIAL_NETX) += netx-serial.o
+obj-$(CONFIG_SERIAL_NETX) += netx-serial.o
\ No newline at end of file
diff -urN linux-2.6.18.patch1/drivers/serial/bfin_5xx.c
linux-2.6.18.patch2/drivers/serial/bfin_5xx.c
--- linux-2.6.18.patch1/drivers/serial/bfin_5xx.c 1970-01-01
08:00:00.000000000 +0800
+++ linux-2.6.18.patch2/drivers/serial/bfin_5xx.c 2006-09-21
09:38:17.000000000 +0800
@@ -0,0 +1,903 @@
+/*
+ * File: drivers/serial/bfin_5xx.c
+ * Based on: Based on drivers/serial/sa1100.c
+ * Author: Aubrey Li <[email protected]>
+ *
+ * Created:
+ * Description: Driver for blackfin 5xx serial ports
+ *
+ * Rev: $Id: bfin_5xx.c,v 1.12 2006/09/04 04:44:27 aubrey Exp $
+ *
+ * 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
+ */
+
+#include <linux/config.h>
+
+#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>
+
+#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
+
+/* We've been assigned a range on the "Low-density serial ports" major */
+#define SERIAL_BFIN_MAJOR TTY_MAJOR
+#define MINOR_START 64
+
+#define DEBUG
+
+#ifdef DEBUG
+# define DPRINTK(x...) printk(KERN_DEBUG x)
+#else
+# define DPRINTK(x...) do { } while (0)
+#endif
+
+/*
+ * Setup for console. Argument comes from the menuconfig
+ */
+
+#if defined(CONFIG_BAUD_9600)
+#define CONSOLE_BAUD_RATE 9600
+#elif defined(CONFIG_BAUD_19200)
+#define CONSOLE_BAUD_RATE 19200
+#elif defined(CONFIG_BAUD_38400)
+#define CONSOLE_BAUD_RATE 38400
+#elif defined(CONFIG_BAUD_57600)
+#define CONSOLE_BAUD_RATE 57600
+#elif defined(CONFIG_BAUD_115200)
+#define CONSOLE_BAUD_RATE 115200
+#endif
+
+#define DMA_RX_XCOUNT TTY_FLIPBUF_SIZE
+#define DMA_RX_YCOUNT (PAGE_SIZE / DMA_RX_XCOUNT)
+
+#define DMA_RX_FLUSH_JIFFIES 5
+
+#ifdef CONFIG_SERIAL_BFIN_DMA
+wait_queue_head_t bfin_serial_tx_queue[NR_PORTS];
+static void bfin_serial_dma_tx_chars(struct bfin_serial_port *uart);
+#else
+static void bfin_serial_do_work(void *);
+static void bfin_serial_tx_chars(struct bfin_serial_port *uart);
+static void local_put_char(struct bfin_serial_port *uart, char ch);
+#endif
+
+static void bfin_serial_mctrl_check(struct bfin_serial_port *uart);
+
+/*
+ * interrupts disabled on entry
+ */
+static void bfin_serial_stop_tx(struct uart_port *port)
+{
+ struct bfin_serial_port *uart = (struct bfin_serial_port *)port;
+ unsigned short ier;
+ ier = UART_GET_IER(uart);
+ ier &= ~ETBEI;
+ UART_PUT_IER(uart, ier);
+#ifdef CONFIG_SERIAL_BFIN_DMA
+ disable_dma(uart->tx_dma_channel);
+#endif
+}
+
+/*
+ * port locked and interrupts 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
+ unsigned short ier;
+ ier = UART_GET_IER(uart);
+ ier |= ETBEI;
+ UART_PUT_IER(uart, ier);
+ bfin_serial_tx_chars(uart);
+#endif
+}
+
+/*
+ * Interrupts enabled
+ */
+static void bfin_serial_stop_rx(struct uart_port *port)
+{
+ struct bfin_serial_port *uart = (struct bfin_serial_port *)port;
+ unsigned short ier;
+ ier = UART_GET_IER(uart);
+ ier &= ERBFI;
+ UART_PUT_IER(uart, ier);
+}
+
+/*
+ * Set the modem control timer to fire immediately.
+ */
+static void bfin_serial_enable_ms(struct uart_port *port)
+{
+}
+
+#ifdef CONFIG_SERIAL_BFIN_PIO
+static void local_put_char(struct bfin_serial_port *uart, char ch)
+{
+ unsigned short status;
+ int flags = 0;
+
+ local_irq_save(flags);
+
+ do {
+ status = UART_GET_LSR(uart);
+ } while (!(status & THRE));
+
+ UART_PUT_CHAR(uart, ch);
+ local_irq_restore(flags);
+}
+
+static void
+bfin_serial_rx_chars(struct bfin_serial_port *uart, struct pt_regs *regs)
+{
+ struct tty_struct *tty = uart->port.info->tty;
+ unsigned int status=0, ch, flg;
+ ch = UART_GET_CHAR(uart);
+ uart->port.icount.rx++;
+ flg = TTY_NORMAL;
+ if (uart_handle_sysrq_char(&uart->port, ch, regs))
+ goto ignore_char;
+ uart_insert_char(&uart->port, status, 1, 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_int(int irq, void *dev_id, struct pt_regs *regs)
+{
+ struct bfin_serial_port *uart = dev_id;
+ unsigned short status;
+
+ spin_lock(&uart->port.lock);
+ status = UART_GET_IIR(uart);
+ do {
+ if ((status & IIR_STATUS) == IIR_TX_READY)
+ bfin_serial_tx_chars(uart);
+ if ((status & IIR_STATUS) == IIR_RX_READY)
+ bfin_serial_rx_chars(uart, regs);
+ status = UART_GET_IIR(uart);
+ } while (status &(IIR_TX_READY | IIR_RX_READY));
+ spin_unlock(&uart->port.lock);
+ return IRQ_HANDLED;
+}
+
+static void bfin_serial_do_work(void *port)
+{
+ struct bfin_serial_port *uart = (struct bfin_serial_port *)port;
+ bfin_serial_mctrl_check(uart);
+}
+
+#endif
+
+#ifdef CONFIG_SERIAL_BFIN_DMA
+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->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;
+ }
+
+ local_irq_save(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;
+ blackfin_dcache_flush_range((unsigned long)(xmit->buf+xmit->tail),
+ (unsigned long)(xmit->buf+xmit->tail+uart->tx_count));
+ set_dma_config(uart->tx_dma_channel,
+ set_bfin_dma_config(DIR_READ, DMA_FLOW_STOP,
+ INTR_ON_BUF,
+ DIMENSION_LINEAR,
+ DATA_SIZE_8));
+ set_dma_start_addr(uart->tx_dma_channel, (unsigned
long)(xmit->buf+xmit->tail));
+ set_dma_x_count(uart->tx_dma_channel, uart->tx_count);
+ set_dma_x_modify(uart->tx_dma_channel, 1);
+ enable_dma(uart->tx_dma_channel);
+ ier = UART_GET_IER(uart);
+ ier |= ETBEI;
+ UART_PUT_IER(uart, ier);
+ local_irq_restore(flags);
+}
+
+static void bfin_serial_dma_rx_chars(struct bfin_serial_port * uart)
+{
+ struct tty_struct *tty = uart->port.info->tty;
+ int i, flg, status = 0;
+
+ uart->port.icount.rx += CIRC_CNT(uart->rx_dma_buf.head,
uart->rx_dma_buf.tail, UART_XMIT_SIZE);;
+ 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], NULL))
+ goto dma_ignore_char;
+ uart_insert_char(&uart->port, status, 1, uart->rx_dma_buf.buf[i], flg);
+ }
+dma_ignore_char:
+ tty_flip_buffer_push(tty);
+}
+
+void bfin_serial_rx_dma_timeout(struct bfin_serial_port *uart)
+{
+ int x_pos, pos;
+ int flags = 0;
+
+ bfin_serial_dma_tx_chars(uart);
+
+ local_irq_save(flags);
+ x_pos = DMA_RX_XCOUNT - get_dma_curr_xcount(uart->rx_dma_channel);
+ 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;
+ bfin_serial_dma_rx_chars(uart);
+ uart->rx_dma_buf.head = uart->rx_dma_buf.tail;
+ }
+ local_irq_restore(flags);
+ uart->rx_dma_timer.expires = jiffies + DMA_RX_FLUSH_JIFFIES;
+ add_timer(&(uart->rx_dma_timer));
+}
+
+static irqreturn_t bfin_serial_dma_tx_int(int irq, void *dev_id,
struct pt_regs *regs)
+{
+ 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);
+ ier = UART_GET_IER(uart);
+ ier &= ~ETBEI;
+ UART_PUT_IER(uart, ier);
+ 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);
+
+ if (uart_circ_empty(xmit))
+ bfin_serial_stop_tx(&uart->port);
+ uart->tx_done = 1;
+ }
+
+ spin_unlock(&uart->port.lock);
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t bfin_serial_dma_rx_int(int irq, void *dev_id,
struct pt_regs *regs)
+{
+ 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);
+ return IRQ_HANDLED;
+}
+#endif
+
+/*
+ * Return TIOCSER_TEMT when transmitter is not busy.
+ */
+static unsigned int bfin_serial_tx_empty(struct uart_port *port)
+{
+ struct bfin_serial_port *uart = (struct bfin_serial_port *)port;
+ unsigned short lsr;
+ lsr = UART_GET_LSR(uart);
+ if (lsr & THRE)
+ return TIOCSER_TEMT;
+ else
+ return 0;
+}
+
+static unsigned int bfin_serial_get_mctrl(struct uart_port *port)
+{
+#ifdef CONFIG_SERIAL_BFIN_CTSRTS
+ if (bfin_read16(CTS_PORT) & (1<<CTS_PIN))
+ return TIOCM_DSR | TIOCM_CAR;
+ else
+#endif
+ return TIOCM_CTS | TIOCM_DSR | TIOCM_CAR;
+}
+
+static void bfin_serial_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+#ifdef CONFIG_SERIAL_BFIN_CTSRTS
+ if (mctrl & TIOCM_RTS)
+ bfin_write16(RTS_PORT, bfin_read16(RTS_PORT)&(~1<<RTS_PIN));
+ else
+ bfin_write16(RTS_PORT, bfin_read16(RTS_PORT)|(1<<RTS_PIN));
+#endif
+}
+
+/*
+ * Handle any change of modem status signal since we were last called.
+ */
+static void bfin_serial_mctrl_check(struct bfin_serial_port *uart)
+{
+#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);
+ if (!(status & TIOCM_CTS)) {
+ tty->hw_stopped = 1;
+ } 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
+}
+
+/*
+ * Interrupts always disabled.
+ */
+static void bfin_serial_break_ctl(struct uart_port *port, int break_state)
+{
+}
+
+int bfin_serial_startup(struct uart_port *port)
+{
+ struct bfin_serial_port *uart = (struct bfin_serial_port *)port;
+
+#ifdef CONFIG_SERIAL_BFIN_DMA
+ dma_addr_t dma_handle;
+
+ if (request_dma(uart->rx_dma_channel, "BFIN_UART_RX") < 0) {
+ printk(KERN_NOTICE "Unable to attach Blackfin UART RX DMA channel\n");
+ return -EBUSY;
+ } else
+ set_dma_callback(uart->rx_dma_channel, bfin_serial_dma_rx_int, uart);
+
+ if (request_dma(uart->tx_dma_channel, "BFIN_UART_TX") < 0) {
+ printk(KERN_NOTICE "Unable to attach Blackfin UART TX DMA channel\n");
+ return -EBUSY;
+ } else
+ set_dma_callback(uart->tx_dma_channel, bfin_serial_dma_tx_int, uart);
+
+ uart->rx_dma_buf.buf = (unsigned char *)dma_alloc_coherent(NULL,
PAGE_SIZE, &dma_handle, GFP_DMA);
+ uart->rx_dma_buf.head = 0;
+ uart->rx_dma_buf.tail = 0;
+ uart->rx_dma_nrows = 0;
+
+ set_dma_config(uart->rx_dma_channel,
+ set_bfin_dma_config(DIR_WRITE, DMA_FLOW_AUTO,
+ INTR_ON_ROW, DIMENSION_2D,
+ DATA_SIZE_8));
+ set_dma_x_count(uart->rx_dma_channel, DMA_RX_XCOUNT);
+ set_dma_x_modify(uart->rx_dma_channel, 1);
+ set_dma_y_count(uart->rx_dma_channel, DMA_RX_YCOUNT);
+ set_dma_y_modify(uart->rx_dma_channel, 1);
+ set_dma_start_addr(uart->rx_dma_channel, (unsigned long)uart->rx_dma_buf.buf);
+ enable_dma(uart->rx_dma_channel);
+
+ uart->rx_dma_timer.data = (unsigned long)(uart);
+ uart->rx_dma_timer.function = (void *)bfin_serial_rx_dma_timeout;
+ uart->rx_dma_timer.expires = jiffies + DMA_RX_FLUSH_JIFFIES;
+ add_timer(&(uart->rx_dma_timer));
+#else
+ if (request_irq
+ (uart->port.irq, bfin_serial_int, SA_INTERRUPT | SA_SHIRQ,
+ "BFIN_UART0_RX", uart)) {
+ printk(KERN_NOTICE "Unable to attach BlackFin UART RX interrupt\n");
+ return -EBUSY;
+ }
+
+ if (request_irq
+ (uart->port.irq+1, bfin_serial_int, SA_INTERRUPT | SA_SHIRQ,
+ "BFIN_UART0_TX", uart)) {
+ printk(KERN_NOTICE "Unable to attach BlackFin UART TX interrupt\n");
+ return -EBUSY;
+ }
+#endif
+ UART_PUT_IER(uart, UART_GET_IER(uart) | ERBFI);
+ return 0;
+}
+
+static void bfin_serial_shutdown(struct uart_port *port)
+{
+ struct bfin_serial_port *uart = (struct bfin_serial_port *)port;
+
+#ifdef CONFIG_SERIAL_BFIN_DMA
+ disable_dma(uart->tx_dma_channel);
+ free_dma(uart->tx_dma_channel);
+ disable_dma(uart->rx_dma_channel);
+ free_dma(uart->rx_dma_channel);
+ del_timer(&(uart->rx_dma_timer));
+#else
+ free_irq(uart->port.irq, uart);
+ free_irq(uart->port.irq+1, uart);
+#endif
+}
+
+static void
+bfin_serial_set_termios(struct uart_port *port, struct termios *termios,
+ struct termios *old)
+{
+}
+
+static const char *bfin_serial_type(struct uart_port *port)
+{
+ struct bfin_serial_port *uart = (struct bfin_serial_port *)port;
+ return uart->port.type == PORT_BFIN ? "BFIN-UART" : NULL;
+}
+
+/*
+ * Release the memory region(s) being used by 'port'.
+ */
+static void bfin_serial_release_port(struct uart_port *port)
+{
+}
+
+/*
+ * Request the memory region(s) being used by 'port'.
+ */
+static int bfin_serial_request_port(struct uart_port *port)
+{
+ return 0;
+}
+
+/*
+ * Configure/autoconfigure the port.
+ */
+static void bfin_serial_config_port(struct uart_port *port, int flags)
+{
+ struct bfin_serial_port *uart = (struct bfin_serial_port *)port;
+
+ if (flags & UART_CONFIG_TYPE &&
+ bfin_serial_request_port(&uart->port) == 0)
+ uart->port.type = PORT_BFIN;
+}
+
+/*
+ * Verify the new serial_struct (for TIOCSSERIAL).
+ * The only change we allow are to the flags and type, and
+ * even then only between PORT_BFIN and PORT_UNKNOWN
+ */
+static int
+bfin_serial_verify_port(struct uart_port *port, struct serial_struct *ser)
+{
+ return 0;
+}
+
+static struct uart_ops bfin_serial_pops = {
+ .tx_empty = bfin_serial_tx_empty,
+ .set_mctrl = bfin_serial_set_mctrl,
+ .get_mctrl = bfin_serial_get_mctrl,
+ .stop_tx = bfin_serial_stop_tx,
+ .start_tx = bfin_serial_start_tx,
+ .stop_rx = bfin_serial_stop_rx,
+ .enable_ms = bfin_serial_enable_ms,
+ .break_ctl = bfin_serial_break_ctl,
+ .startup = bfin_serial_startup,
+ .shutdown = bfin_serial_shutdown,
+ .set_termios = bfin_serial_set_termios,
+ .type = bfin_serial_type,
+ .release_port = bfin_serial_release_port,
+ .request_port = bfin_serial_request_port,
+ .config_port = bfin_serial_config_port,
+ .verify_port = bfin_serial_verify_port,
+};
+
+static int bfin_serial_calc_baud(unsigned int uartclk)
+{
+ int baud;
+ baud = get_sclk()/(uartclk*8);
+ if ((baud & 0x1) == 1) {
+ baud++;
+ }
+ return baud/2;
+}
+
+static void __init bfin_serial_init_ports(void)
+{
+ static int first = 1;
+ int i;
+ unsigned short val;
+ int baud;
+
+ if (!first)
+ return;
+ first = 0;
+ bfin_serial_hw_init();
+
+ for (i = 0; i < NR_PORTS; i++) {
+ bfin_serial_ports[i].port.uartclk = CONSOLE_BAUD_RATE;
+ bfin_serial_ports[i].port.ops = &bfin_serial_pops;
+ bfin_serial_ports[i].port.line = i;
+ bfin_serial_ports[i].port.iotype = UPIO_MEM;
+ bfin_serial_ports[i].port.membase = (void __iomem *)uart_base_addr[i];
+ bfin_serial_ports[i].port.mapbase = uart_base_addr[i];
+ bfin_serial_ports[i].port.irq = uart_irq[i];
+ bfin_serial_ports[i].port.flags = UPF_BOOT_AUTOCONF;
+#ifdef CONFIG_SERIAL_BFIN_DMA
+ bfin_serial_ports[i].tx_done = 1;
+ bfin_serial_ports[i].tx_count = 0;
+ bfin_serial_ports[i].tx_dma_channel = uart_tx_dma_channel[i];
+ bfin_serial_ports[i].rx_dma_channel = uart_rx_dma_channel[i];
+
+ init_timer(&(bfin_serial_ports[i].rx_dma_timer));
+#else
+ INIT_WORK(&bfin_serial_ports[i].cts_workqueue, bfin_serial_do_work,
&bfin_serial_ports[i]);
+#endif
+
+ baud = bfin_serial_calc_baud(bfin_serial_ports[i].port.uartclk);
+
+ /* Enable UART */
+ val = UART_GET_GCTL(&bfin_serial_ports[i]);
+ val |= UCEN;
+ UART_PUT_GCTL(&bfin_serial_ports[i], val);
+
+ /* Set DLAB in LCR to Access DLL and DLH */
+ val = UART_GET_LCR(&bfin_serial_ports[i]);
+ val |= DLAB;
+ UART_PUT_LCR(&bfin_serial_ports[i], val);
+
+ UART_PUT_DLL(&bfin_serial_ports[i], baud&0xFF);
+ UART_PUT_DLH(&bfin_serial_ports[i], (baud>>8)&0xFF);
+
+ /* Clear DLAB in LCR to Access THR RBR IER */
+ val = UART_GET_LCR(&bfin_serial_ports[i]);
+ val &= ~DLAB;
+ UART_PUT_LCR(&bfin_serial_ports[i], val);
+
+ /* Set LCR to Word Lengh 8-bit word select */
+ val = WLS(8);
+ UART_PUT_LCR(&bfin_serial_ports[i], val);
+ }
+}
+
+#ifdef CONFIG_SERIAL_BFIN_CONSOLE
+/*
+ * Interrupts are disabled on entering
+ */
+static void
+bfin_serial_console_write(struct console *co, const char *s, unsigned
int count)
+{
+ struct bfin_serial_port *uart = &bfin_serial_ports[co->index];
+ int flags = 0;
+ unsigned short status, tmp;
+ int i;
+
+ local_irq_save(flags);
+
+ for (i = 0; i < count; i++) {
+ do {
+ status = UART_GET_LSR(uart);
+ } while (!(status & THRE));
+
+ tmp = UART_GET_LCR(uart);
+ tmp &= ~DLAB;
+ UART_PUT_LCR(uart, tmp);
+
+ UART_PUT_CHAR(uart, s[i]);
+ if (s[i] == '\n') {
+ do {
+ status = UART_GET_LSR(uart);
+ } while(!(status & THRE));
+ UART_PUT_CHAR(uart, '\r');
+ }
+ }
+
+ local_irq_restore(flags);
+}
+
+/*
+ * If the port was already initialised (eg, by a boot loader),
+ * try to determine the current setup.
+ */
+static void __init
+bfin_serial_console_get_options(struct bfin_serial_port *uart, int *baud,
+ int *parity, int *bits)
+{
+ unsigned short status;
+
+ status = UART_GET_IER(uart) & (ERBFI | ETBEI);
+ if (status == (ERBFI | ETBEI)) {
+ /* ok, the port was enabled */
+ unsigned short lcr, val;
+ unsigned short dlh, dll;
+
+ lcr = UART_GET_LCR(uart);
+
+ *parity = 'n';
+ if (lcr & PEN) {
+ if (lcr & EPS)
+ *parity = 'e';
+ else
+ *parity = 'o';
+ }
+ switch (lcr & 0x03) {
+ case 0: *bits = 5; break;
+ case 1: *bits = 6; break;
+ case 2: *bits = 7; break;
+ case 3: *bits = 8; break;
+ }
+ /* Set DLAB in LCR to Access DLL and DLH */
+ val = UART_GET_LCR(uart);
+ val |= DLAB;
+ UART_PUT_LCR(uart, val);
+
+ dll = UART_GET_DLL(uart);
+ dlh = UART_GET_DLH(uart);
+
+ /* Clear DLAB in LCR to Access THR RBR IER */
+ val = UART_GET_LCR(uart);
+ val &= ~DLAB;
+ UART_PUT_LCR(uart, val);
+
+ *baud = get_sclk()/(16*(dll|dlh<<8));
+ }
+ DPRINTK("%s:baud = %d, parity = %c, bits= %d\n", __FUNCTION__,
*baud, *parity, *bits);
+}
+
+static int __init
+bfin_serial_console_setup(struct console *co, char *options)
+{
+ struct bfin_serial_port *uart;
+ int baud = CONSOLE_BAUD_RATE;
+ int bits = 8;
+ int parity = 'n';
+#ifdef CONFIG_SERIAL_BFIN_CTSRTS
+ int flow = 'r';
+#else
+ int flow = 'n';
+#endif
+
+ /*
+ * Check whether an invalid uart number has been specified, and
+ * if so, search for the first available port that does have
+ * console support.
+ */
+ if (co->index == -1 || co->index >= NR_PORTS)
+ co->index = 0;
+ uart = &bfin_serial_ports[co->index];
+
+ if (options)
+ uart_parse_options(options, &baud, &parity, &bits, &flow);
+ else
+ bfin_serial_console_get_options(uart, &baud, &parity, &bits);
+
+ return uart_set_options(&uart->port, co, baud, parity, bits, flow);
+}
+
+static struct uart_driver bfin_serial_reg;
+static struct console bfin_serial_console = {
+ .name = "ttyS",
+ .write = bfin_serial_console_write,
+ .device = uart_console_device,
+ .setup = bfin_serial_console_setup,
+ .flags = CON_PRINTBUFFER,
+ .index = -1,
+ .data = &bfin_serial_reg,
+};
+
+static int __init bfin_serial_rs_console_init(void)
+{
+ bfin_serial_init_ports();
+ register_console(&bfin_serial_console);
+ return 0;
+}
+console_initcall(bfin_serial_rs_console_init);
+
+#define BFIN_SERIAL_CONSOLE &bfin_serial_console
+#else
+#define BFIN_SERIAL_CONSOLE NULL
+#endif
+
+static struct uart_driver bfin_serial_reg = {
+ .owner = THIS_MODULE,
+ .driver_name = "bfin-uart",
+ .dev_name = "ttyS",
+ .major = SERIAL_BFIN_MAJOR,
+ .minor = MINOR_START,
+ .nr = NR_PORTS,
+ .cons = BFIN_SERIAL_CONSOLE,
+};
+
+static int bfin_serial_suspend(struct platform_device *dev, pm_message_t state)
+{
+ struct bfin_serial_port *uart = platform_get_drvdata(dev);
+
+ if (uart)
+ uart_suspend_port(&bfin_serial_reg, &uart->port);
+
+ return 0;
+}
+
+static int bfin_serial_resume(struct platform_device *dev)
+{
+ struct bfin_serial_port *uart = platform_get_drvdata(dev);
+
+ if (uart)
+ uart_resume_port(&bfin_serial_reg, &uart->port);
+
+ return 0;
+}
+
+static int bfin_serial_probe(struct platform_device *dev)
+{
+ struct resource *res = dev->resource;
+ int i;
+
+ for (i = 0; i < dev->num_resources; i++, res++)
+ if (res->flags & IORESOURCE_MEM)
+ break;
+
+ if (i < dev->num_resources) {
+ for (i = 0; i < NR_PORTS; i++, res++) {
+ if (bfin_serial_ports[i].port.mapbase != res->start)
+ continue;
+ bfin_serial_ports[i].port.dev = &dev->dev;
+ uart_add_one_port(&bfin_serial_reg, &bfin_serial_ports[i].port);
+ platform_set_drvdata(dev, &bfin_serial_ports[i]);
+ }
+ }
+
+ return 0;
+}
+
+static int bfin_serial_remove(struct platform_device *pdev)
+{
+ struct bfin_serial_port *uart = platform_get_drvdata(pdev);
+
+ platform_set_drvdata(pdev, NULL);
+
+ if (uart)
+ uart_remove_one_port(&bfin_serial_reg, &uart->port);
+
+ return 0;
+}
+
+static struct platform_driver bfin_serial_driver = {
+ .probe = bfin_serial_probe,
+ .remove = bfin_serial_remove,
+ .suspend = bfin_serial_suspend,
+ .resume = bfin_serial_resume,
+ .driver = {
+ .name = "bfin-uart",
+ },
+};
+
+static int __init bfin_serial_init(void)
+{
+ int ret;
+
+ printk(KERN_INFO "Serial: Blackfin serial driver\n");
+
+ bfin_serial_init_ports();
+
+ ret = uart_register_driver(&bfin_serial_reg);
+ if (ret == 0) {
+ ret = platform_driver_register(&bfin_serial_driver);
+ if (ret) {
+ DPRINTK("uart register failed\n");
+ uart_unregister_driver(&bfin_serial_reg);
+ }
+ }
+ return ret;
+}
+
+static void __exit bfin_serial_exit(void)
+{
+ platform_driver_unregister(&bfin_serial_driver);
+ uart_unregister_driver(&bfin_serial_reg);
+}
+
+module_init(bfin_serial_init);
+module_exit(bfin_serial_exit);
+
+MODULE_AUTHOR("Aubrey.Li <[email protected]>");
+MODULE_DESCRIPTION("Blackfin generic serial port driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_CHARDEV_MAJOR(SERIAL_BFIN_MAJOR);
diff -urN linux-2.6.18.patch1/include/linux/serial_core.h
linux-2.6.18.patch2/include/linux/serial_core.h
--- linux-2.6.18.patch1/include/linux/serial_core.h 2006-09-21
09:14:54.000000000 +0800
+++ linux-2.6.18.patch2/include/linux/serial_core.h 2006-09-21
09:38:17.000000000 +0800
@@ -132,6 +132,9 @@

#define PORT_S3C2412 73

+/* Blackfin bf5xx */
+#define PORT_BFIN 74
+

#ifdef __KERNEL__

--
Best regards,
Luke Yang
[email protected]


Attachments:
(No filename) (27.10 kB)
blackfin_serial_drv_2.6.18.patch (26.63 kB)
Download all attachments

2006-09-21 05:27:31

by Randy Dunlap

[permalink] [raw]
Subject: Re: [PATCH 2/4] Blackfin: Serial driver for Blackfin arch on 2.6.18

On Thu, 21 Sep 2006 11:33:05 +0800 Luke Yang wrote:

> This is the serial driver for Blackfin. It is designed for the serial
> core framework.
>
> As to other drivers, I'll send them one by one later.
>
> Signed-off-by: Luke Yang <[email protected]>
>
> drivers/serial/Kconfig | 35 +
> drivers/serial/Makefile | 3
> drivers/serial/bfin_5xx.c | 903 ++++++++++++++++++++++++++++++++++++++++++++
> include/linux/serial_core.h | 3
> 4 files changed, 943 insertions(+), 1 deletion(-)
>
> diff -urN linux-2.6.18.patch1/drivers/serial/Kconfig
> linux-2.6.18.patch2/drivers/serial/Kconfig
> --- linux-2.6.18.patch1/drivers/serial/Kconfig 2006-09-21
> 09:14:42.000000000 +0800
> +++ linux-2.6.18.patch2/drivers/serial/Kconfig 2006-09-21
> 09:38:17.000000000 +0800
> @@ -488,6 +488,41 @@
> your boot loader (lilo or loadlin) about how to pass options to the
> kernel at boot time.)
>
> +config SERIAL_BFIN
> + bool "Blackfin serial port support (EXPERIMENTAL)"
> + depends on BFIN && EXPERIMENTAL
> + select SERIAL_CORE

Just curious: why bool and not tristate? (i.e., why is loadable
module not allowed?)

> +config SERIAL_BFIN_CONSOLE
> + bool "Console on Blackfin serial port"
> + depends on SERIAL_BFIN
> + select SERIAL_CORE_CONSOLE
> +
> +choice
> + prompt "Blackfin UART Mode"
> + depends on SERIAL_BFIN
> + default SERIAL_BFIN_DMA
> + ---help---
> + This driver supports the built-in serial ports of the
> Blackfin family of CPUs
> +
> +config SERIAL_BFIN_DMA
> + bool "Blackfin UART DMA mode"
> + depends on DMA_UNCACHED_1M
> + help
> + This driver works under DMA mode. If this option is
> selected, the blackfin simple dma driver is also enabled.

Please break that long line at < 80 columns (so that left-right
scrolling is not required to read it in menuconfig).

> +config SERIAL_BFIN_PIO
> + bool "Blackfin UART PIO mode"
> + help
> + This driver works under PIO mode.
> +endchoice
> +
> +config SERIAL_BFIN_CTSRTS
> + bool "Enable hardware flow control"
> + depends on SERIAL_BFIN
> + help
> + Enable hardware flow control in the driver. Using GPIO emulate the
> CTS/RTS signal.

Split the long help text into 2 lines.

> config SERIAL_IMX
> bool "IMX serial port support"
> depends on ARM && ARCH_IMX

> diff -urN linux-2.6.18.patch1/drivers/serial/Makefile
> linux-2.6.18.patch2/drivers/serial/Makefile
> --- linux-2.6.18.patch1/drivers/serial/Makefile 2006-09-21
> 09:14:42.000000000 +0800
> +++ linux-2.6.18.patch2/drivers/serial/Makefile 2006-09-21
> 09:38:17.000000000 +0800
> @@ -55,4 +56,4 @@
> obj-$(CONFIG_SERIAL_SGI_IOC4) += ioc4_serial.o
> obj-$(CONFIG_SERIAL_SGI_IOC3) += ioc3_serial.o
> obj-$(CONFIG_SERIAL_AT91) += at91_serial.o
> -obj-$(CONFIG_SERIAL_NETX) += netx-serial.o
> +obj-$(CONFIG_SERIAL_NETX) += netx-serial.o
> \ No newline at end of file

What is the purpose of the change above?

> diff -urN linux-2.6.18.patch1/drivers/serial/bfin_5xx.c
> linux-2.6.18.patch2/drivers/serial/bfin_5xx.c
> --- linux-2.6.18.patch1/drivers/serial/bfin_5xx.c 1970-01-01
> 08:00:00.000000000 +0800
> +++ linux-2.6.18.patch2/drivers/serial/bfin_5xx.c 2006-09-21
> 09:38:17.000000000 +0800
> @@ -0,0 +1,903 @@
> +
> +#include <linux/config.h>

Don't include the config.h header file. That's done automatically
by the build system.

> +static irqreturn_t bfin_serial_int(int irq, void *dev_id, struct pt_regs *regs)
> +{
> + struct bfin_serial_port *uart = dev_id;
> + unsigned short status;
> +
> + spin_lock(&uart->port.lock);
> + status = UART_GET_IIR(uart);
> + do {
> + if ((status & IIR_STATUS) == IIR_TX_READY)
> + bfin_serial_tx_chars(uart);
> + if ((status & IIR_STATUS) == IIR_RX_READY)
> + bfin_serial_rx_chars(uart, regs);
> + status = UART_GET_IIR(uart);
> + } while (status &(IIR_TX_READY | IIR_RX_READY));
> + spin_unlock(&uart->port.lock);
> + return IRQ_HANDLED;

So, the interrupt is requested as Shared, but then the int. handler
code (above here) does not check to see if the interrupt was
for this device. Shouldn't it do that and then return IRQ_NONE
if it wasn't for this device?

> +}
> + bfin_serial_mctrl_check(uart);
> +}
> +
> +#endif
> +
> +#ifdef CONFIG_SERIAL_BFIN_DMA
> +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->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;
> + }
> +
> + local_irq_save(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;

odd indentation above.

> + blackfin_dcache_flush_range((unsigned long)(xmit->buf+xmit->tail),
> + (unsigned long)(xmit->buf+xmit->tail+uart->tx_count));
> + set_dma_config(uart->tx_dma_channel,
> + set_bfin_dma_config(DIR_READ, DMA_FLOW_STOP,
> + INTR_ON_BUF,
> + DIMENSION_LINEAR,
> + DATA_SIZE_8));
> + set_dma_start_addr(uart->tx_dma_channel, (unsigned
> long)(xmit->buf+xmit->tail));
> + set_dma_x_count(uart->tx_dma_channel, uart->tx_count);
> + set_dma_x_modify(uart->tx_dma_channel, 1);
> + enable_dma(uart->tx_dma_channel);
> + ier = UART_GET_IER(uart);
> + ier |= ETBEI;
> + UART_PUT_IER(uart, ier);
> + local_irq_restore(flags);
> +}
> +

> +static irqreturn_t bfin_serial_dma_tx_int(int irq, void *dev_id,
> struct pt_regs *regs)

"struct" line above is a separate line but does not have a
beginning '+' mark, so the patch is malformed/corrupted.
This happened in a few other places also, so something is
breaking/splitting lines badly for us. :(

> +{
> + 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);
> + ier = UART_GET_IER(uart);
> + ier &= ~ETBEI;
> + UART_PUT_IER(uart, ier);
> + 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);
> +
> + if (uart_circ_empty(xmit))
> + bfin_serial_stop_tx(&uart->port);
> + uart->tx_done = 1;
> + }
> +
> + spin_unlock(&uart->port.lock);
> + return IRQ_HANDLED;
> +}
> +
> +static irqreturn_t bfin_serial_dma_rx_int(int irq, void *dev_id,
> struct pt_regs *regs)
> +{
> + 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);
> + return IRQ_HANDLED;
> +}
> +#endif

> +static unsigned int bfin_serial_get_mctrl(struct uart_port *port)
> +{
> +#ifdef CONFIG_SERIAL_BFIN_CTSRTS
> + if (bfin_read16(CTS_PORT) & (1<<CTS_PIN))
> + return TIOCM_DSR | TIOCM_CAR;
> + else
> +#endif
> + return TIOCM_CTS | TIOCM_DSR | TIOCM_CAR;

Hardcoded return value, without reading a port, right?

> +}
> +
> +static void bfin_serial_set_mctrl(struct uart_port *port, unsigned int mctrl)
> +{
> +#ifdef CONFIG_SERIAL_BFIN_CTSRTS
> + if (mctrl & TIOCM_RTS)
> + bfin_write16(RTS_PORT, bfin_read16(RTS_PORT)&(~1<<RTS_PIN));
> + else
> + bfin_write16(RTS_PORT, bfin_read16(RTS_PORT)|(1<<RTS_PIN));
> +#endif
> +}

> +int bfin_serial_startup(struct uart_port *port)
> +{
> + struct bfin_serial_port *uart = (struct bfin_serial_port *)port;
> +
> +#ifdef CONFIG_SERIAL_BFIN_DMA
> + dma_addr_t dma_handle;
> +
> + if (request_dma(uart->rx_dma_channel, "BFIN_UART_RX") < 0) {
> + printk(KERN_NOTICE "Unable to attach Blackfin UART RX DMA channel\n");
> + return -EBUSY;
> + } else
> + set_dma_callback(uart->rx_dma_channel, bfin_serial_dma_rx_int, uart);
> +
> + if (request_dma(uart->tx_dma_channel, "BFIN_UART_TX") < 0) {
> + printk(KERN_NOTICE "Unable to attach Blackfin UART TX DMA channel\n");

Before returning, this failure path needs to free_dma() for the
first request_dma() that succeeded.
I would also suggest doing the set_dma_callback() calls after
both request_dma() calls have succeeded.

> + return -EBUSY;
> + } else
> + set_dma_callback(uart->tx_dma_channel, bfin_serial_dma_tx_int, uart);
> +
> + uart->rx_dma_buf.buf = (unsigned char *)dma_alloc_coherent(NULL,
> PAGE_SIZE, &dma_handle, GFP_DMA);

bad line split.

> + uart->rx_dma_buf.head = 0;
> + uart->rx_dma_buf.tail = 0;
> + uart->rx_dma_nrows = 0;
> +
> + set_dma_config(uart->rx_dma_channel,
> + set_bfin_dma_config(DIR_WRITE, DMA_FLOW_AUTO,
> + INTR_ON_ROW, DIMENSION_2D,
> + DATA_SIZE_8));
> + set_dma_x_count(uart->rx_dma_channel, DMA_RX_XCOUNT);
> + set_dma_x_modify(uart->rx_dma_channel, 1);
> + set_dma_y_count(uart->rx_dma_channel, DMA_RX_YCOUNT);
> + set_dma_y_modify(uart->rx_dma_channel, 1);
> + set_dma_start_addr(uart->rx_dma_channel, (unsigned long)uart->rx_dma_buf.buf);

ditto

> + enable_dma(uart->rx_dma_channel);
> +
> + uart->rx_dma_timer.data = (unsigned long)(uart);
> + uart->rx_dma_timer.function = (void *)bfin_serial_rx_dma_timeout;
> + uart->rx_dma_timer.expires = jiffies + DMA_RX_FLUSH_JIFFIES;
> + add_timer(&(uart->rx_dma_timer));
> +#else
> + if (request_irq
> + (uart->port.irq, bfin_serial_int, SA_INTERRUPT | SA_SHIRQ,

The request_irq() parameters have changed a bit recently.
SA_SHIRQ is now IRQF_SHARED and SA_INTERRUPT is IRQF_DISABLED.
Please change to use the new interface.
It is documented in Documentation/DocBook/genericirq*

> + "BFIN_UART0_RX", uart)) {
> + printk(KERN_NOTICE "Unable to attach BlackFin UART RX interrupt\n");
> + return -EBUSY;
> + }
> +
> + if (request_irq
> + (uart->port.irq+1, bfin_serial_int, SA_INTERRUPT | SA_SHIRQ,
> + "BFIN_UART0_TX", uart)) {
> + printk(KERN_NOTICE "Unable to attach BlackFin UART TX interrupt\n");

This second request_irq() failure needs to call free_irq() for the
first request_irq() that succeeded...

> + return -EBUSY;
> + }
> +#endif
> + UART_PUT_IER(uart, UART_GET_IER(uart) | ERBFI);
> + return 0;
> +}

> +static int bfin_serial_calc_baud(unsigned int uartclk)
> +{
> + int baud;
> + baud = get_sclk()/(uartclk*8);

Throw a few spaces in there, like so:
baud = get_sclk() / (uartclk * 8);

> + if ((baud & 0x1) == 1) {
> + baud++;
> + }
> + return baud/2;
> +}

---
~Randy

2006-09-21 08:28:36

by Luke Yang

[permalink] [raw]
Subject: Re: [PATCH 2/4] Blackfin: Serial driver for Blackfin arch on 2.6.18

OK I merged the fixes and renewed the patch.

drivers/serial/Kconfig | 44 ++
drivers/serial/Makefile | 1
drivers/serial/bfin_5xx.c | 903 ++++++++++++++++++++++++++++++++++++++++++++
include/linux/serial_core.h | 3
4 files changed, 951 insertions(+)

diff -urN linux-2.6.18.patch1/drivers/serial/Kconfig
linux-2.6.18.patch2/drivers/serial/Kconfig
--- linux-2.6.18.patch1/drivers/serial/Kconfig 2006-09-21
09:14:42.000000000 +0800
+++ linux-2.6.18.patch2/drivers/serial/Kconfig 2006-09-21
16:17:50.000000000 +0800
@@ -488,6 +488,50 @@
your boot loader (lilo or loadlin) about how to pass options to the
kernel at boot time.)

+config SERIAL_BFIN
+ tristate "Blackfin serial port support (EXPERIMENTAL)"
+ depends on BFIN && EXPERIMENTAL
+ select SERIAL_CORE
+ help
+ Add support for the built-in UARTs on the Blackfin.
+
+ To compile this driver as a module, choose M here: the
+ module will be called bfin_5xx.
+
+config SERIAL_BFIN_CONSOLE
+ bool "Console on Blackfin serial port"
+ depends on SERIAL_BFIN
+ select SERIAL_CORE_CONSOLE
+
+choice
+ prompt "Blackfin UART Mode"
+ depends on SERIAL_BFIN
+ default SERIAL_BFIN_DMA
+ help
+ This driver supports the built-in serial ports of the Blackfin family
+ of CPUs
+
+config SERIAL_BFIN_DMA
+ bool "Blackfin UART DMA mode"
+ depends on DMA_UNCACHED_1M
+ help
+ This driver works under DMA mode. If this option is selected, the
+ blackfin simple dma driver is also enabled.
+
+config SERIAL_BFIN_PIO
+ bool "Blackfin UART PIO mode"
+ help
+ This driver works under PIO mode.
+
+endchoice
+
+config SERIAL_BFIN_CTSRTS
+ bool "Enable hardware flow control"
+ depends on SERIAL_BFIN
+ help
+ Enable hardware flow control in the driver. Using GPIO emulate the CTS/RTS
+ signal.
+
config SERIAL_IMX
bool "IMX serial port support"
depends on ARM && ARCH_IMX
diff -urN linux-2.6.18.patch1/drivers/serial/Makefile
linux-2.6.18.patch2/drivers/serial/Makefile
--- linux-2.6.18.patch1/drivers/serial/Makefile 2006-09-21
09:14:42.000000000 +0800
+++ linux-2.6.18.patch2/drivers/serial/Makefile 2006-09-21
15:54:13.000000000 +0800
@@ -25,6 +25,7 @@
obj-$(CONFIG_SERIAL_CLPS711X) += clps711x.o
obj-$(CONFIG_SERIAL_PXA) += pxa.o
obj-$(CONFIG_SERIAL_SA1100) += sa1100.o
+obj-$(CONFIG_SERIAL_BFIN) += bfin_5xx.o
obj-$(CONFIG_SERIAL_S3C2410) += s3c2410.o
obj-$(CONFIG_SERIAL_SUNCORE) += suncore.o
obj-$(CONFIG_SERIAL_SUNHV) += sunhv.o
diff -urN linux-2.6.18.patch1/drivers/serial/bfin_5xx.c
linux-2.6.18.patch2/drivers/serial/bfin_5xx.c
--- linux-2.6.18.patch1/drivers/serial/bfin_5xx.c 1970-01-01
08:00:00.000000000 +0800
+++ linux-2.6.18.patch2/drivers/serial/bfin_5xx.c 2006-09-21
16:19:02.000000000 +0800
@@ -0,0 +1,903 @@
+/*
+ * File: drivers/serial/bfin_5xx.c
+ * Based on: Based on drivers/serial/sa1100.c
+ * Author: Aubrey Li <[email protected]>
+ *
+ * Created:
+ * Description: Driver for blackfin 5xx serial ports
+ *
+ * Rev: $Id: bfin_5xx.c,v 1.12 2006/09/04 04:44:27 aubrey Exp $
+ *
+ * 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
+ */
+
+#include <linux/config.h>
+
+#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>
+
+#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
+
+/* We've been assigned a range on the "Low-density serial ports" major */
+#define SERIAL_BFIN_MAJOR TTY_MAJOR
+#define MINOR_START 64
+
+#define DEBUG
+
+#ifdef DEBUG
+# define DPRINTK(x...) printk(KERN_DEBUG x)
+#else
+# define DPRINTK(x...) do { } while (0)
+#endif
+
+/*
+ * Setup for console. Argument comes from the menuconfig
+ */
+
+#if defined(CONFIG_BAUD_9600)
+#define CONSOLE_BAUD_RATE 9600
+#elif defined(CONFIG_BAUD_19200)
+#define CONSOLE_BAUD_RATE 19200
+#elif defined(CONFIG_BAUD_38400)
+#define CONSOLE_BAUD_RATE 38400
+#elif defined(CONFIG_BAUD_57600)
+#define CONSOLE_BAUD_RATE 57600
+#elif defined(CONFIG_BAUD_115200)
+#define CONSOLE_BAUD_RATE 115200
+#endif
+
+#define DMA_RX_XCOUNT TTY_FLIPBUF_SIZE
+#define DMA_RX_YCOUNT (PAGE_SIZE / DMA_RX_XCOUNT)
+
+#define DMA_RX_FLUSH_JIFFIES 5
+
+#ifdef CONFIG_SERIAL_BFIN_DMA
+wait_queue_head_t bfin_serial_tx_queue[NR_PORTS];
+static void bfin_serial_dma_tx_chars(struct bfin_serial_port *uart);
+#else
+static void bfin_serial_do_work(void *);
+static void bfin_serial_tx_chars(struct bfin_serial_port *uart);
+static void local_put_char(struct bfin_serial_port *uart, char ch);
+#endif
+
+static void bfin_serial_mctrl_check(struct bfin_serial_port *uart);
+
+/*
+ * interrupts disabled on entry
+ */
+static void bfin_serial_stop_tx(struct uart_port *port)
+{
+ struct bfin_serial_port *uart = (struct bfin_serial_port *)port;
+ unsigned short ier;
+ ier = UART_GET_IER(uart);
+ ier &= ~ETBEI;
+ UART_PUT_IER(uart, ier);
+#ifdef CONFIG_SERIAL_BFIN_DMA
+ disable_dma(uart->tx_dma_channel);
+#endif
+}
+
+/*
+ * port locked and interrupts 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
+ unsigned short ier;
+ ier = UART_GET_IER(uart);
+ ier |= ETBEI;
+ UART_PUT_IER(uart, ier);
+ bfin_serial_tx_chars(uart);
+#endif
+}
+
+/*
+ * Interrupts enabled
+ */
+static void bfin_serial_stop_rx(struct uart_port *port)
+{
+ struct bfin_serial_port *uart = (struct bfin_serial_port *)port;
+ unsigned short ier;
+ ier = UART_GET_IER(uart);
+ ier &= ERBFI;
+ UART_PUT_IER(uart, ier);
+}
+
+/*
+ * Set the modem control timer to fire immediately.
+ */
+static void bfin_serial_enable_ms(struct uart_port *port)
+{
+}
+
+#ifdef CONFIG_SERIAL_BFIN_PIO
+static void local_put_char(struct bfin_serial_port *uart, char ch)
+{
+ unsigned short status;
+ int flags = 0;
+
+ local_irq_save(flags);
+
+ do {
+ status = UART_GET_LSR(uart);
+ } while (!(status & THRE));
+
+ UART_PUT_CHAR(uart, ch);
+ local_irq_restore(flags);
+}
+
+static void
+bfin_serial_rx_chars(struct bfin_serial_port *uart, struct pt_regs *regs)
+{
+ struct tty_struct *tty = uart->port.info->tty;
+ unsigned int status=0, ch, flg;
+ ch = UART_GET_CHAR(uart);
+ uart->port.icount.rx++;
+ flg = TTY_NORMAL;
+ if (uart_handle_sysrq_char(&uart->port, ch, regs))
+ goto ignore_char;
+ uart_insert_char(&uart->port, status, 1, 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_int(int irq, void *dev_id, struct pt_regs *regs)
+{
+ struct bfin_serial_port *uart = dev_id;
+ unsigned short status;
+
+ spin_lock(&uart->port.lock);
+ status = UART_GET_IIR(uart);
+ do {
+ if ((status & IIR_STATUS) == IIR_TX_READY)
+ bfin_serial_tx_chars(uart);
+ if ((status & IIR_STATUS) == IIR_RX_READY)
+ bfin_serial_rx_chars(uart, regs);
+ status = UART_GET_IIR(uart);
+ } while (status &(IIR_TX_READY | IIR_RX_READY));
+ spin_unlock(&uart->port.lock);
+ return IRQ_HANDLED;
+}
+
+static void bfin_serial_do_work(void *port)
+{
+ struct bfin_serial_port *uart = (struct bfin_serial_port *)port;
+ bfin_serial_mctrl_check(uart);
+}
+
+#endif
+
+#ifdef CONFIG_SERIAL_BFIN_DMA
+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->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;
+ }
+
+ local_irq_save(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;
+ blackfin_dcache_flush_range((unsigned long)(xmit->buf+xmit->tail),
+ (unsigned long)(xmit->buf+xmit->tail+uart->tx_count));
+ set_dma_config(uart->tx_dma_channel,
+ set_bfin_dma_config(DIR_READ, DMA_FLOW_STOP,
+ INTR_ON_BUF,
+ DIMENSION_LINEAR,
+ DATA_SIZE_8));
+ set_dma_start_addr(uart->tx_dma_channel, (unsigned
long)(xmit->buf+xmit->tail));
+ set_dma_x_count(uart->tx_dma_channel, uart->tx_count);
+ set_dma_x_modify(uart->tx_dma_channel, 1);
+ enable_dma(uart->tx_dma_channel);
+ ier = UART_GET_IER(uart);
+ ier |= ETBEI;
+ UART_PUT_IER(uart, ier);
+ local_irq_restore(flags);
+}
+
+static void bfin_serial_dma_rx_chars(struct bfin_serial_port * uart)
+{
+ struct tty_struct *tty = uart->port.info->tty;
+ int i, flg, status = 0;
+
+ uart->port.icount.rx += CIRC_CNT(uart->rx_dma_buf.head,
uart->rx_dma_buf.tail, UART_XMIT_SIZE);;
+ 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], NULL))
+ goto dma_ignore_char;
+ uart_insert_char(&uart->port, status, 1, uart->rx_dma_buf.buf[i], flg);
+ }
+dma_ignore_char:
+ tty_flip_buffer_push(tty);
+}
+
+void bfin_serial_rx_dma_timeout(struct bfin_serial_port *uart)
+{
+ int x_pos, pos;
+ int flags = 0;
+
+ bfin_serial_dma_tx_chars(uart);
+
+ local_irq_save(flags);
+ x_pos = DMA_RX_XCOUNT - get_dma_curr_xcount(uart->rx_dma_channel);
+ 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;
+ bfin_serial_dma_rx_chars(uart);
+ uart->rx_dma_buf.head = uart->rx_dma_buf.tail;
+ }
+ local_irq_restore(flags);
+ uart->rx_dma_timer.expires = jiffies + DMA_RX_FLUSH_JIFFIES;
+ add_timer(&(uart->rx_dma_timer));
+}
+
+static irqreturn_t bfin_serial_dma_tx_int(int irq, void *dev_id,
struct pt_regs *regs)
+{
+ 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);
+ ier = UART_GET_IER(uart);
+ ier &= ~ETBEI;
+ UART_PUT_IER(uart, ier);
+ 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);
+
+ if (uart_circ_empty(xmit))
+ bfin_serial_stop_tx(&uart->port);
+ uart->tx_done = 1;
+ }
+
+ spin_unlock(&uart->port.lock);
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t bfin_serial_dma_rx_int(int irq, void *dev_id,
struct pt_regs *regs)
+{
+ 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);
+ return IRQ_HANDLED;
+}
+#endif
+
+/*
+ * Return TIOCSER_TEMT when transmitter is not busy.
+ */
+static unsigned int bfin_serial_tx_empty(struct uart_port *port)
+{
+ struct bfin_serial_port *uart = (struct bfin_serial_port *)port;
+ unsigned short lsr;
+ lsr = UART_GET_LSR(uart);
+ if (lsr & THRE)
+ return TIOCSER_TEMT;
+ else
+ return 0;
+}
+
+static unsigned int bfin_serial_get_mctrl(struct uart_port *port)
+{
+#ifdef CONFIG_SERIAL_BFIN_CTSRTS
+ if (bfin_read16(CTS_PORT) & (1<<CTS_PIN))
+ return TIOCM_DSR | TIOCM_CAR;
+ else
+#endif
+ return TIOCM_CTS | TIOCM_DSR | TIOCM_CAR;
+}
+
+static void bfin_serial_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+#ifdef CONFIG_SERIAL_BFIN_CTSRTS
+ if (mctrl & TIOCM_RTS)
+ bfin_write16(RTS_PORT, bfin_read16(RTS_PORT)&(~1<<RTS_PIN));
+ else
+ bfin_write16(RTS_PORT, bfin_read16(RTS_PORT)|(1<<RTS_PIN));
+#endif
+}
+
+/*
+ * Handle any change of modem status signal since we were last called.
+ */
+static void bfin_serial_mctrl_check(struct bfin_serial_port *uart)
+{
+#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);
+ if (!(status & TIOCM_CTS)) {
+ tty->hw_stopped = 1;
+ } 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
+}
+
+/*
+ * Interrupts always disabled.
+ */
+static void bfin_serial_break_ctl(struct uart_port *port, int break_state)
+{
+}
+
+int bfin_serial_startup(struct uart_port *port)
+{
+ struct bfin_serial_port *uart = (struct bfin_serial_port *)port;
+
+#ifdef CONFIG_SERIAL_BFIN_DMA
+ dma_addr_t dma_handle;
+
+ if (request_dma(uart->rx_dma_channel, "BFIN_UART_RX") < 0) {
+ printk(KERN_NOTICE "Unable to attach Blackfin UART RX DMA channel\n");
+ return -EBUSY;
+ } else
+ set_dma_callback(uart->rx_dma_channel, bfin_serial_dma_rx_int, uart);
+
+ if (request_dma(uart->tx_dma_channel, "BFIN_UART_TX") < 0) {
+ printk(KERN_NOTICE "Unable to attach Blackfin UART TX DMA channel\n");
+ return -EBUSY;
+ } else
+ set_dma_callback(uart->tx_dma_channel, bfin_serial_dma_tx_int, uart);
+
+ uart->rx_dma_buf.buf = (unsigned char *)dma_alloc_coherent(NULL,
PAGE_SIZE, &dma_handle, GFP_DMA);
+ uart->rx_dma_buf.head = 0;
+ uart->rx_dma_buf.tail = 0;
+ uart->rx_dma_nrows = 0;
+
+ set_dma_config(uart->rx_dma_channel,
+ set_bfin_dma_config(DIR_WRITE, DMA_FLOW_AUTO,
+ INTR_ON_ROW, DIMENSION_2D,
+ DATA_SIZE_8));
+ set_dma_x_count(uart->rx_dma_channel, DMA_RX_XCOUNT);
+ set_dma_x_modify(uart->rx_dma_channel, 1);
+ set_dma_y_count(uart->rx_dma_channel, DMA_RX_YCOUNT);
+ set_dma_y_modify(uart->rx_dma_channel, 1);
+ set_dma_start_addr(uart->rx_dma_channel, (unsigned long)uart->rx_dma_buf.buf);
+ enable_dma(uart->rx_dma_channel);
+
+ uart->rx_dma_timer.data = (unsigned long)(uart);
+ uart->rx_dma_timer.function = (void *)bfin_serial_rx_dma_timeout;
+ uart->rx_dma_timer.expires = jiffies + DMA_RX_FLUSH_JIFFIES;
+ add_timer(&(uart->rx_dma_timer));
+#else
+ if (request_irq
+ (uart->port.irq, bfin_serial_int, IRQF_DISABLED | IRQF_SHARED,
+ "BFIN_UART0_RX", uart)) {
+ printk(KERN_NOTICE "Unable to attach BlackFin UART RX interrupt\n");
+ return -EBUSY;
+ }
+
+ if (request_irq
+ (uart->port.irq+1, bfin_serial_int, IRQF_DISABLED | IRQF_SHARED,
+ "BFIN_UART0_TX", uart)) {
+ printk(KERN_NOTICE "Unable to attach BlackFin UART TX interrupt\n");
+ return -EBUSY;
+ }
+#endif
+ UART_PUT_IER(uart, UART_GET_IER(uart) | ERBFI);
+ return 0;
+}
+
+static void bfin_serial_shutdown(struct uart_port *port)
+{
+ struct bfin_serial_port *uart = (struct bfin_serial_port *)port;
+
+#ifdef CONFIG_SERIAL_BFIN_DMA
+ disable_dma(uart->tx_dma_channel);
+ free_dma(uart->tx_dma_channel);
+ disable_dma(uart->rx_dma_channel);
+ free_dma(uart->rx_dma_channel);
+ del_timer(&(uart->rx_dma_timer));
+#else
+ free_irq(uart->port.irq, uart);
+ free_irq(uart->port.irq+1, uart);
+#endif
+}
+
+static void
+bfin_serial_set_termios(struct uart_port *port, struct termios *termios,
+ struct termios *old)
+{
+}
+
+static const char *bfin_serial_type(struct uart_port *port)
+{
+ struct bfin_serial_port *uart = (struct bfin_serial_port *)port;
+ return uart->port.type == PORT_BFIN ? "BFIN-UART" : NULL;
+}
+
+/*
+ * Release the memory region(s) being used by 'port'.
+ */
+static void bfin_serial_release_port(struct uart_port *port)
+{
+}
+
+/*
+ * Request the memory region(s) being used by 'port'.
+ */
+static int bfin_serial_request_port(struct uart_port *port)
+{
+ return 0;
+}
+
+/*
+ * Configure/autoconfigure the port.
+ */
+static void bfin_serial_config_port(struct uart_port *port, int flags)
+{
+ struct bfin_serial_port *uart = (struct bfin_serial_port *)port;
+
+ if (flags & UART_CONFIG_TYPE &&
+ bfin_serial_request_port(&uart->port) == 0)
+ uart->port.type = PORT_BFIN;
+}
+
+/*
+ * Verify the new serial_struct (for TIOCSSERIAL).
+ * The only change we allow are to the flags and type, and
+ * even then only between PORT_BFIN and PORT_UNKNOWN
+ */
+static int
+bfin_serial_verify_port(struct uart_port *port, struct serial_struct *ser)
+{
+ return 0;
+}
+
+static struct uart_ops bfin_serial_pops = {
+ .tx_empty = bfin_serial_tx_empty,
+ .set_mctrl = bfin_serial_set_mctrl,
+ .get_mctrl = bfin_serial_get_mctrl,
+ .stop_tx = bfin_serial_stop_tx,
+ .start_tx = bfin_serial_start_tx,
+ .stop_rx = bfin_serial_stop_rx,
+ .enable_ms = bfin_serial_enable_ms,
+ .break_ctl = bfin_serial_break_ctl,
+ .startup = bfin_serial_startup,
+ .shutdown = bfin_serial_shutdown,
+ .set_termios = bfin_serial_set_termios,
+ .type = bfin_serial_type,
+ .release_port = bfin_serial_release_port,
+ .request_port = bfin_serial_request_port,
+ .config_port = bfin_serial_config_port,
+ .verify_port = bfin_serial_verify_port,
+};
+
+static int bfin_serial_calc_baud(unsigned int uartclk)
+{
+ int baud;
+ baud = get_sclk()/(uartclk*8);
+ if ((baud & 0x1) == 1) {
+ baud++;
+ }
+ return baud/2;
+}
+
+static void __init bfin_serial_init_ports(void)
+{
+ static int first = 1;
+ int i;
+ unsigned short val;
+ int baud;
+
+ if (!first)
+ return;
+ first = 0;
+ bfin_serial_hw_init();
+
+ for (i = 0; i < NR_PORTS; i++) {
+ bfin_serial_ports[i].port.uartclk = CONSOLE_BAUD_RATE;
+ bfin_serial_ports[i].port.ops = &bfin_serial_pops;
+ bfin_serial_ports[i].port.line = i;
+ bfin_serial_ports[i].port.iotype = UPIO_MEM;
+ bfin_serial_ports[i].port.membase = (void __iomem *)uart_base_addr[i];
+ bfin_serial_ports[i].port.mapbase = uart_base_addr[i];
+ bfin_serial_ports[i].port.irq = uart_irq[i];
+ bfin_serial_ports[i].port.flags = UPF_BOOT_AUTOCONF;
+#ifdef CONFIG_SERIAL_BFIN_DMA
+ bfin_serial_ports[i].tx_done = 1;
+ bfin_serial_ports[i].tx_count = 0;
+ bfin_serial_ports[i].tx_dma_channel = uart_tx_dma_channel[i];
+ bfin_serial_ports[i].rx_dma_channel = uart_rx_dma_channel[i];
+
+ init_timer(&(bfin_serial_ports[i].rx_dma_timer));
+#else
+ INIT_WORK(&bfin_serial_ports[i].cts_workqueue, bfin_serial_do_work,
&bfin_serial_ports[i]);
+#endif
+
+ baud = bfin_serial_calc_baud(bfin_serial_ports[i].port.uartclk);
+
+ /* Enable UART */
+ val = UART_GET_GCTL(&bfin_serial_ports[i]);
+ val |= UCEN;
+ UART_PUT_GCTL(&bfin_serial_ports[i], val);
+
+ /* Set DLAB in LCR to Access DLL and DLH */
+ val = UART_GET_LCR(&bfin_serial_ports[i]);
+ val |= DLAB;
+ UART_PUT_LCR(&bfin_serial_ports[i], val);
+
+ UART_PUT_DLL(&bfin_serial_ports[i], baud&0xFF);
+ UART_PUT_DLH(&bfin_serial_ports[i], (baud>>8)&0xFF);
+
+ /* Clear DLAB in LCR to Access THR RBR IER */
+ val = UART_GET_LCR(&bfin_serial_ports[i]);
+ val &= ~DLAB;
+ UART_PUT_LCR(&bfin_serial_ports[i], val);
+
+ /* Set LCR to Word Lengh 8-bit word select */
+ val = WLS(8);
+ UART_PUT_LCR(&bfin_serial_ports[i], val);
+ }
+}
+
+#ifdef CONFIG_SERIAL_BFIN_CONSOLE
+/*
+ * Interrupts are disabled on entering
+ */
+static void
+bfin_serial_console_write(struct console *co, const char *s, unsigned
int count)
+{
+ struct bfin_serial_port *uart = &bfin_serial_ports[co->index];
+ int flags = 0;
+ unsigned short status, tmp;
+ int i;
+
+ local_irq_save(flags);
+
+ for (i = 0; i < count; i++) {
+ do {
+ status = UART_GET_LSR(uart);
+ } while (!(status & THRE));
+
+ tmp = UART_GET_LCR(uart);
+ tmp &= ~DLAB;
+ UART_PUT_LCR(uart, tmp);
+
+ UART_PUT_CHAR(uart, s[i]);
+ if (s[i] == '\n') {
+ do {
+ status = UART_GET_LSR(uart);
+ } while(!(status & THRE));
+ UART_PUT_CHAR(uart, '\r');
+ }
+ }
+
+ local_irq_restore(flags);
+}
+
+/*
+ * If the port was already initialised (eg, by a boot loader),
+ * try to determine the current setup.
+ */
+static void __init
+bfin_serial_console_get_options(struct bfin_serial_port *uart, int *baud,
+ int *parity, int *bits)
+{
+ unsigned short status;
+
+ status = UART_GET_IER(uart) & (ERBFI | ETBEI);
+ if (status == (ERBFI | ETBEI)) {
+ /* ok, the port was enabled */
+ unsigned short lcr, val;
+ unsigned short dlh, dll;
+
+ lcr = UART_GET_LCR(uart);
+
+ *parity = 'n';
+ if (lcr & PEN) {
+ if (lcr & EPS)
+ *parity = 'e';
+ else
+ *parity = 'o';
+ }
+ switch (lcr & 0x03) {
+ case 0: *bits = 5; break;
+ case 1: *bits = 6; break;
+ case 2: *bits = 7; break;
+ case 3: *bits = 8; break;
+ }
+ /* Set DLAB in LCR to Access DLL and DLH */
+ val = UART_GET_LCR(uart);
+ val |= DLAB;
+ UART_PUT_LCR(uart, val);
+
+ dll = UART_GET_DLL(uart);
+ dlh = UART_GET_DLH(uart);
+
+ /* Clear DLAB in LCR to Access THR RBR IER */
+ val = UART_GET_LCR(uart);
+ val &= ~DLAB;
+ UART_PUT_LCR(uart, val);
+
+ *baud = get_sclk()/(16*(dll|dlh<<8));
+ }
+ DPRINTK("%s:baud = %d, parity = %c, bits= %d\n", __FUNCTION__,
*baud, *parity, *bits);
+}
+
+static int __init
+bfin_serial_console_setup(struct console *co, char *options)
+{
+ struct bfin_serial_port *uart;
+ int baud = CONSOLE_BAUD_RATE;
+ int bits = 8;
+ int parity = 'n';
+#ifdef CONFIG_SERIAL_BFIN_CTSRTS
+ int flow = 'r';
+#else
+ int flow = 'n';
+#endif
+
+ /*
+ * Check whether an invalid uart number has been specified, and
+ * if so, search for the first available port that does have
+ * console support.
+ */
+ if (co->index == -1 || co->index >= NR_PORTS)
+ co->index = 0;
+ uart = &bfin_serial_ports[co->index];
+
+ if (options)
+ uart_parse_options(options, &baud, &parity, &bits, &flow);
+ else
+ bfin_serial_console_get_options(uart, &baud, &parity, &bits);
+
+ return uart_set_options(&uart->port, co, baud, parity, bits, flow);
+}
+
+static struct uart_driver bfin_serial_reg;
+static struct console bfin_serial_console = {
+ .name = "ttyS",
+ .write = bfin_serial_console_write,
+ .device = uart_console_device,
+ .setup = bfin_serial_console_setup,
+ .flags = CON_PRINTBUFFER,
+ .index = -1,
+ .data = &bfin_serial_reg,
+};
+
+static int __init bfin_serial_rs_console_init(void)
+{
+ bfin_serial_init_ports();
+ register_console(&bfin_serial_console);
+ return 0;
+}
+console_initcall(bfin_serial_rs_console_init);
+
+#define BFIN_SERIAL_CONSOLE &bfin_serial_console
+#else
+#define BFIN_SERIAL_CONSOLE NULL
+#endif
+
+static struct uart_driver bfin_serial_reg = {
+ .owner = THIS_MODULE,
+ .driver_name = "bfin-uart",
+ .dev_name = "ttyS",
+ .major = SERIAL_BFIN_MAJOR,
+ .minor = MINOR_START,
+ .nr = NR_PORTS,
+ .cons = BFIN_SERIAL_CONSOLE,
+};
+
+static int bfin_serial_suspend(struct platform_device *dev, pm_message_t state)
+{
+ struct bfin_serial_port *uart = platform_get_drvdata(dev);
+
+ if (uart)
+ uart_suspend_port(&bfin_serial_reg, &uart->port);
+
+ return 0;
+}
+
+static int bfin_serial_resume(struct platform_device *dev)
+{
+ struct bfin_serial_port *uart = platform_get_drvdata(dev);
+
+ if (uart)
+ uart_resume_port(&bfin_serial_reg, &uart->port);
+
+ return 0;
+}
+
+static int bfin_serial_probe(struct platform_device *dev)
+{
+ struct resource *res = dev->resource;
+ int i;
+
+ for (i = 0; i < dev->num_resources; i++, res++)
+ if (res->flags & IORESOURCE_MEM)
+ break;
+
+ if (i < dev->num_resources) {
+ for (i = 0; i < NR_PORTS; i++, res++) {
+ if (bfin_serial_ports[i].port.mapbase != res->start)
+ continue;
+ bfin_serial_ports[i].port.dev = &dev->dev;
+ uart_add_one_port(&bfin_serial_reg, &bfin_serial_ports[i].port);
+ platform_set_drvdata(dev, &bfin_serial_ports[i]);
+ }
+ }
+
+ return 0;
+}
+
+static int bfin_serial_remove(struct platform_device *pdev)
+{
+ struct bfin_serial_port *uart = platform_get_drvdata(pdev);
+
+ platform_set_drvdata(pdev, NULL);
+
+ if (uart)
+ uart_remove_one_port(&bfin_serial_reg, &uart->port);
+
+ return 0;
+}
+
+static struct platform_driver bfin_serial_driver = {
+ .probe = bfin_serial_probe,
+ .remove = bfin_serial_remove,
+ .suspend = bfin_serial_suspend,
+ .resume = bfin_serial_resume,
+ .driver = {
+ .name = "bfin-uart",
+ },
+};
+
+static int __init bfin_serial_init(void)
+{
+ int ret;
+
+ printk(KERN_INFO "Serial: Blackfin serial driver\n");
+
+ bfin_serial_init_ports();
+
+ ret = uart_register_driver(&bfin_serial_reg);
+ if (ret == 0) {
+ ret = platform_driver_register(&bfin_serial_driver);
+ if (ret) {
+ DPRINTK("uart register failed\n");
+ uart_unregister_driver(&bfin_serial_reg);
+ }
+ }
+ return ret;
+}
+
+static void __exit bfin_serial_exit(void)
+{
+ platform_driver_unregister(&bfin_serial_driver);
+ uart_unregister_driver(&bfin_serial_reg);
+}
+
+module_init(bfin_serial_init);
+module_exit(bfin_serial_exit);
+
+MODULE_AUTHOR("Aubrey.Li <[email protected]>");
+MODULE_DESCRIPTION("Blackfin generic serial port driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_CHARDEV_MAJOR(SERIAL_BFIN_MAJOR);
diff -urN linux-2.6.18.patch1/include/linux/serial_core.h
linux-2.6.18.patch2/include/linux/serial_core.h
--- linux-2.6.18.patch1/include/linux/serial_core.h 2006-09-21
09:14:54.000000000 +0800
+++ linux-2.6.18.patch2/include/linux/serial_core.h 2006-09-21
09:38:17.000000000 +0800
@@ -132,6 +132,9 @@

#define PORT_S3C2412 73

+/* Blackfin bf5xx */
+#define PORT_BFIN 74
+

#ifdef __KERNEL__


On 9/21/06, Aubrey <[email protected]> wrote:
> On 9/21/06, Randy.Dunlap <[email protected]> wrote:
> > On Thu, 21 Sep 2006 11:33:05 +0800 Luke Yang wrote:
> >
> > > This is the serial driver for Blackfin. It is designed for the serial
> > > core framework.
> > >
> > > As to other drivers, I'll send them one by one later.
> > >
> > > Signed-off-by: Luke Yang <[email protected]>
> > >
> > > drivers/serial/Kconfig | 35 +
> > > drivers/serial/Makefile | 3
> > > drivers/serial/bfin_5xx.c | 903 ++++++++++++++++++++++++++++++++++++++++++++
> > > include/linux/serial_core.h | 3
> > > 4 files changed, 943 insertions(+), 1 deletion(-)
> > >
> > > diff -urN linux-2.6.18.patch1/drivers/serial/Kconfig
> > > linux-2.6.18.patch2/drivers/serial/Kconfig
> > > --- linux-2.6.18.patch1/drivers/serial/Kconfig 2006-09-21
> > > 09:14:42.000000000 +0800
> > > +++ linux-2.6.18.patch2/drivers/serial/Kconfig 2006-09-21
> > > 09:38:17.000000000 +0800
> > > @@ -488,6 +488,41 @@
> > > your boot loader (lilo or loadlin) about how to pass options to the
> > > kernel at boot time.)
> > >
> > > +config SERIAL_BFIN
> > > + bool "Blackfin serial port support (EXPERIMENTAL)"
> > > + depends on BFIN && EXPERIMENTAL
> > > + select SERIAL_CORE
> >
> > Just curious: why bool and not tristate? (i.e., why is loadable
> > module not allowed?)
>
> Thanks to point it out, this will be changed in the new patch.
>
> >
> > > +config SERIAL_BFIN_CONSOLE
> > > + bool "Console on Blackfin serial port"
> > > + depends on SERIAL_BFIN
> > > + select SERIAL_CORE_CONSOLE
> > > +
> > > +choice
> > > + prompt "Blackfin UART Mode"
> > > + depends on SERIAL_BFIN
> > > + default SERIAL_BFIN_DMA
> > > + ---help---
> > > + This driver supports the built-in serial ports of the
> > > Blackfin family of CPUs
> > > +
> > > +config SERIAL_BFIN_DMA
> > > + bool "Blackfin UART DMA mode"
> > > + depends on DMA_UNCACHED_1M
> > > + help
> > > + This driver works under DMA mode. If this option is
> > > selected, the blackfin simple dma driver is also enabled.
> >
> > Please break that long line at < 80 columns (so that left-right
> > scrolling is not required to read it in menuconfig).
>
> It will be corrected in the new patch.
>
> >
> > > +config SERIAL_BFIN_PIO
> > > + bool "Blackfin UART PIO mode"
> > > + help
> > > + This driver works under PIO mode.
> > > +endchoice
> > > +
> > > +config SERIAL_BFIN_CTSRTS
> > > + bool "Enable hardware flow control"
> > > + depends on SERIAL_BFIN
> > > + help
> > > + Enable hardware flow control in the driver. Using GPIO emulate the
> > > CTS/RTS signal.
> >
> > Split the long help text into 2 lines.
> >
> > > config SERIAL_IMX
> > > bool "IMX serial port support"
> > > depends on ARM && ARCH_IMX
> >
> > > diff -urN linux-2.6.18.patch1/drivers/serial/Makefile
> > > linux-2.6.18.patch2/drivers/serial/Makefile
> > > --- linux-2.6.18.patch1/drivers/serial/Makefile 2006-09-21
> > > 09:14:42.000000000 +0800
> > > +++ linux-2.6.18.patch2/drivers/serial/Makefile 2006-09-21
> > > 09:38:17.000000000 +0800
> > > @@ -55,4 +56,4 @@
> > > obj-$(CONFIG_SERIAL_SGI_IOC4) += ioc4_serial.o
> > > obj-$(CONFIG_SERIAL_SGI_IOC3) += ioc3_serial.o
> > > obj-$(CONFIG_SERIAL_AT91) += at91_serial.o
> > > -obj-$(CONFIG_SERIAL_NETX) += netx-serial.o
> > > +obj-$(CONFIG_SERIAL_NETX) += netx-serial.o
> > > \ No newline at end of file
> >
> > What is the purpose of the change above?
>
> This shouldn't be changed, should be excluded in the patch.
> >
> > > diff -urN linux-2.6.18.patch1/drivers/serial/bfin_5xx.c
> > > linux-2.6.18.patch2/drivers/serial/bfin_5xx.c
> > > --- linux-2.6.18.patch1/drivers/serial/bfin_5xx.c 1970-01-01
> > > 08:00:00.000000000 +0800
> > > +++ linux-2.6.18.patch2/drivers/serial/bfin_5xx.c 2006-09-21
> > > 09:38:17.000000000 +0800
> > > @@ -0,0 +1,903 @@
> > > +
> > > +#include <linux/config.h>
> >
> > Don't include the config.h header file. That's done automatically
> > by the build system.
>
> The driver is based on the current serial driver sa1100.c. But yes,
> I'll remove it in the new patch.
>
> >
> > > +static irqreturn_t bfin_serial_int(int irq, void *dev_id, struct pt_regs *regs)
> > > +{
> > > + struct bfin_serial_port *uart = dev_id;
> > > + unsigned short status;
> > > +
> > > + spin_lock(&uart->port.lock);
> > > + status = UART_GET_IIR(uart);
> > > + do {
> > > + if ((status & IIR_STATUS) == IIR_TX_READY)
> > > + bfin_serial_tx_chars(uart);
> > > + if ((status & IIR_STATUS) == IIR_RX_READY)
> > > + bfin_serial_rx_chars(uart, regs);
> > > + status = UART_GET_IIR(uart);
> > > + } while (status &(IIR_TX_READY | IIR_RX_READY));
> > > + spin_unlock(&uart->port.lock);
> > > + return IRQ_HANDLED;
> >
> > So, the interrupt is requested as Shared, but then the int. handler
> > code (above here) does not check to see if the interrupt was
> > for this device. Shouldn't it do that and then return IRQ_NONE
> > if it wasn't for this device?
> >
>
> IMHO, I don't think it's necessary. Because it's not possble that the
> interrupt occurs from a device and the handler is called by another
> one.
>
> > > +}
> > > + bfin_serial_mctrl_check(uart);
> > > +}
> > > +
> > > +#endif
> > > +
> > > +#ifdef CONFIG_SERIAL_BFIN_DMA
> > > +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->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;
> > > + }
> > > +
> > > + local_irq_save(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;
> >
> > odd indentation above.
>
> Please comments the attachment on all coding style issues
>
> >
> > > + blackfin_dcache_flush_range((unsigned long)(xmit->buf+xmit->tail),
> > > + (unsigned long)(xmit->buf+xmit->tail+uart->tx_count));
> > > + set_dma_config(uart->tx_dma_channel,
> > > + set_bfin_dma_config(DIR_READ, DMA_FLOW_STOP,
> > > + INTR_ON_BUF,
> > > + DIMENSION_LINEAR,
> > > + DATA_SIZE_8));
> > > + set_dma_start_addr(uart->tx_dma_channel, (unsigned
> > > long)(xmit->buf+xmit->tail));
> > > + set_dma_x_count(uart->tx_dma_channel, uart->tx_count);
> > > + set_dma_x_modify(uart->tx_dma_channel, 1);
> > > + enable_dma(uart->tx_dma_channel);
> > > + ier = UART_GET_IER(uart);
> > > + ier |= ETBEI;
> > > + UART_PUT_IER(uart, ier);
> > > + local_irq_restore(flags);
> > > +}
> > > +
> >
> > > +static irqreturn_t bfin_serial_dma_tx_int(int irq, void *dev_id,
> > > struct pt_regs *regs)
> >
> > "struct" line above is a separate line but does not have a
> > beginning '+' mark, so the patch is malformed/corrupted.
> > This happened in a few other places also, so something is
> > breaking/splitting lines badly for us. :(
> >
> > > +{
> > > + 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);
> > > + ier = UART_GET_IER(uart);
> > > + ier &= ~ETBEI;
> > > + UART_PUT_IER(uart, ier);
> > > + 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);
> > > +
> > > + if (uart_circ_empty(xmit))
> > > + bfin_serial_stop_tx(&uart->port);
> > > + uart->tx_done = 1;
> > > + }
> > > +
> > > + spin_unlock(&uart->port.lock);
> > > + return IRQ_HANDLED;
> > > +}
> > > +
> > > +static irqreturn_t bfin_serial_dma_rx_int(int irq, void *dev_id,
> > > struct pt_regs *regs)
> > > +{
> > > + 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);
> > > + return IRQ_HANDLED;
> > > +}
> > > +#endif
> >
> > > +static unsigned int bfin_serial_get_mctrl(struct uart_port *port)
> > > +{
> > > +#ifdef CONFIG_SERIAL_BFIN_CTSRTS
> > > + if (bfin_read16(CTS_PORT) & (1<<CTS_PIN))
> > > + return TIOCM_DSR | TIOCM_CAR;
> > > + else
> > > +#endif
> > > + return TIOCM_CTS | TIOCM_DSR | TIOCM_CAR;
> >
> > Hardcoded return value, without reading a port, right?
>
> Right. It will be corrected in the new patch.
>
> >
> > > +}
> > > +
> > > +static void bfin_serial_set_mctrl(struct uart_port *port, unsigned int mctrl)
> > > +{
> > > +#ifdef CONFIG_SERIAL_BFIN_CTSRTS
> > > + if (mctrl & TIOCM_RTS)
> > > + bfin_write16(RTS_PORT, bfin_read16(RTS_PORT)&(~1<<RTS_PIN));
> > > + else
> > > + bfin_write16(RTS_PORT, bfin_read16(RTS_PORT)|(1<<RTS_PIN));
> > > +#endif
> > > +}
> >
> > > +int bfin_serial_startup(struct uart_port *port)
> > > +{
> > > + struct bfin_serial_port *uart = (struct bfin_serial_port *)port;
> > > +
> > > +#ifdef CONFIG_SERIAL_BFIN_DMA
> > > + dma_addr_t dma_handle;
> > > +
> > > + if (request_dma(uart->rx_dma_channel, "BFIN_UART_RX") < 0) {
> > > + printk(KERN_NOTICE "Unable to attach Blackfin UART RX DMA channel\n");
> > > + return -EBUSY;
> > > + } else
> > > + set_dma_callback(uart->rx_dma_channel, bfin_serial_dma_rx_int, uart);
> > > +
> > > + if (request_dma(uart->tx_dma_channel, "BFIN_UART_TX") < 0) {
> > > + printk(KERN_NOTICE "Unable to attach Blackfin UART TX DMA channel\n");
> >
> > Before returning, this failure path needs to free_dma() for the
> > first request_dma() that succeeded.
> > I would also suggest doing the set_dma_callback() calls after
> > both request_dma() calls have succeeded.
> >
>
> Good suggestion.
>
> > > + return -EBUSY;
> > > + } else
> > > + set_dma_callback(uart->tx_dma_channel, bfin_serial_dma_tx_int, uart);
> > > +
> > > + uart->rx_dma_buf.buf = (unsigned char *)dma_alloc_coherent(NULL,
> > > PAGE_SIZE, &dma_handle, GFP_DMA);
> >
> > bad line split.
> >
> > > + uart->rx_dma_buf.head = 0;
> > > + uart->rx_dma_buf.tail = 0;
> > > + uart->rx_dma_nrows = 0;
> > > +
> > > + set_dma_config(uart->rx_dma_channel,
> > > + set_bfin_dma_config(DIR_WRITE, DMA_FLOW_AUTO,
> > > + INTR_ON_ROW, DIMENSION_2D,
> > > + DATA_SIZE_8));
> > > + set_dma_x_count(uart->rx_dma_channel, DMA_RX_XCOUNT);
> > > + set_dma_x_modify(uart->rx_dma_channel, 1);
> > > + set_dma_y_count(uart->rx_dma_channel, DMA_RX_YCOUNT);
> > > + set_dma_y_modify(uart->rx_dma_channel, 1);
> > > + set_dma_start_addr(uart->rx_dma_channel, (unsigned long)uart->rx_dma_buf.buf);
> >
> > ditto
> >
> > > + enable_dma(uart->rx_dma_channel);
> > > +
> > > + uart->rx_dma_timer.data = (unsigned long)(uart);
> > > + uart->rx_dma_timer.function = (void *)bfin_serial_rx_dma_timeout;
> > > + uart->rx_dma_timer.expires = jiffies + DMA_RX_FLUSH_JIFFIES;
> > > + add_timer(&(uart->rx_dma_timer));
> > > +#else
> > > + if (request_irq
> > > + (uart->port.irq, bfin_serial_int, SA_INTERRUPT | SA_SHIRQ,
> >
> > The request_irq() parameters have changed a bit recently.
> > SA_SHIRQ is now IRQF_SHARED and SA_INTERRUPT is IRQF_DISABLED.
> > Please change to use the new interface.
> > It is documented in Documentation/DocBook/genericirq*
> >
>
> I'll change it.
>
> > > + "BFIN_UART0_RX", uart)) {
> > > + printk(KERN_NOTICE "Unable to attach BlackFin UART RX interrupt\n");
> > > + return -EBUSY;
> > > + }
> > > +
> > > + if (request_irq
> > > + (uart->port.irq+1, bfin_serial_int, SA_INTERRUPT | SA_SHIRQ,
> > > + "BFIN_UART0_TX", uart)) {
> > > + printk(KERN_NOTICE "Unable to attach BlackFin UART TX interrupt\n");
> >
> > This second request_irq() failure needs to call free_irq() for the
> > first request_irq() that succeeded...
> >
>
> Yeah, will change it.
>
> > > + return -EBUSY;
> > > + }
> > > +#endif
> > > + UART_PUT_IER(uart, UART_GET_IER(uart) | ERBFI);
> > > + return 0;
> > > +}
> >
> > > +static int bfin_serial_calc_baud(unsigned int uartclk)
> > > +{
> > > + int baud;
> > > + baud = get_sclk()/(uartclk*8);
> >
> > Throw a few spaces in there, like so:
> > baud = get_sclk() / (uartclk * 8);
> >
> > > + if ((baud & 0x1) == 1) {
> > > + baud++;
> > > + }
> > > + return baud/2;
> > > +}
> >
>
> It will be fixed.
>
> Thanks for your comments.
> -Aubrey
>


--
Best regards,
Luke Yang
[email protected]


Attachments:
(No filename) (40.95 kB)
blackfin_serial_drv_2.6.18.patch (26.46 kB)
Download all attachments

2006-09-21 09:02:14

by Alan

[permalink] [raw]
Subject: Re: [PATCH 2/4] Blackfin: Serial driver for Blackfin arch on 2.6.18

Ar Iau, 2006-09-21 am 11:33 +0800, ysgrifennodd Luke Yang:
> Hi,
>
> This is the serial driver for Blackfin. It is designed for the serial
> core framework.

> +#define DMA_RX_XCOUNT TTY_FLIPBUF_SIZE

TTY_FLIPBUF_SIZE is going away. Just pick a value good for your hardware
and under PAGE_SIZE.

Other question - is your locking ok for low latency. In low latency mode
tty_flip_buffer_push() may directly end up calling your write methods.

Otherwise looks good

Alan

2006-09-21 09:01:56

by Luke Yang

[permalink] [raw]
Subject: Re: [PATCH 2/4] Blackfin: Serial driver for Blackfin arch on 2.6.18

Some more fixes in the serial driver. new patch here.

drivers/serial/Kconfig | 44 ++
drivers/serial/Makefile | 1
drivers/serial/bfin_5xx.c | 906 ++++++++++++++++++++++++++++++++++++++++++++
include/linux/serial_core.h | 3
4 files changed, 954 insertions(+)

diff -urN linux-2.6.18.patch1/drivers/serial/Kconfig
linux-2.6.18.patch2/drivers/serial/Kconfig
--- linux-2.6.18.patch1/drivers/serial/Kconfig 2006-09-21
09:14:42.000000000 +0800
+++ linux-2.6.18.patch2/drivers/serial/Kconfig 2006-09-21
16:17:50.000000000 +0800
@@ -488,6 +488,50 @@
your boot loader (lilo or loadlin) about how to pass options to the
kernel at boot time.)

+config SERIAL_BFIN
+ tristate "Blackfin serial port support (EXPERIMENTAL)"
+ depends on BFIN && EXPERIMENTAL
+ select SERIAL_CORE
+ help
+ Add support for the built-in UARTs on the Blackfin.
+
+ To compile this driver as a module, choose M here: the
+ module will be called bfin_5xx.
+
+config SERIAL_BFIN_CONSOLE
+ bool "Console on Blackfin serial port"
+ depends on SERIAL_BFIN
+ select SERIAL_CORE_CONSOLE
+
+choice
+ prompt "Blackfin UART Mode"
+ depends on SERIAL_BFIN
+ default SERIAL_BFIN_DMA
+ help
+ This driver supports the built-in serial ports of the Blackfin family
+ of CPUs
+
+config SERIAL_BFIN_DMA
+ bool "Blackfin UART DMA mode"
+ depends on DMA_UNCACHED_1M
+ help
+ This driver works under DMA mode. If this option is selected, the
+ blackfin simple dma driver is also enabled.
+
+config SERIAL_BFIN_PIO
+ bool "Blackfin UART PIO mode"
+ help
+ This driver works under PIO mode.
+
+endchoice
+
+config SERIAL_BFIN_CTSRTS
+ bool "Enable hardware flow control"
+ depends on SERIAL_BFIN
+ help
+ Enable hardware flow control in the driver. Using GPIO emulate the CTS/RTS
+ signal.
+
config SERIAL_IMX
bool "IMX serial port support"
depends on ARM && ARCH_IMX
diff -urN linux-2.6.18.patch1/drivers/serial/Makefile
linux-2.6.18.patch2/drivers/serial/Makefile
--- linux-2.6.18.patch1/drivers/serial/Makefile 2006-09-21
09:14:42.000000000 +0800
+++ linux-2.6.18.patch2/drivers/serial/Makefile 2006-09-21
15:54:13.000000000 +0800
@@ -25,6 +25,7 @@
obj-$(CONFIG_SERIAL_CLPS711X) += clps711x.o
obj-$(CONFIG_SERIAL_PXA) += pxa.o
obj-$(CONFIG_SERIAL_SA1100) += sa1100.o
+obj-$(CONFIG_SERIAL_BFIN) += bfin_5xx.o
obj-$(CONFIG_SERIAL_S3C2410) += s3c2410.o
obj-$(CONFIG_SERIAL_SUNCORE) += suncore.o
obj-$(CONFIG_SERIAL_SUNHV) += sunhv.o
diff -urN linux-2.6.18.patch1/drivers/serial/bfin_5xx.c
linux-2.6.18.patch2/drivers/serial/bfin_5xx.c
--- linux-2.6.18.patch1/drivers/serial/bfin_5xx.c 1970-01-01
08:00:00.000000000 +0800
+++ linux-2.6.18.patch2/drivers/serial/bfin_5xx.c 2006-09-21
16:54:46.000000000 +0800
@@ -0,0 +1,906 @@
+/*
+ * File: drivers/serial/bfin_5xx.c
+ * Based on: Based on drivers/serial/sa1100.c
+ * Author: Aubrey Li <[email protected]>
+ *
+ * Created:
+ * Description: Driver for blackfin 5xx serial ports
+ *
+ * Rev: $Id: bfin_5xx.c,v 1.12 2006/09/04 04:44:27 aubrey Exp $
+ *
+ * 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>
+
+#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
+
+/* We've been assigned a range on the "Low-density serial ports" major */
+#define SERIAL_BFIN_MAJOR TTY_MAJOR
+#define MINOR_START 64
+
+#define DEBUG
+
+#ifdef DEBUG
+# define DPRINTK(x...) printk(KERN_DEBUG x)
+#else
+# define DPRINTK(x...) do { } while (0)
+#endif
+
+/*
+ * Setup for console. Argument comes from the menuconfig
+ */
+
+#if defined(CONFIG_BAUD_9600)
+#define CONSOLE_BAUD_RATE 9600
+#elif defined(CONFIG_BAUD_19200)
+#define CONSOLE_BAUD_RATE 19200
+#elif defined(CONFIG_BAUD_38400)
+#define CONSOLE_BAUD_RATE 38400
+#elif defined(CONFIG_BAUD_57600)
+#define CONSOLE_BAUD_RATE 57600
+#elif defined(CONFIG_BAUD_115200)
+#define CONSOLE_BAUD_RATE 115200
+#endif
+
+#define DMA_RX_XCOUNT TTY_FLIPBUF_SIZE
+#define DMA_RX_YCOUNT (PAGE_SIZE / DMA_RX_XCOUNT)
+
+#define DMA_RX_FLUSH_JIFFIES 5
+
+#ifdef CONFIG_SERIAL_BFIN_DMA
+wait_queue_head_t bfin_serial_tx_queue[NR_PORTS];
+static void bfin_serial_dma_tx_chars(struct bfin_serial_port *uart);
+#else
+static void bfin_serial_do_work(void *);
+static void bfin_serial_tx_chars(struct bfin_serial_port *uart);
+static void local_put_char(struct bfin_serial_port *uart, char ch);
+#endif
+
+static void bfin_serial_mctrl_check(struct bfin_serial_port *uart);
+
+/*
+ * interrupts disabled on entry
+ */
+static void bfin_serial_stop_tx(struct uart_port *port)
+{
+ struct bfin_serial_port *uart = (struct bfin_serial_port *)port;
+ unsigned short ier;
+ ier = UART_GET_IER(uart);
+ ier &= ~ETBEI;
+ UART_PUT_IER(uart, ier);
+#ifdef CONFIG_SERIAL_BFIN_DMA
+ disable_dma(uart->tx_dma_channel);
+#endif
+}
+
+/*
+ * port locked and interrupts 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
+ unsigned short ier;
+ ier = UART_GET_IER(uart);
+ ier |= ETBEI;
+ UART_PUT_IER(uart, ier);
+ bfin_serial_tx_chars(uart);
+#endif
+}
+
+/*
+ * Interrupts enabled
+ */
+static void bfin_serial_stop_rx(struct uart_port *port)
+{
+ struct bfin_serial_port *uart = (struct bfin_serial_port *)port;
+ unsigned short ier;
+ ier = UART_GET_IER(uart);
+ ier &= ERBFI;
+ UART_PUT_IER(uart, ier);
+}
+
+/*
+ * Set the modem control timer to fire immediately.
+ */
+static void bfin_serial_enable_ms(struct uart_port *port)
+{
+}
+
+#ifdef CONFIG_SERIAL_BFIN_PIO
+static void local_put_char(struct bfin_serial_port *uart, char ch)
+{
+ unsigned short status;
+ int flags = 0;
+
+ local_irq_save(flags);
+
+ do {
+ status = UART_GET_LSR(uart);
+ } while (!(status & THRE));
+
+ UART_PUT_CHAR(uart, ch);
+ local_irq_restore(flags);
+}
+
+static void
+bfin_serial_rx_chars(struct bfin_serial_port *uart, struct pt_regs *regs)
+{
+ struct tty_struct *tty = uart->port.info->tty;
+ unsigned int status=0, ch, flg;
+ ch = UART_GET_CHAR(uart);
+ uart->port.icount.rx++;
+ flg = TTY_NORMAL;
+ if (uart_handle_sysrq_char(&uart->port, ch, regs))
+ goto ignore_char;
+ uart_insert_char(&uart->port, status, 1, 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_int(int irq, void *dev_id, struct pt_regs *regs)
+{
+ struct bfin_serial_port *uart = dev_id;
+ unsigned short status;
+
+ spin_lock(&uart->port.lock);
+ status = UART_GET_IIR(uart);
+ do {
+ if ((status & IIR_STATUS) == IIR_TX_READY)
+ bfin_serial_tx_chars(uart);
+ if ((status & IIR_STATUS) == IIR_RX_READY)
+ bfin_serial_rx_chars(uart, regs);
+ status = UART_GET_IIR(uart);
+ } while (status &(IIR_TX_READY | IIR_RX_READY));
+ spin_unlock(&uart->port.lock);
+ return IRQ_HANDLED;
+}
+
+static void bfin_serial_do_work(void *port)
+{
+ struct bfin_serial_port *uart = (struct bfin_serial_port *)port;
+ bfin_serial_mctrl_check(uart);
+}
+
+#endif
+
+#ifdef CONFIG_SERIAL_BFIN_DMA
+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->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;
+ }
+
+ local_irq_save(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;
+ blackfin_dcache_flush_range((unsigned long)(xmit->buf+xmit->tail),
+ (unsigned long)(xmit->buf+xmit->tail+uart->tx_count));
+ set_dma_config(uart->tx_dma_channel,
+ set_bfin_dma_config(DIR_READ, DMA_FLOW_STOP,
+ INTR_ON_BUF,
+ DIMENSION_LINEAR,
+ DATA_SIZE_8));
+ set_dma_start_addr(uart->tx_dma_channel, (unsigned
long)(xmit->buf+xmit->tail));
+ set_dma_x_count(uart->tx_dma_channel, uart->tx_count);
+ set_dma_x_modify(uart->tx_dma_channel, 1);
+ enable_dma(uart->tx_dma_channel);
+ ier = UART_GET_IER(uart);
+ ier |= ETBEI;
+ UART_PUT_IER(uart, ier);
+ local_irq_restore(flags);
+}
+
+static void bfin_serial_dma_rx_chars(struct bfin_serial_port * uart)
+{
+ struct tty_struct *tty = uart->port.info->tty;
+ int i, flg, status = 0;
+
+ uart->port.icount.rx += CIRC_CNT(uart->rx_dma_buf.head,
uart->rx_dma_buf.tail, UART_XMIT_SIZE);;
+ 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], NULL))
+ goto dma_ignore_char;
+ uart_insert_char(&uart->port, status, 1, uart->rx_dma_buf.buf[i], flg);
+ }
+dma_ignore_char:
+ tty_flip_buffer_push(tty);
+}
+
+void bfin_serial_rx_dma_timeout(struct bfin_serial_port *uart)
+{
+ int x_pos, pos;
+ int flags = 0;
+
+ bfin_serial_dma_tx_chars(uart);
+
+ local_irq_save(flags);
+ x_pos = DMA_RX_XCOUNT - get_dma_curr_xcount(uart->rx_dma_channel);
+ 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;
+ bfin_serial_dma_rx_chars(uart);
+ uart->rx_dma_buf.head = uart->rx_dma_buf.tail;
+ }
+ local_irq_restore(flags);
+ uart->rx_dma_timer.expires = jiffies + DMA_RX_FLUSH_JIFFIES;
+ add_timer(&(uart->rx_dma_timer));
+}
+
+static irqreturn_t bfin_serial_dma_tx_int(int irq, void *dev_id,
struct pt_regs *regs)
+{
+ 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);
+ ier = UART_GET_IER(uart);
+ ier &= ~ETBEI;
+ UART_PUT_IER(uart, ier);
+ 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);
+
+ if (uart_circ_empty(xmit))
+ bfin_serial_stop_tx(&uart->port);
+ uart->tx_done = 1;
+ }
+
+ spin_unlock(&uart->port.lock);
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t bfin_serial_dma_rx_int(int irq, void *dev_id,
struct pt_regs *regs)
+{
+ 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);
+ return IRQ_HANDLED;
+}
+#endif
+
+/*
+ * Return TIOCSER_TEMT when transmitter is not busy.
+ */
+static unsigned int bfin_serial_tx_empty(struct uart_port *port)
+{
+ struct bfin_serial_port *uart = (struct bfin_serial_port *)port;
+ unsigned short lsr;
+ lsr = UART_GET_LSR(uart);
+ if (lsr & THRE)
+ return TIOCSER_TEMT;
+ else
+ return 0;
+}
+
+static unsigned int bfin_serial_get_mctrl(struct uart_port *port)
+{
+ /* Hardware flow control is only supported on the first port */
+#ifdef CONFIG_SERIAL_BFIN_CTSRTS
+ if ((bfin_read16(CTS_PORT) & (1 << CTS_PIN)) && (port->line == 0))
+ return TIOCM_DSR | TIOCM_CAR;
+ else
+#endif
+ return TIOCM_CTS | TIOCM_DSR | TIOCM_CAR;
+}
+
+static void bfin_serial_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+#ifdef CONFIG_SERIAL_BFIN_CTSRTS
+ if (mctrl & TIOCM_RTS)
+ bfin_write16(RTS_PORT, bfin_read16(RTS_PORT) & (~1 << RTS_PIN));
+ else
+ bfin_write16(RTS_PORT, bfin_read16(RTS_PORT) | (1 << RTS_PIN));
+#endif
+}
+
+/*
+ * Handle any change of modem status signal since we were last called.
+ */
+static void bfin_serial_mctrl_check(struct bfin_serial_port *uart)
+{
+#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);
+ if (!(status & TIOCM_CTS)) {
+ tty->hw_stopped = 1;
+ } 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
+}
+
+/*
+ * Interrupts always disabled.
+ */
+static void bfin_serial_break_ctl(struct uart_port *port, int break_state)
+{
+}
+
+int bfin_serial_startup(struct uart_port *port)
+{
+ struct bfin_serial_port *uart = (struct bfin_serial_port *)port;
+
+#ifdef CONFIG_SERIAL_BFIN_DMA
+ dma_addr_t dma_handle;
+
+ if (request_dma(uart->rx_dma_channel, "BFIN_UART_RX") < 0) {
+ printk(KERN_NOTICE "Unable to attach Blackfin UART RX DMA channel\n");
+ return -EBUSY;
+ }
+
+ if (request_dma(uart->tx_dma_channel, "BFIN_UART_TX") < 0) {
+ printk(KERN_NOTICE "Unable to attach Blackfin UART TX DMA channel\n");
+ free_dma(uart->rx_dma_channel);
+ return -EBUSY;
+ }
+
+ set_dma_callback(uart->rx_dma_channel, bfin_serial_dma_rx_int, uart);
+ set_dma_callback(uart->tx_dma_channel, bfin_serial_dma_tx_int, uart);
+
+ uart->rx_dma_buf.buf = (unsigned char *)dma_alloc_coherent(NULL,
PAGE_SIZE, &dma_handle, GFP_DMA);
+ uart->rx_dma_buf.head = 0;
+ uart->rx_dma_buf.tail = 0;
+ uart->rx_dma_nrows = 0;
+
+ set_dma_config(uart->rx_dma_channel,
+ set_bfin_dma_config(DIR_WRITE, DMA_FLOW_AUTO,
+ INTR_ON_ROW, DIMENSION_2D,
+ DATA_SIZE_8));
+ set_dma_x_count(uart->rx_dma_channel, DMA_RX_XCOUNT);
+ set_dma_x_modify(uart->rx_dma_channel, 1);
+ set_dma_y_count(uart->rx_dma_channel, DMA_RX_YCOUNT);
+ set_dma_y_modify(uart->rx_dma_channel, 1);
+ set_dma_start_addr(uart->rx_dma_channel, (unsigned long)uart->rx_dma_buf.buf);
+ enable_dma(uart->rx_dma_channel);
+
+ uart->rx_dma_timer.data = (unsigned long)(uart);
+ uart->rx_dma_timer.function = (void *)bfin_serial_rx_dma_timeout;
+ uart->rx_dma_timer.expires = jiffies + DMA_RX_FLUSH_JIFFIES;
+ add_timer(&(uart->rx_dma_timer));
+#else
+ if (request_irq
+ (uart->port.irq, bfin_serial_int, IRQF_DISABLED | IRQF_SHARED,
+ "BFIN_UART0_RX", uart)) {
+ printk(KERN_NOTICE "Unable to attach BlackFin UART RX interrupt\n");
+ return -EBUSY;
+ }
+
+ if (request_irq
+ (uart->port.irq+1, bfin_serial_int, IRQF_DISABLED | IRQF_SHARED,
+ "BFIN_UART0_TX", uart)) {
+ printk(KERN_NOTICE "Unable to attach BlackFin UART TX interrupt\n");
+ free_irq(uart->port.irq, uart);
+ return -EBUSY;
+ }
+#endif
+ UART_PUT_IER(uart, UART_GET_IER(uart) | ERBFI);
+ return 0;
+}
+
+static void bfin_serial_shutdown(struct uart_port *port)
+{
+ struct bfin_serial_port *uart = (struct bfin_serial_port *)port;
+
+#ifdef CONFIG_SERIAL_BFIN_DMA
+ disable_dma(uart->tx_dma_channel);
+ free_dma(uart->tx_dma_channel);
+ disable_dma(uart->rx_dma_channel);
+ free_dma(uart->rx_dma_channel);
+ del_timer(&(uart->rx_dma_timer));
+#else
+ free_irq(uart->port.irq, uart);
+ free_irq(uart->port.irq+1, uart);
+#endif
+}
+
+static void
+bfin_serial_set_termios(struct uart_port *port, struct termios *termios,
+ struct termios *old)
+{
+}
+
+static const char *bfin_serial_type(struct uart_port *port)
+{
+ struct bfin_serial_port *uart = (struct bfin_serial_port *)port;
+ return uart->port.type == PORT_BFIN ? "BFIN-UART" : NULL;
+}
+
+/*
+ * Release the memory region(s) being used by 'port'.
+ */
+static void bfin_serial_release_port(struct uart_port *port)
+{
+}
+
+/*
+ * Request the memory region(s) being used by 'port'.
+ */
+static int bfin_serial_request_port(struct uart_port *port)
+{
+ return 0;
+}
+
+/*
+ * Configure/autoconfigure the port.
+ */
+static void bfin_serial_config_port(struct uart_port *port, int flags)
+{
+ struct bfin_serial_port *uart = (struct bfin_serial_port *)port;
+
+ if (flags & UART_CONFIG_TYPE &&
+ bfin_serial_request_port(&uart->port) == 0)
+ uart->port.type = PORT_BFIN;
+}
+
+/*
+ * Verify the new serial_struct (for TIOCSSERIAL).
+ * The only change we allow are to the flags and type, and
+ * even then only between PORT_BFIN and PORT_UNKNOWN
+ */
+static int
+bfin_serial_verify_port(struct uart_port *port, struct serial_struct *ser)
+{
+ return 0;
+}
+
+static struct uart_ops bfin_serial_pops = {
+ .tx_empty = bfin_serial_tx_empty,
+ .set_mctrl = bfin_serial_set_mctrl,
+ .get_mctrl = bfin_serial_get_mctrl,
+ .stop_tx = bfin_serial_stop_tx,
+ .start_tx = bfin_serial_start_tx,
+ .stop_rx = bfin_serial_stop_rx,
+ .enable_ms = bfin_serial_enable_ms,
+ .break_ctl = bfin_serial_break_ctl,
+ .startup = bfin_serial_startup,
+ .shutdown = bfin_serial_shutdown,
+ .set_termios = bfin_serial_set_termios,
+ .type = bfin_serial_type,
+ .release_port = bfin_serial_release_port,
+ .request_port = bfin_serial_request_port,
+ .config_port = bfin_serial_config_port,
+ .verify_port = bfin_serial_verify_port,
+};
+
+static int bfin_serial_calc_baud(unsigned int uartclk)
+{
+ int baud;
+ baud = get_sclk() / (uartclk*8);
+ if ((baud & 0x1) == 1) {
+ baud++;
+ }
+ return baud/2;
+}
+
+static void __init bfin_serial_init_ports(void)
+{
+ static int first = 1;
+ int i;
+ unsigned short val;
+ int baud;
+
+ if (!first)
+ return;
+ first = 0;
+ bfin_serial_hw_init();
+
+ for (i = 0; i < NR_PORTS; i++) {
+ bfin_serial_ports[i].port.uartclk = CONSOLE_BAUD_RATE;
+ bfin_serial_ports[i].port.ops = &bfin_serial_pops;
+ bfin_serial_ports[i].port.line = i;
+ bfin_serial_ports[i].port.iotype = UPIO_MEM;
+ bfin_serial_ports[i].port.membase = (void __iomem *)uart_base_addr[i];
+ bfin_serial_ports[i].port.mapbase = uart_base_addr[i];
+ bfin_serial_ports[i].port.irq = uart_irq[i];
+ bfin_serial_ports[i].port.flags = UPF_BOOT_AUTOCONF;
+#ifdef CONFIG_SERIAL_BFIN_DMA
+ bfin_serial_ports[i].tx_done = 1;
+ bfin_serial_ports[i].tx_count = 0;
+ bfin_serial_ports[i].tx_dma_channel = uart_tx_dma_channel[i];
+ bfin_serial_ports[i].rx_dma_channel = uart_rx_dma_channel[i];
+
+ init_timer(&(bfin_serial_ports[i].rx_dma_timer));
+#else
+ INIT_WORK(&bfin_serial_ports[i].cts_workqueue, bfin_serial_do_work,
&bfin_serial_ports[i]);
+#endif
+
+ baud = bfin_serial_calc_baud(bfin_serial_ports[i].port.uartclk);
+
+ /* Enable UART */
+ val = UART_GET_GCTL(&bfin_serial_ports[i]);
+ val |= UCEN;
+ UART_PUT_GCTL(&bfin_serial_ports[i], val);
+
+ /* Set DLAB in LCR to Access DLL and DLH */
+ val = UART_GET_LCR(&bfin_serial_ports[i]);
+ val |= DLAB;
+ UART_PUT_LCR(&bfin_serial_ports[i], val);
+
+ UART_PUT_DLL(&bfin_serial_ports[i], baud & 0xFF);
+ UART_PUT_DLH(&bfin_serial_ports[i], (baud >> 8) & 0xFF);
+
+ /* Clear DLAB in LCR to Access THR RBR IER */
+ val = UART_GET_LCR(&bfin_serial_ports[i]);
+ val &= ~DLAB;
+ UART_PUT_LCR(&bfin_serial_ports[i], val);
+
+ /* Set LCR to Word Lengh 8-bit word select */
+ val = WLS(8);
+ UART_PUT_LCR(&bfin_serial_ports[i], val);
+ }
+}
+
+#ifdef CONFIG_SERIAL_BFIN_CONSOLE
+/*
+ * Interrupts are disabled on entering
+ */
+static void
+bfin_serial_console_write(struct console *co, const char *s, unsigned
int count)
+{
+ struct bfin_serial_port *uart = &bfin_serial_ports[co->index];
+ int flags = 0;
+ unsigned short status, tmp;
+ int i;
+
+ local_irq_save(flags);
+
+ for (i = 0; i < count; i++) {
+ do {
+ status = UART_GET_LSR(uart);
+ } while (!(status & THRE));
+
+ tmp = UART_GET_LCR(uart);
+ tmp &= ~DLAB;
+ UART_PUT_LCR(uart, tmp);
+
+ UART_PUT_CHAR(uart, s[i]);
+ if (s[i] == '\n') {
+ do {
+ status = UART_GET_LSR(uart);
+ } while(!(status & THRE));
+ UART_PUT_CHAR(uart, '\r');
+ }
+ }
+
+ local_irq_restore(flags);
+}
+
+/*
+ * If the port was already initialised (eg, by a boot loader),
+ * try to determine the current setup.
+ */
+static void __init
+bfin_serial_console_get_options(struct bfin_serial_port *uart, int *baud,
+ int *parity, int *bits)
+{
+ unsigned short status;
+
+ status = UART_GET_IER(uart) & (ERBFI | ETBEI);
+ if (status == (ERBFI | ETBEI)) {
+ /* ok, the port was enabled */
+ unsigned short lcr, val;
+ unsigned short dlh, dll;
+
+ lcr = UART_GET_LCR(uart);
+
+ *parity = 'n';
+ if (lcr & PEN) {
+ if (lcr & EPS)
+ *parity = 'e';
+ else
+ *parity = 'o';
+ }
+ switch (lcr & 0x03) {
+ case 0: *bits = 5; break;
+ case 1: *bits = 6; break;
+ case 2: *bits = 7; break;
+ case 3: *bits = 8; break;
+ }
+ /* Set DLAB in LCR to Access DLL and DLH */
+ val = UART_GET_LCR(uart);
+ val |= DLAB;
+ UART_PUT_LCR(uart, val);
+
+ dll = UART_GET_DLL(uart);
+ dlh = UART_GET_DLH(uart);
+
+ /* Clear DLAB in LCR to Access THR RBR IER */
+ val = UART_GET_LCR(uart);
+ val &= ~DLAB;
+ UART_PUT_LCR(uart, val);
+
+ *baud = get_sclk() / (16*(dll | dlh << 8));
+ }
+ DPRINTK("%s:baud = %d, parity = %c, bits= %d\n", __FUNCTION__,
*baud, *parity, *bits);
+}
+
+static int __init
+bfin_serial_console_setup(struct console *co, char *options)
+{
+ struct bfin_serial_port *uart;
+ int baud = CONSOLE_BAUD_RATE;
+ int bits = 8;
+ int parity = 'n';
+#ifdef CONFIG_SERIAL_BFIN_CTSRTS
+ int flow = 'r';
+#else
+ int flow = 'n';
+#endif
+
+ /*
+ * Check whether an invalid uart number has been specified, and
+ * if so, search for the first available port that does have
+ * console support.
+ */
+ if (co->index == -1 || co->index >= NR_PORTS)
+ co->index = 0;
+ uart = &bfin_serial_ports[co->index];
+
+ if (options)
+ uart_parse_options(options, &baud, &parity, &bits, &flow);
+ else
+ bfin_serial_console_get_options(uart, &baud, &parity, &bits);
+
+ return uart_set_options(&uart->port, co, baud, parity, bits, flow);
+}
+
+static struct uart_driver bfin_serial_reg;
+static struct console bfin_serial_console = {
+ .name = "ttyS",
+ .write = bfin_serial_console_write,
+ .device = uart_console_device,
+ .setup = bfin_serial_console_setup,
+ .flags = CON_PRINTBUFFER,
+ .index = -1,
+ .data = &bfin_serial_reg,
+};
+
+static int __init bfin_serial_rs_console_init(void)
+{
+ bfin_serial_init_ports();
+ register_console(&bfin_serial_console);
+ return 0;
+}
+console_initcall(bfin_serial_rs_console_init);
+
+#define BFIN_SERIAL_CONSOLE &bfin_serial_console
+#else
+#define BFIN_SERIAL_CONSOLE NULL
+#endif
+
+static struct uart_driver bfin_serial_reg = {
+ .owner = THIS_MODULE,
+ .driver_name = "bfin-uart",
+ .dev_name = "ttyS",
+ .devfs_name = "ttyS/",
+ .major = SERIAL_BFIN_MAJOR,
+ .minor = MINOR_START,
+ .nr = NR_PORTS,
+ .cons = BFIN_SERIAL_CONSOLE,
+};
+
+static int bfin_serial_suspend(struct platform_device *dev, pm_message_t state)
+{
+ struct bfin_serial_port *uart = platform_get_drvdata(dev);
+
+ if (uart)
+ uart_suspend_port(&bfin_serial_reg, &uart->port);
+
+ return 0;
+}
+
+static int bfin_serial_resume(struct platform_device *dev)
+{
+ struct bfin_serial_port *uart = platform_get_drvdata(dev);
+
+ if (uart)
+ uart_resume_port(&bfin_serial_reg, &uart->port);
+
+ return 0;
+}
+
+static int bfin_serial_probe(struct platform_device *dev)
+{
+ struct resource *res = dev->resource;
+ int i;
+
+ for (i = 0; i < dev->num_resources; i++, res++)
+ if (res->flags & IORESOURCE_MEM)
+ break;
+
+ if (i < dev->num_resources) {
+ for (i = 0; i < NR_PORTS; i++, res++) {
+ if (bfin_serial_ports[i].port.mapbase != res->start)
+ continue;
+ bfin_serial_ports[i].port.dev = &dev->dev;
+ uart_add_one_port(&bfin_serial_reg, &bfin_serial_ports[i].port);
+ platform_set_drvdata(dev, &bfin_serial_ports[i]);
+ }
+ }
+
+ return 0;
+}
+
+static int bfin_serial_remove(struct platform_device *pdev)
+{
+ struct bfin_serial_port *uart = platform_get_drvdata(pdev);
+
+ platform_set_drvdata(pdev, NULL);
+
+ if (uart)
+ uart_remove_one_port(&bfin_serial_reg, &uart->port);
+
+ return 0;
+}
+
+static struct platform_driver bfin_serial_driver = {
+ .probe = bfin_serial_probe,
+ .remove = bfin_serial_remove,
+ .suspend = bfin_serial_suspend,
+ .resume = bfin_serial_resume,
+ .driver = {
+ .name = "bfin-uart",
+ },
+};
+
+static int __init bfin_serial_init(void)
+{
+ int ret;
+
+ printk(KERN_INFO "Serial: Blackfin serial driver\n");
+
+ bfin_serial_init_ports();
+
+ ret = uart_register_driver(&bfin_serial_reg);
+ if (ret == 0) {
+ ret = platform_driver_register(&bfin_serial_driver);
+ if (ret) {
+ DPRINTK("uart register failed\n");
+ uart_unregister_driver(&bfin_serial_reg);
+ }
+ }
+ return ret;
+}
+
+static void __exit bfin_serial_exit(void)
+{
+ platform_driver_unregister(&bfin_serial_driver);
+ uart_unregister_driver(&bfin_serial_reg);
+}
+
+module_init(bfin_serial_init);
+module_exit(bfin_serial_exit);
+
+MODULE_AUTHOR("Aubrey.Li <[email protected]>");
+MODULE_DESCRIPTION("Blackfin generic serial port driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_CHARDEV_MAJOR(SERIAL_BFIN_MAJOR);
diff -urN linux-2.6.18.patch1/include/linux/serial_core.h
linux-2.6.18.patch2/include/linux/serial_core.h
--- linux-2.6.18.patch1/include/linux/serial_core.h 2006-09-21
09:14:54.000000000 +0800
+++ linux-2.6.18.patch2/include/linux/serial_core.h 2006-09-21
09:38:17.000000000 +0800
@@ -132,6 +132,9 @@

#define PORT_S3C2412 73

+/* Blackfin bf5xx */
+#define PORT_BFIN 74
+

#ifdef __KERNEL__



> On 9/21/06, Aubrey <[email protected]> wrote:
> > On 9/21/06, Randy.Dunlap <[email protected]> wrote:
> > > On Thu, 21 Sep 2006 11:33:05 +0800 Luke Yang wrote:
> > >
> > > > This is the serial driver for Blackfin. It is designed for the serial
> > > > core framework.
> > > >
> > > > As to other drivers, I'll send them one by one later.
> > > >
> > > > Signed-off-by: Luke Yang <[email protected]>
> > > >
> > > > drivers/serial/Kconfig | 35 +
> > > > drivers/serial/Makefile | 3
> > > > drivers/serial/bfin_5xx.c | 903 ++++++++++++++++++++++++++++++++++++++++++++
> > > > include/linux/serial_core.h | 3
> > > > 4 files changed, 943 insertions(+), 1 deletion(-)
> > > >
> > > > diff -urN linux-2.6.18.patch1/drivers/serial/Kconfig
> > > > linux-2.6.18.patch2/drivers/serial/Kconfig
> > > > --- linux-2.6.18.patch1/drivers/serial/Kconfig 2006-09-21
> > > > 09:14:42.000000000 +0800
> > > > +++ linux-2.6.18.patch2/drivers/serial/Kconfig 2006-09-21
> > > > 09:38:17.000000000 +0800
> > > > @@ -488,6 +488,41 @@
> > > > your boot loader (lilo or loadlin) about how to pass options to the
> > > > kernel at boot time.)
> > > >
> > > > +config SERIAL_BFIN
> > > > + bool "Blackfin serial port support (EXPERIMENTAL)"
> > > > + depends on BFIN && EXPERIMENTAL
> > > > + select SERIAL_CORE
> > >
> > > Just curious: why bool and not tristate? (i.e., why is loadable
> > > module not allowed?)
> >
> > Thanks to point it out, this will be changed in the new patch.
> >
> > >
> > > > +config SERIAL_BFIN_CONSOLE
> > > > + bool "Console on Blackfin serial port"
> > > > + depends on SERIAL_BFIN
> > > > + select SERIAL_CORE_CONSOLE
> > > > +
> > > > +choice
> > > > + prompt "Blackfin UART Mode"
> > > > + depends on SERIAL_BFIN
> > > > + default SERIAL_BFIN_DMA
> > > > + ---help---
> > > > + This driver supports the built-in serial ports of the
> > > > Blackfin family of CPUs
> > > > +
> > > > +config SERIAL_BFIN_DMA
> > > > + bool "Blackfin UART DMA mode"
> > > > + depends on DMA_UNCACHED_1M
> > > > + help
> > > > + This driver works under DMA mode. If this option is
> > > > selected, the blackfin simple dma driver is also enabled.
> > >
> > > Please break that long line at < 80 columns (so that left-right
> > > scrolling is not required to read it in menuconfig).
> >
> > It will be corrected in the new patch.
> >
> > >
> > > > +config SERIAL_BFIN_PIO
> > > > + bool "Blackfin UART PIO mode"
> > > > + help
> > > > + This driver works under PIO mode.
> > > > +endchoice
> > > > +
> > > > +config SERIAL_BFIN_CTSRTS
> > > > + bool "Enable hardware flow control"
> > > > + depends on SERIAL_BFIN
> > > > + help
> > > > + Enable hardware flow control in the driver. Using GPIO emulate the
> > > > CTS/RTS signal.
> > >
> > > Split the long help text into 2 lines.
> > >
> > > > config SERIAL_IMX
> > > > bool "IMX serial port support"
> > > > depends on ARM && ARCH_IMX
> > >
> > > > diff -urN linux-2.6.18.patch1/drivers/serial/Makefile
> > > > linux-2.6.18.patch2/drivers/serial/Makefile
> > > > --- linux-2.6.18.patch1/drivers/serial/Makefile 2006-09-21
> > > > 09:14:42.000000000 +0800
> > > > +++ linux-2.6.18.patch2/drivers/serial/Makefile 2006-09-21
> > > > 09:38:17.000000000 +0800
> > > > @@ -55,4 +56,4 @@
> > > > obj-$(CONFIG_SERIAL_SGI_IOC4) += ioc4_serial.o
> > > > obj-$(CONFIG_SERIAL_SGI_IOC3) += ioc3_serial.o
> > > > obj-$(CONFIG_SERIAL_AT91) += at91_serial.o
> > > > -obj-$(CONFIG_SERIAL_NETX) += netx-serial.o
> > > > +obj-$(CONFIG_SERIAL_NETX) += netx-serial.o
> > > > \ No newline at end of file
> > >
> > > What is the purpose of the change above?
> >
> > This shouldn't be changed, should be excluded in the patch.
> > >
> > > > diff -urN linux-2.6.18.patch1/drivers/serial/bfin_5xx.c
> > > > linux-2.6.18.patch2/drivers/serial/bfin_5xx.c
> > > > --- linux-2.6.18.patch1/drivers/serial/bfin_5xx.c 1970-01-01
> > > > 08:00:00.000000000 +0800
> > > > +++ linux-2.6.18.patch2/drivers/serial/bfin_5xx.c 2006-09-21
> > > > 09:38:17.000000000 +0800
> > > > @@ -0,0 +1,903 @@
> > > > +
> > > > +#include <linux/config.h>
> > >
> > > Don't include the config.h header file. That's done automatically
> > > by the build system.
> >
> > The driver is based on the current serial driver sa1100.c. But yes,
> > I'll remove it in the new patch.
> >
> > >
> > > > +static irqreturn_t bfin_serial_int(int irq, void *dev_id, struct pt_regs *regs)
> > > > +{
> > > > + struct bfin_serial_port *uart = dev_id;
> > > > + unsigned short status;
> > > > +
> > > > + spin_lock(&uart->port.lock);
> > > > + status = UART_GET_IIR(uart);
> > > > + do {
> > > > + if ((status & IIR_STATUS) == IIR_TX_READY)
> > > > + bfin_serial_tx_chars(uart);
> > > > + if ((status & IIR_STATUS) == IIR_RX_READY)
> > > > + bfin_serial_rx_chars(uart, regs);
> > > > + status = UART_GET_IIR(uart);
> > > > + } while (status &(IIR_TX_READY | IIR_RX_READY));
> > > > + spin_unlock(&uart->port.lock);
> > > > + return IRQ_HANDLED;
> > >
> > > So, the interrupt is requested as Shared, but then the int. handler
> > > code (above here) does not check to see if the interrupt was
> > > for this device. Shouldn't it do that and then return IRQ_NONE
> > > if it wasn't for this device?
> > >
> >
> > IMHO, I don't think it's necessary. Because it's not possble that the
> > interrupt occurs from a device and the handler is called by another
> > one.
> >
> > > > +}
> > > > + bfin_serial_mctrl_check(uart);
> > > > +}
> > > > +
> > > > +#endif
> > > > +
> > > > +#ifdef CONFIG_SERIAL_BFIN_DMA
> > > > +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->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;
> > > > + }
> > > > +
> > > > + local_irq_save(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;
> > >
> > > odd indentation above.
> >
> > Please comments the attachment on all coding style issues
> >
> > >
> > > > + blackfin_dcache_flush_range((unsigned long)(xmit->buf+xmit->tail),
> > > > + (unsigned long)(xmit->buf+xmit->tail+uart->tx_count));
> > > > + set_dma_config(uart->tx_dma_channel,
> > > > + set_bfin_dma_config(DIR_READ, DMA_FLOW_STOP,
> > > > + INTR_ON_BUF,
> > > > + DIMENSION_LINEAR,
> > > > + DATA_SIZE_8));
> > > > + set_dma_start_addr(uart->tx_dma_channel, (unsigned
> > > > long)(xmit->buf+xmit->tail));
> > > > + set_dma_x_count(uart->tx_dma_channel, uart->tx_count);
> > > > + set_dma_x_modify(uart->tx_dma_channel, 1);
> > > > + enable_dma(uart->tx_dma_channel);
> > > > + ier = UART_GET_IER(uart);
> > > > + ier |= ETBEI;
> > > > + UART_PUT_IER(uart, ier);
> > > > + local_irq_restore(flags);
> > > > +}
> > > > +
> > >
> > > > +static irqreturn_t bfin_serial_dma_tx_int(int irq, void *dev_id,
> > > > struct pt_regs *regs)
> > >
> > > "struct" line above is a separate line but does not have a
> > > beginning '+' mark, so the patch is malformed/corrupted.
> > > This happened in a few other places also, so something is
> > > breaking/splitting lines badly for us. :(
> > >
> > > > +{
> > > > + 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);
> > > > + ier = UART_GET_IER(uart);
> > > > + ier &= ~ETBEI;
> > > > + UART_PUT_IER(uart, ier);
> > > > + 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);
> > > > +
> > > > + if (uart_circ_empty(xmit))
> > > > + bfin_serial_stop_tx(&uart->port);
> > > > + uart->tx_done = 1;
> > > > + }
> > > > +
> > > > + spin_unlock(&uart->port.lock);
> > > > + return IRQ_HANDLED;
> > > > +}
> > > > +
> > > > +static irqreturn_t bfin_serial_dma_rx_int(int irq, void *dev_id,
> > > > struct pt_regs *regs)
> > > > +{
> > > > + 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);
> > > > + return IRQ_HANDLED;
> > > > +}
> > > > +#endif
> > >
> > > > +static unsigned int bfin_serial_get_mctrl(struct uart_port *port)
> > > > +{
> > > > +#ifdef CONFIG_SERIAL_BFIN_CTSRTS
> > > > + if (bfin_read16(CTS_PORT) & (1<<CTS_PIN))
> > > > + return TIOCM_DSR | TIOCM_CAR;
> > > > + else
> > > > +#endif
> > > > + return TIOCM_CTS | TIOCM_DSR | TIOCM_CAR;
> > >
> > > Hardcoded return value, without reading a port, right?
> >
> > Right. It will be corrected in the new patch.
> >
> > >
> > > > +}
> > > > +
> > > > +static void bfin_serial_set_mctrl(struct uart_port *port, unsigned int mctrl)
> > > > +{
> > > > +#ifdef CONFIG_SERIAL_BFIN_CTSRTS
> > > > + if (mctrl & TIOCM_RTS)
> > > > + bfin_write16(RTS_PORT, bfin_read16(RTS_PORT)&(~1<<RTS_PIN));
> > > > + else
> > > > + bfin_write16(RTS_PORT, bfin_read16(RTS_PORT)|(1<<RTS_PIN));
> > > > +#endif
> > > > +}
> > >
> > > > +int bfin_serial_startup(struct uart_port *port)
> > > > +{
> > > > + struct bfin_serial_port *uart = (struct bfin_serial_port *)port;
> > > > +
> > > > +#ifdef CONFIG_SERIAL_BFIN_DMA
> > > > + dma_addr_t dma_handle;
> > > > +
> > > > + if (request_dma(uart->rx_dma_channel, "BFIN_UART_RX") < 0) {
> > > > + printk(KERN_NOTICE "Unable to attach Blackfin UART RX DMA channel\n");
> > > > + return -EBUSY;
> > > > + } else
> > > > + set_dma_callback(uart->rx_dma_channel, bfin_serial_dma_rx_int, uart);
> > > > +
> > > > + if (request_dma(uart->tx_dma_channel, "BFIN_UART_TX") < 0) {
> > > > + printk(KERN_NOTICE "Unable to attach Blackfin UART TX DMA channel\n");
> > >
> > > Before returning, this failure path needs to free_dma() for the
> > > first request_dma() that succeeded.
> > > I would also suggest doing the set_dma_callback() calls after
> > > both request_dma() calls have succeeded.
> > >
> >
> > Good suggestion.
> >
> > > > + return -EBUSY;
> > > > + } else
> > > > + set_dma_callback(uart->tx_dma_channel, bfin_serial_dma_tx_int, uart);
> > > > +
> > > > + uart->rx_dma_buf.buf = (unsigned char *)dma_alloc_coherent(NULL,
> > > > PAGE_SIZE, &dma_handle, GFP_DMA);
> > >
> > > bad line split.
> > >
> > > > + uart->rx_dma_buf.head = 0;
> > > > + uart->rx_dma_buf.tail = 0;
> > > > + uart->rx_dma_nrows = 0;
> > > > +
> > > > + set_dma_config(uart->rx_dma_channel,
> > > > + set_bfin_dma_config(DIR_WRITE, DMA_FLOW_AUTO,
> > > > + INTR_ON_ROW, DIMENSION_2D,
> > > > + DATA_SIZE_8));
> > > > + set_dma_x_count(uart->rx_dma_channel, DMA_RX_XCOUNT);
> > > > + set_dma_x_modify(uart->rx_dma_channel, 1);
> > > > + set_dma_y_count(uart->rx_dma_channel, DMA_RX_YCOUNT);
> > > > + set_dma_y_modify(uart->rx_dma_channel, 1);
> > > > + set_dma_start_addr(uart->rx_dma_channel, (unsigned long)uart->rx_dma_buf.buf);
> > >
> > > ditto
> > >
> > > > + enable_dma(uart->rx_dma_channel);
> > > > +
> > > > + uart->rx_dma_timer.data = (unsigned long)(uart);
> > > > + uart->rx_dma_timer.function = (void *)bfin_serial_rx_dma_timeout;
> > > > + uart->rx_dma_timer.expires = jiffies + DMA_RX_FLUSH_JIFFIES;
> > > > + add_timer(&(uart->rx_dma_timer));
> > > > +#else
> > > > + if (request_irq
> > > > + (uart->port.irq, bfin_serial_int, SA_INTERRUPT | SA_SHIRQ,
> > >
> > > The request_irq() parameters have changed a bit recently.
> > > SA_SHIRQ is now IRQF_SHARED and SA_INTERRUPT is IRQF_DISABLED.
> > > Please change to use the new interface.
> > > It is documented in Documentation/DocBook/genericirq*
> > >
> >
> > I'll change it.
> >
> > > > + "BFIN_UART0_RX", uart)) {
> > > > + printk(KERN_NOTICE "Unable to attach BlackFin UART RX interrupt\n");
> > > > + return -EBUSY;
> > > > + }
> > > > +
> > > > + if (request_irq
> > > > + (uart->port.irq+1, bfin_serial_int, SA_INTERRUPT | SA_SHIRQ,
> > > > + "BFIN_UART0_TX", uart)) {
> > > > + printk(KERN_NOTICE "Unable to attach BlackFin UART TX interrupt\n");
> > >
> > > This second request_irq() failure needs to call free_irq() for the
> > > first request_irq() that succeeded...
> > >
> >
> > Yeah, will change it.
> >
> > > > + return -EBUSY;
> > > > + }
> > > > +#endif
> > > > + UART_PUT_IER(uart, UART_GET_IER(uart) | ERBFI);
> > > > + return 0;
> > > > +}
> > >
> > > > +static int bfin_serial_calc_baud(unsigned int uartclk)
> > > > +{
> > > > + int baud;
> > > > + baud = get_sclk()/(uartclk*8);
> > >
> > > Throw a few spaces in there, like so:
> > > baud = get_sclk() / (uartclk * 8);
> > >
> > > > + if ((baud & 0x1) == 1) {
> > > > + baud++;
> > > > + }
> > > > + return baud/2;
> > > > +}
> > >
> >
> > It will be fixed.
> >
> > Thanks for your comments.
> > -Aubrey
> >
>
>
> --
> Best regards,
> Luke Yang
> [email protected]
>
>
>


--
Best regards,
Luke Yang
[email protected]


Attachments:
(No filename) (41.98 kB)
blackfin_serial_drv_2.6.18.patch (26.62 kB)
Download all attachments

2006-09-21 09:23:54

by Aubrey Li

[permalink] [raw]
Subject: Re: [PATCH 2/4] Blackfin: Serial driver for Blackfin arch on 2.6.18

On 9/21/06, Alan Cox <[email protected]> wrote:
> Ar Iau, 2006-09-21 am 11:33 +0800, ysgrifennodd Luke Yang:
> > Hi,
> >
> > This is the serial driver for Blackfin. It is designed for the serial
> > core framework.
>
> > +#define DMA_RX_XCOUNT TTY_FLIPBUF_SIZE
>
> TTY_FLIPBUF_SIZE is going away. Just pick a value good for your hardware
> and under PAGE_SIZE.

Thanks for your comments. I'll change it soon and submit a new patch.

>
> Other question - is your locking ok for low latency. In low latency mode
> tty_flip_buffer_push() may directly end up calling your write methods.
>
Yes, I noticed that and I think it's ok. The driver is tested everday
and works fine.

-Aubrey

2006-09-21 09:57:09

by Luke Yang

[permalink] [raw]
Subject: Re: [PATCH 2/4] Blackfin: Serial driver for Blackfin arch on 2.6.18

Great thanks. Here is the new patch:

Signed-off-by: Luke Yang <[email protected]>
Acked-by: Randy.Dunlap <[email protected]>
Acked-by: Alan Cox <[email protected]>

drivers/serial/Kconfig | 44 ++
drivers/serial/Makefile | 1
drivers/serial/bfin_5xx.c | 906 ++++++++++++++++++++++++++++++++++++++++++++
include/linux/serial_core.h | 3
4 files changed, 954 insertions(+)

diff -urN linux-2.6.18.patch1/drivers/serial/Kconfig
linux-2.6.18.patch2/drivers/serial/Kconfig
--- linux-2.6.18.patch1/drivers/serial/Kconfig 2006-09-21
09:14:42.000000000 +0800
+++ linux-2.6.18.patch2/drivers/serial/Kconfig 2006-09-21
16:17:50.000000000 +0800
@@ -488,6 +488,50 @@
your boot loader (lilo or loadlin) about how to pass options to the
kernel at boot time.)

+config SERIAL_BFIN
+ tristate "Blackfin serial port support (EXPERIMENTAL)"
+ depends on BFIN && EXPERIMENTAL
+ select SERIAL_CORE
+ help
+ Add support for the built-in UARTs on the Blackfin.
+
+ To compile this driver as a module, choose M here: the
+ module will be called bfin_5xx.
+
+config SERIAL_BFIN_CONSOLE
+ bool "Console on Blackfin serial port"
+ depends on SERIAL_BFIN
+ select SERIAL_CORE_CONSOLE
+
+choice
+ prompt "Blackfin UART Mode"
+ depends on SERIAL_BFIN
+ default SERIAL_BFIN_DMA
+ help
+ This driver supports the built-in serial ports of the Blackfin family
+ of CPUs
+
+config SERIAL_BFIN_DMA
+ bool "Blackfin UART DMA mode"
+ depends on DMA_UNCACHED_1M
+ help
+ This driver works under DMA mode. If this option is selected, the
+ blackfin simple dma driver is also enabled.
+
+config SERIAL_BFIN_PIO
+ bool "Blackfin UART PIO mode"
+ help
+ This driver works under PIO mode.
+
+endchoice
+
+config SERIAL_BFIN_CTSRTS
+ bool "Enable hardware flow control"
+ depends on SERIAL_BFIN
+ help
+ Enable hardware flow control in the driver. Using GPIO emulate the CTS/RTS
+ signal.
+
config SERIAL_IMX
bool "IMX serial port support"
depends on ARM && ARCH_IMX
diff -urN linux-2.6.18.patch1/drivers/serial/Makefile
linux-2.6.18.patch2/drivers/serial/Makefile
--- linux-2.6.18.patch1/drivers/serial/Makefile 2006-09-21
09:14:42.000000000 +0800
+++ linux-2.6.18.patch2/drivers/serial/Makefile 2006-09-21
15:54:13.000000000 +0800
@@ -25,6 +25,7 @@
obj-$(CONFIG_SERIAL_CLPS711X) += clps711x.o
obj-$(CONFIG_SERIAL_PXA) += pxa.o
obj-$(CONFIG_SERIAL_SA1100) += sa1100.o
+obj-$(CONFIG_SERIAL_BFIN) += bfin_5xx.o
obj-$(CONFIG_SERIAL_S3C2410) += s3c2410.o
obj-$(CONFIG_SERIAL_SUNCORE) += suncore.o
obj-$(CONFIG_SERIAL_SUNHV) += sunhv.o
diff -urN linux-2.6.18.patch1/drivers/serial/bfin_5xx.c
linux-2.6.18.patch2/drivers/serial/bfin_5xx.c
--- linux-2.6.18.patch1/drivers/serial/bfin_5xx.c 1970-01-01
08:00:00.000000000 +0800
+++ linux-2.6.18.patch2/drivers/serial/bfin_5xx.c 2006-09-21
17:51:02.000000000 +0800
@@ -0,0 +1,906 @@
+/*
+ * File: drivers/serial/bfin_5xx.c
+ * Based on: Based on drivers/serial/sa1100.c
+ * Author: Aubrey Li <[email protected]>
+ *
+ * Created:
+ * Description: Driver for blackfin 5xx serial ports
+ *
+ * Rev: $Id: bfin_5xx.c,v 1.12 2006/09/04 04:44:27 aubrey Exp $
+ *
+ * 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>
+
+#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
+
+/* We've been assigned a range on the "Low-density serial ports" major */
+#define SERIAL_BFIN_MAJOR TTY_MAJOR
+#define MINOR_START 64
+
+#undef DEBUG
+
+#ifdef DEBUG
+# define DPRINTK(x...) printk(KERN_DEBUG x)
+#else
+# define DPRINTK(x...) do { } while (0)
+#endif
+
+/*
+ * Setup for console. Argument comes from the menuconfig
+ */
+
+#if defined(CONFIG_BAUD_9600)
+#define CONSOLE_BAUD_RATE 9600
+#elif defined(CONFIG_BAUD_19200)
+#define CONSOLE_BAUD_RATE 19200
+#elif defined(CONFIG_BAUD_38400)
+#define CONSOLE_BAUD_RATE 38400
+#elif defined(CONFIG_BAUD_57600)
+#define CONSOLE_BAUD_RATE 57600
+#elif defined(CONFIG_BAUD_115200)
+#define CONSOLE_BAUD_RATE 115200
+#endif
+
+#define DMA_RX_XCOUNT 512
+#define DMA_RX_YCOUNT (PAGE_SIZE / DMA_RX_XCOUNT)
+
+#define DMA_RX_FLUSH_JIFFIES 5
+
+#ifdef CONFIG_SERIAL_BFIN_DMA
+wait_queue_head_t bfin_serial_tx_queue[NR_PORTS];
+static void bfin_serial_dma_tx_chars(struct bfin_serial_port *uart);
+#else
+static void bfin_serial_do_work(void *);
+static void bfin_serial_tx_chars(struct bfin_serial_port *uart);
+static void local_put_char(struct bfin_serial_port *uart, char ch);
+#endif
+
+static void bfin_serial_mctrl_check(struct bfin_serial_port *uart);
+
+/*
+ * interrupts disabled on entry
+ */
+static void bfin_serial_stop_tx(struct uart_port *port)
+{
+ struct bfin_serial_port *uart = (struct bfin_serial_port *)port;
+ unsigned short ier;
+ ier = UART_GET_IER(uart);
+ ier &= ~ETBEI;
+ UART_PUT_IER(uart, ier);
+#ifdef CONFIG_SERIAL_BFIN_DMA
+ disable_dma(uart->tx_dma_channel);
+#endif
+}
+
+/*
+ * port locked and interrupts 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
+ unsigned short ier;
+ ier = UART_GET_IER(uart);
+ ier |= ETBEI;
+ UART_PUT_IER(uart, ier);
+ bfin_serial_tx_chars(uart);
+#endif
+}
+
+/*
+ * Interrupts enabled
+ */
+static void bfin_serial_stop_rx(struct uart_port *port)
+{
+ struct bfin_serial_port *uart = (struct bfin_serial_port *)port;
+ unsigned short ier;
+ ier = UART_GET_IER(uart);
+ ier &= ERBFI;
+ UART_PUT_IER(uart, ier);
+}
+
+/*
+ * Set the modem control timer to fire immediately.
+ */
+static void bfin_serial_enable_ms(struct uart_port *port)
+{
+}
+
+#ifdef CONFIG_SERIAL_BFIN_PIO
+static void local_put_char(struct bfin_serial_port *uart, char ch)
+{
+ unsigned short status;
+ int flags = 0;
+
+ local_irq_save(flags);
+
+ do {
+ status = UART_GET_LSR(uart);
+ } while (!(status & THRE));
+
+ UART_PUT_CHAR(uart, ch);
+ local_irq_restore(flags);
+}
+
+static void
+bfin_serial_rx_chars(struct bfin_serial_port *uart, struct pt_regs *regs)
+{
+ struct tty_struct *tty = uart->port.info->tty;
+ unsigned int status=0, ch, flg;
+ ch = UART_GET_CHAR(uart);
+ uart->port.icount.rx++;
+ flg = TTY_NORMAL;
+ if (uart_handle_sysrq_char(&uart->port, ch, regs))
+ goto ignore_char;
+ uart_insert_char(&uart->port, status, 1, 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_int(int irq, void *dev_id, struct pt_regs *regs)
+{
+ struct bfin_serial_port *uart = dev_id;
+ unsigned short status;
+
+ spin_lock(&uart->port.lock);
+ status = UART_GET_IIR(uart);
+ do {
+ if ((status & IIR_STATUS) == IIR_TX_READY)
+ bfin_serial_tx_chars(uart);
+ if ((status & IIR_STATUS) == IIR_RX_READY)
+ bfin_serial_rx_chars(uart, regs);
+ status = UART_GET_IIR(uart);
+ } while (status &(IIR_TX_READY | IIR_RX_READY));
+ spin_unlock(&uart->port.lock);
+ return IRQ_HANDLED;
+}
+
+static void bfin_serial_do_work(void *port)
+{
+ struct bfin_serial_port *uart = (struct bfin_serial_port *)port;
+ bfin_serial_mctrl_check(uart);
+}
+
+#endif
+
+#ifdef CONFIG_SERIAL_BFIN_DMA
+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->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;
+ }
+
+ local_irq_save(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;
+ blackfin_dcache_flush_range((unsigned long)(xmit->buf+xmit->tail),
+ (unsigned long)(xmit->buf+xmit->tail+uart->tx_count));
+ set_dma_config(uart->tx_dma_channel,
+ set_bfin_dma_config(DIR_READ, DMA_FLOW_STOP,
+ INTR_ON_BUF,
+ DIMENSION_LINEAR,
+ DATA_SIZE_8));
+ set_dma_start_addr(uart->tx_dma_channel, (unsigned
long)(xmit->buf+xmit->tail));
+ set_dma_x_count(uart->tx_dma_channel, uart->tx_count);
+ set_dma_x_modify(uart->tx_dma_channel, 1);
+ enable_dma(uart->tx_dma_channel);
+ ier = UART_GET_IER(uart);
+ ier |= ETBEI;
+ UART_PUT_IER(uart, ier);
+ local_irq_restore(flags);
+}
+
+static void bfin_serial_dma_rx_chars(struct bfin_serial_port * uart)
+{
+ struct tty_struct *tty = uart->port.info->tty;
+ int i, flg, status = 0;
+
+ uart->port.icount.rx += CIRC_CNT(uart->rx_dma_buf.head,
uart->rx_dma_buf.tail, UART_XMIT_SIZE);;
+ 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], NULL))
+ goto dma_ignore_char;
+ uart_insert_char(&uart->port, status, 1, uart->rx_dma_buf.buf[i], flg);
+ }
+dma_ignore_char:
+ tty_flip_buffer_push(tty);
+}
+
+void bfin_serial_rx_dma_timeout(struct bfin_serial_port *uart)
+{
+ int x_pos, pos;
+ int flags = 0;
+
+ bfin_serial_dma_tx_chars(uart);
+
+ local_irq_save(flags);
+ x_pos = DMA_RX_XCOUNT - get_dma_curr_xcount(uart->rx_dma_channel);
+ 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;
+ bfin_serial_dma_rx_chars(uart);
+ uart->rx_dma_buf.head = uart->rx_dma_buf.tail;
+ }
+ local_irq_restore(flags);
+ uart->rx_dma_timer.expires = jiffies + DMA_RX_FLUSH_JIFFIES;
+ add_timer(&(uart->rx_dma_timer));
+}
+
+static irqreturn_t bfin_serial_dma_tx_int(int irq, void *dev_id,
struct pt_regs *regs)
+{
+ 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);
+ ier = UART_GET_IER(uart);
+ ier &= ~ETBEI;
+ UART_PUT_IER(uart, ier);
+ 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);
+
+ if (uart_circ_empty(xmit))
+ bfin_serial_stop_tx(&uart->port);
+ uart->tx_done = 1;
+ }
+
+ spin_unlock(&uart->port.lock);
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t bfin_serial_dma_rx_int(int irq, void *dev_id,
struct pt_regs *regs)
+{
+ 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);
+ return IRQ_HANDLED;
+}
+#endif
+
+/*
+ * Return TIOCSER_TEMT when transmitter is not busy.
+ */
+static unsigned int bfin_serial_tx_empty(struct uart_port *port)
+{
+ struct bfin_serial_port *uart = (struct bfin_serial_port *)port;
+ unsigned short lsr;
+ lsr = UART_GET_LSR(uart);
+ if (lsr & THRE)
+ return TIOCSER_TEMT;
+ else
+ return 0;
+}
+
+static unsigned int bfin_serial_get_mctrl(struct uart_port *port)
+{
+ /* Hardware flow control is only supported on the first port */
+#ifdef CONFIG_SERIAL_BFIN_CTSRTS
+ if ((bfin_read16(CTS_PORT) & (1 << CTS_PIN)) && (port->line == 0))
+ return TIOCM_DSR | TIOCM_CAR;
+ else
+#endif
+ return TIOCM_CTS | TIOCM_DSR | TIOCM_CAR;
+}
+
+static void bfin_serial_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+#ifdef CONFIG_SERIAL_BFIN_CTSRTS
+ if (mctrl & TIOCM_RTS)
+ bfin_write16(RTS_PORT, bfin_read16(RTS_PORT) & (~1 << RTS_PIN));
+ else
+ bfin_write16(RTS_PORT, bfin_read16(RTS_PORT) | (1 << RTS_PIN));
+#endif
+}
+
+/*
+ * Handle any change of modem status signal since we were last called.
+ */
+static void bfin_serial_mctrl_check(struct bfin_serial_port *uart)
+{
+#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);
+ if (!(status & TIOCM_CTS)) {
+ tty->hw_stopped = 1;
+ } 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
+}
+
+/*
+ * Interrupts always disabled.
+ */
+static void bfin_serial_break_ctl(struct uart_port *port, int break_state)
+{
+}
+
+int bfin_serial_startup(struct uart_port *port)
+{
+ struct bfin_serial_port *uart = (struct bfin_serial_port *)port;
+
+#ifdef CONFIG_SERIAL_BFIN_DMA
+ dma_addr_t dma_handle;
+
+ if (request_dma(uart->rx_dma_channel, "BFIN_UART_RX") < 0) {
+ printk(KERN_NOTICE "Unable to attach Blackfin UART RX DMA channel\n");
+ return -EBUSY;
+ }
+
+ if (request_dma(uart->tx_dma_channel, "BFIN_UART_TX") < 0) {
+ printk(KERN_NOTICE "Unable to attach Blackfin UART TX DMA channel\n");
+ free_dma(uart->rx_dma_channel);
+ return -EBUSY;
+ }
+
+ set_dma_callback(uart->rx_dma_channel, bfin_serial_dma_rx_int, uart);
+ set_dma_callback(uart->tx_dma_channel, bfin_serial_dma_tx_int, uart);
+
+ uart->rx_dma_buf.buf = (unsigned char *)dma_alloc_coherent(NULL,
PAGE_SIZE, &dma_handle, GFP_DMA);
+ uart->rx_dma_buf.head = 0;
+ uart->rx_dma_buf.tail = 0;
+ uart->rx_dma_nrows = 0;
+
+ set_dma_config(uart->rx_dma_channel,
+ set_bfin_dma_config(DIR_WRITE, DMA_FLOW_AUTO,
+ INTR_ON_ROW, DIMENSION_2D,
+ DATA_SIZE_8));
+ set_dma_x_count(uart->rx_dma_channel, DMA_RX_XCOUNT);
+ set_dma_x_modify(uart->rx_dma_channel, 1);
+ set_dma_y_count(uart->rx_dma_channel, DMA_RX_YCOUNT);
+ set_dma_y_modify(uart->rx_dma_channel, 1);
+ set_dma_start_addr(uart->rx_dma_channel, (unsigned long)uart->rx_dma_buf.buf);
+ enable_dma(uart->rx_dma_channel);
+
+ uart->rx_dma_timer.data = (unsigned long)(uart);
+ uart->rx_dma_timer.function = (void *)bfin_serial_rx_dma_timeout;
+ uart->rx_dma_timer.expires = jiffies + DMA_RX_FLUSH_JIFFIES;
+ add_timer(&(uart->rx_dma_timer));
+#else
+ if (request_irq
+ (uart->port.irq, bfin_serial_int, IRQF_DISABLED | IRQF_SHARED,
+ "BFIN_UART0_RX", uart)) {
+ printk(KERN_NOTICE "Unable to attach BlackFin UART RX interrupt\n");
+ return -EBUSY;
+ }
+
+ if (request_irq
+ (uart->port.irq+1, bfin_serial_int, IRQF_DISABLED | IRQF_SHARED,
+ "BFIN_UART0_TX", uart)) {
+ printk(KERN_NOTICE "Unable to attach BlackFin UART TX interrupt\n");
+ free_irq(uart->port.irq, uart);
+ return -EBUSY;
+ }
+#endif
+ UART_PUT_IER(uart, UART_GET_IER(uart) | ERBFI);
+ return 0;
+}
+
+static void bfin_serial_shutdown(struct uart_port *port)
+{
+ struct bfin_serial_port *uart = (struct bfin_serial_port *)port;
+
+#ifdef CONFIG_SERIAL_BFIN_DMA
+ disable_dma(uart->tx_dma_channel);
+ free_dma(uart->tx_dma_channel);
+ disable_dma(uart->rx_dma_channel);
+ free_dma(uart->rx_dma_channel);
+ del_timer(&(uart->rx_dma_timer));
+#else
+ free_irq(uart->port.irq, uart);
+ free_irq(uart->port.irq+1, uart);
+#endif
+}
+
+static void
+bfin_serial_set_termios(struct uart_port *port, struct termios *termios,
+ struct termios *old)
+{
+}
+
+static const char *bfin_serial_type(struct uart_port *port)
+{
+ struct bfin_serial_port *uart = (struct bfin_serial_port *)port;
+ return uart->port.type == PORT_BFIN ? "BFIN-UART" : NULL;
+}
+
+/*
+ * Release the memory region(s) being used by 'port'.
+ */
+static void bfin_serial_release_port(struct uart_port *port)
+{
+}
+
+/*
+ * Request the memory region(s) being used by 'port'.
+ */
+static int bfin_serial_request_port(struct uart_port *port)
+{
+ return 0;
+}
+
+/*
+ * Configure/autoconfigure the port.
+ */
+static void bfin_serial_config_port(struct uart_port *port, int flags)
+{
+ struct bfin_serial_port *uart = (struct bfin_serial_port *)port;
+
+ if (flags & UART_CONFIG_TYPE &&
+ bfin_serial_request_port(&uart->port) == 0)
+ uart->port.type = PORT_BFIN;
+}
+
+/*
+ * Verify the new serial_struct (for TIOCSSERIAL).
+ * The only change we allow are to the flags and type, and
+ * even then only between PORT_BFIN and PORT_UNKNOWN
+ */
+static int
+bfin_serial_verify_port(struct uart_port *port, struct serial_struct *ser)
+{
+ return 0;
+}
+
+static struct uart_ops bfin_serial_pops = {
+ .tx_empty = bfin_serial_tx_empty,
+ .set_mctrl = bfin_serial_set_mctrl,
+ .get_mctrl = bfin_serial_get_mctrl,
+ .stop_tx = bfin_serial_stop_tx,
+ .start_tx = bfin_serial_start_tx,
+ .stop_rx = bfin_serial_stop_rx,
+ .enable_ms = bfin_serial_enable_ms,
+ .break_ctl = bfin_serial_break_ctl,
+ .startup = bfin_serial_startup,
+ .shutdown = bfin_serial_shutdown,
+ .set_termios = bfin_serial_set_termios,
+ .type = bfin_serial_type,
+ .release_port = bfin_serial_release_port,
+ .request_port = bfin_serial_request_port,
+ .config_port = bfin_serial_config_port,
+ .verify_port = bfin_serial_verify_port,
+};
+
+static int bfin_serial_calc_baud(unsigned int uartclk)
+{
+ int baud;
+ baud = get_sclk() / (uartclk*8);
+ if ((baud & 0x1) == 1) {
+ baud++;
+ }
+ return baud/2;
+}
+
+static void __init bfin_serial_init_ports(void)
+{
+ static int first = 1;
+ int i;
+ unsigned short val;
+ int baud;
+
+ if (!first)
+ return;
+ first = 0;
+ bfin_serial_hw_init();
+
+ for (i = 0; i < NR_PORTS; i++) {
+ bfin_serial_ports[i].port.uartclk = CONSOLE_BAUD_RATE;
+ bfin_serial_ports[i].port.ops = &bfin_serial_pops;
+ bfin_serial_ports[i].port.line = i;
+ bfin_serial_ports[i].port.iotype = UPIO_MEM;
+ bfin_serial_ports[i].port.membase = (void __iomem *)uart_base_addr[i];
+ bfin_serial_ports[i].port.mapbase = uart_base_addr[i];
+ bfin_serial_ports[i].port.irq = uart_irq[i];
+ bfin_serial_ports[i].port.flags = UPF_BOOT_AUTOCONF;
+#ifdef CONFIG_SERIAL_BFIN_DMA
+ bfin_serial_ports[i].tx_done = 1;
+ bfin_serial_ports[i].tx_count = 0;
+ bfin_serial_ports[i].tx_dma_channel = uart_tx_dma_channel[i];
+ bfin_serial_ports[i].rx_dma_channel = uart_rx_dma_channel[i];
+
+ init_timer(&(bfin_serial_ports[i].rx_dma_timer));
+#else
+ INIT_WORK(&bfin_serial_ports[i].cts_workqueue, bfin_serial_do_work,
&bfin_serial_ports[i]);
+#endif
+
+ baud = bfin_serial_calc_baud(bfin_serial_ports[i].port.uartclk);
+
+ /* Enable UART */
+ val = UART_GET_GCTL(&bfin_serial_ports[i]);
+ val |= UCEN;
+ UART_PUT_GCTL(&bfin_serial_ports[i], val);
+
+ /* Set DLAB in LCR to Access DLL and DLH */
+ val = UART_GET_LCR(&bfin_serial_ports[i]);
+ val |= DLAB;
+ UART_PUT_LCR(&bfin_serial_ports[i], val);
+
+ UART_PUT_DLL(&bfin_serial_ports[i], baud & 0xFF);
+ UART_PUT_DLH(&bfin_serial_ports[i], (baud >> 8) & 0xFF);
+
+ /* Clear DLAB in LCR to Access THR RBR IER */
+ val = UART_GET_LCR(&bfin_serial_ports[i]);
+ val &= ~DLAB;
+ UART_PUT_LCR(&bfin_serial_ports[i], val);
+
+ /* Set LCR to Word Lengh 8-bit word select */
+ val = WLS(8);
+ UART_PUT_LCR(&bfin_serial_ports[i], val);
+ }
+}
+
+#ifdef CONFIG_SERIAL_BFIN_CONSOLE
+/*
+ * Interrupts are disabled on entering
+ */
+static void
+bfin_serial_console_write(struct console *co, const char *s, unsigned
int count)
+{
+ struct bfin_serial_port *uart = &bfin_serial_ports[co->index];
+ int flags = 0;
+ unsigned short status, tmp;
+ int i;
+
+ local_irq_save(flags);
+
+ for (i = 0; i < count; i++) {
+ do {
+ status = UART_GET_LSR(uart);
+ } while (!(status & THRE));
+
+ tmp = UART_GET_LCR(uart);
+ tmp &= ~DLAB;
+ UART_PUT_LCR(uart, tmp);
+
+ UART_PUT_CHAR(uart, s[i]);
+ if (s[i] == '\n') {
+ do {
+ status = UART_GET_LSR(uart);
+ } while(!(status & THRE));
+ UART_PUT_CHAR(uart, '\r');
+ }
+ }
+
+ local_irq_restore(flags);
+}
+
+/*
+ * If the port was already initialised (eg, by a boot loader),
+ * try to determine the current setup.
+ */
+static void __init
+bfin_serial_console_get_options(struct bfin_serial_port *uart, int *baud,
+ int *parity, int *bits)
+{
+ unsigned short status;
+
+ status = UART_GET_IER(uart) & (ERBFI | ETBEI);
+ if (status == (ERBFI | ETBEI)) {
+ /* ok, the port was enabled */
+ unsigned short lcr, val;
+ unsigned short dlh, dll;
+
+ lcr = UART_GET_LCR(uart);
+
+ *parity = 'n';
+ if (lcr & PEN) {
+ if (lcr & EPS)
+ *parity = 'e';
+ else
+ *parity = 'o';
+ }
+ switch (lcr & 0x03) {
+ case 0: *bits = 5; break;
+ case 1: *bits = 6; break;
+ case 2: *bits = 7; break;
+ case 3: *bits = 8; break;
+ }
+ /* Set DLAB in LCR to Access DLL and DLH */
+ val = UART_GET_LCR(uart);
+ val |= DLAB;
+ UART_PUT_LCR(uart, val);
+
+ dll = UART_GET_DLL(uart);
+ dlh = UART_GET_DLH(uart);
+
+ /* Clear DLAB in LCR to Access THR RBR IER */
+ val = UART_GET_LCR(uart);
+ val &= ~DLAB;
+ UART_PUT_LCR(uart, val);
+
+ *baud = get_sclk() / (16*(dll | dlh << 8));
+ }
+ DPRINTK("%s:baud = %d, parity = %c, bits= %d\n", __FUNCTION__,
*baud, *parity, *bits);
+}
+
+static int __init
+bfin_serial_console_setup(struct console *co, char *options)
+{
+ struct bfin_serial_port *uart;
+ int baud = CONSOLE_BAUD_RATE;
+ int bits = 8;
+ int parity = 'n';
+#ifdef CONFIG_SERIAL_BFIN_CTSRTS
+ int flow = 'r';
+#else
+ int flow = 'n';
+#endif
+
+ /*
+ * Check whether an invalid uart number has been specified, and
+ * if so, search for the first available port that does have
+ * console support.
+ */
+ if (co->index == -1 || co->index >= NR_PORTS)
+ co->index = 0;
+ uart = &bfin_serial_ports[co->index];
+
+ if (options)
+ uart_parse_options(options, &baud, &parity, &bits, &flow);
+ else
+ bfin_serial_console_get_options(uart, &baud, &parity, &bits);
+
+ return uart_set_options(&uart->port, co, baud, parity, bits, flow);
+}
+
+static struct uart_driver bfin_serial_reg;
+static struct console bfin_serial_console = {
+ .name = "ttyS",
+ .write = bfin_serial_console_write,
+ .device = uart_console_device,
+ .setup = bfin_serial_console_setup,
+ .flags = CON_PRINTBUFFER,
+ .index = -1,
+ .data = &bfin_serial_reg,
+};
+
+static int __init bfin_serial_rs_console_init(void)
+{
+ bfin_serial_init_ports();
+ register_console(&bfin_serial_console);
+ return 0;
+}
+console_initcall(bfin_serial_rs_console_init);
+
+#define BFIN_SERIAL_CONSOLE &bfin_serial_console
+#else
+#define BFIN_SERIAL_CONSOLE NULL
+#endif
+
+static struct uart_driver bfin_serial_reg = {
+ .owner = THIS_MODULE,
+ .driver_name = "bfin-uart",
+ .dev_name = "ttyS",
+ .devfs_name = "ttyS/",
+ .major = SERIAL_BFIN_MAJOR,
+ .minor = MINOR_START,
+ .nr = NR_PORTS,
+ .cons = BFIN_SERIAL_CONSOLE,
+};
+
+static int bfin_serial_suspend(struct platform_device *dev, pm_message_t state)
+{
+ struct bfin_serial_port *uart = platform_get_drvdata(dev);
+
+ if (uart)
+ uart_suspend_port(&bfin_serial_reg, &uart->port);
+
+ return 0;
+}
+
+static int bfin_serial_resume(struct platform_device *dev)
+{
+ struct bfin_serial_port *uart = platform_get_drvdata(dev);
+
+ if (uart)
+ uart_resume_port(&bfin_serial_reg, &uart->port);
+
+ return 0;
+}
+
+static int bfin_serial_probe(struct platform_device *dev)
+{
+ struct resource *res = dev->resource;
+ int i;
+
+ for (i = 0; i < dev->num_resources; i++, res++)
+ if (res->flags & IORESOURCE_MEM)
+ break;
+
+ if (i < dev->num_resources) {
+ for (i = 0; i < NR_PORTS; i++, res++) {
+ if (bfin_serial_ports[i].port.mapbase != res->start)
+ continue;
+ bfin_serial_ports[i].port.dev = &dev->dev;
+ uart_add_one_port(&bfin_serial_reg, &bfin_serial_ports[i].port);
+ platform_set_drvdata(dev, &bfin_serial_ports[i]);
+ }
+ }
+
+ return 0;
+}
+
+static int bfin_serial_remove(struct platform_device *pdev)
+{
+ struct bfin_serial_port *uart = platform_get_drvdata(pdev);
+
+ platform_set_drvdata(pdev, NULL);
+
+ if (uart)
+ uart_remove_one_port(&bfin_serial_reg, &uart->port);
+
+ return 0;
+}
+
+static struct platform_driver bfin_serial_driver = {
+ .probe = bfin_serial_probe,
+ .remove = bfin_serial_remove,
+ .suspend = bfin_serial_suspend,
+ .resume = bfin_serial_resume,
+ .driver = {
+ .name = "bfin-uart",
+ },
+};
+
+static int __init bfin_serial_init(void)
+{
+ int ret;
+
+ printk(KERN_INFO "Serial: Blackfin serial driver\n");
+
+ bfin_serial_init_ports();
+
+ ret = uart_register_driver(&bfin_serial_reg);
+ if (ret == 0) {
+ ret = platform_driver_register(&bfin_serial_driver);
+ if (ret) {
+ DPRINTK("uart register failed\n");
+ uart_unregister_driver(&bfin_serial_reg);
+ }
+ }
+ return ret;
+}
+
+static void __exit bfin_serial_exit(void)
+{
+ platform_driver_unregister(&bfin_serial_driver);
+ uart_unregister_driver(&bfin_serial_reg);
+}
+
+module_init(bfin_serial_init);
+module_exit(bfin_serial_exit);
+
+MODULE_AUTHOR("Aubrey.Li <[email protected]>");
+MODULE_DESCRIPTION("Blackfin generic serial port driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_CHARDEV_MAJOR(SERIAL_BFIN_MAJOR);
diff -urN linux-2.6.18.patch1/include/linux/serial_core.h
linux-2.6.18.patch2/include/linux/serial_core.h
--- linux-2.6.18.patch1/include/linux/serial_core.h 2006-09-21
09:14:54.000000000 +0800
+++ linux-2.6.18.patch2/include/linux/serial_core.h 2006-09-21
09:38:17.000000000 +0800
@@ -132,6 +132,9 @@

#define PORT_S3C2412 73

+/* Blackfin bf5xx */
+#define PORT_BFIN 74
+

#ifdef __KERNEL__


On 9/21/06, Aubrey <[email protected]> wrote:
> On 9/21/06, Alan Cox <[email protected]> wrote:
> > Ar Iau, 2006-09-21 am 11:33 +0800, ysgrifennodd Luke Yang:
> > > Hi,
> > >
> > > This is the serial driver for Blackfin. It is designed for the serial
> > > core framework.
> >
> > > +#define DMA_RX_XCOUNT TTY_FLIPBUF_SIZE
> >
> > TTY_FLIPBUF_SIZE is going away. Just pick a value good for your hardware
> > and under PAGE_SIZE.
>
> Thanks for your comments. I'll change it soon and submit a new patch.
>
> >
> > Other question - is your locking ok for low latency. In low latency mode
> > tty_flip_buffer_push() may directly end up calling your write methods.
> >
> Yes, I noticed that and I think it's ok. The driver is tested everday
> and works fine.
>
> -Aubrey
>


--
Best regards,
Luke Yang
[email protected]


Attachments:
(No filename) (27.83 kB)
blackfin_serial_drv_2.6.18.patch (26.61 kB)
Download all attachments

2006-09-21 10:28:54

by Matti Aarnio

[permalink] [raw]
Subject: Re: [PATCH 2/4] Blackfin: Serial driver for Blackfin arch on 2.6.18

On Wed, Sep 20, 2006 at 10:28:37PM -0700, Randy.Dunlap wrote:
> On Thu, 21 Sep 2006 11:33:05 +0800 Luke Yang wrote:
>
> > This is the serial driver for Blackfin. It is designed for the serial
> > core framework.
> >
> > As to other drivers, I'll send them one by one later.
> >
> > Signed-off-by: Luke Yang <[email protected]>
> >
> > drivers/serial/Kconfig | 35 +
> > drivers/serial/Makefile | 3
> > drivers/serial/bfin_5xx.c | 903 ++++++++++++++++++++++++++++++++++++++++++++
> > include/linux/serial_core.h | 3
> > 4 files changed, 943 insertions(+), 1 deletion(-)
> >
> > diff -urN linux-2.6.18.patch1/drivers/serial/Kconfig
> > linux-2.6.18.patch2/drivers/serial/Kconfig
> > --- linux-2.6.18.patch1/drivers/serial/Kconfig 2006-09-21
> > 09:14:42.000000000 +0800
> > +++ linux-2.6.18.patch2/drivers/serial/Kconfig 2006-09-21
> > 09:38:17.000000000 +0800
> > @@ -488,6 +488,41 @@
> > your boot loader (lilo or loadlin) about how to pass options to the
> > kernel at boot time.)
> >
> > +config SERIAL_BFIN
> > + bool "Blackfin serial port support (EXPERIMENTAL)"
> > + depends on BFIN && EXPERIMENTAL
> > + select SERIAL_CORE
>
> Just curious: why bool and not tristate? (i.e., why is loadable
> module not allowed?)

Target is, after all, uCLinux embedded processor, where one should
be able to pre-determine what things go in as baseline kernel (what
hardware was used to build it) and what are possible options.

All Blackfins have internal UART (or two).
Some multiplex on same pins also CAN.

One of these weeks I should push onward my own BF537 project, I am
still just pre-collecting all tools to be able to debug it once
I commit boards to manufacturing...


/Matti Aarnio

2006-09-21 15:36:56

by Randy Dunlap

[permalink] [raw]
Subject: Re: [PATCH 2/4] Blackfin: Serial driver for Blackfin arch on 2.6.18

On Thu, 21 Sep 2006 17:57:03 +0800 Luke Yang wrote:

> Great thanks. Here is the new patch:
>
> Signed-off-by: Luke Yang <[email protected]>
> Acked-by: Randy.Dunlap <[email protected]>
> Acked-by: Alan Cox <[email protected]>

I can't find an email where I acked this patch...

> drivers/serial/Kconfig | 44 ++
> drivers/serial/Makefile | 1
> drivers/serial/bfin_5xx.c | 906 ++++++++++++++++++++++++++++++++++++++++++++
> include/linux/serial_core.h | 3
> 4 files changed, 954 insertions(+)

---
~Randy

2006-09-21 16:00:43

by Aubrey Li

[permalink] [raw]
Subject: Re: [PATCH 2/4] Blackfin: Serial driver for Blackfin arch on 2.6.18

Hi Randy,

I think luke just assumed you acked the patch after we have done the
modification. But he'll remove it. Sorry for that.

Please comment the newest patch.
Thanks,
-Aubrey

On 9/21/06, Luke Yang <[email protected]> wrote:
> On 9/21/06, Randy.Dunlap <[email protected]> wrote:
> > On Thu, 21 Sep 2006 17:57:03 +0800 Luke Yang wrote:
> >
> > > Great thanks. Here is the new patch:
> > >
> > > Signed-off-by: Luke Yang <[email protected]>
> > > Acked-by: Randy.Dunlap <[email protected]>
> > > Acked-by: Alan Cox <[email protected]>
> >
> > I can't find an email where I acked this patch...
> Sorry, I'll remove it.
> >
> > > drivers/serial/Kconfig | 44 ++
> > > drivers/serial/Makefile | 1
> > > drivers/serial/bfin_5xx.c | 906 ++++++++++++++++++++++++++++++++++++++++++++
> > > include/linux/serial_core.h | 3
> > > 4 files changed, 954 insertions(+)
> >
> > ---
> > ~Randy
> >
>
>
> --
> Best regards,
> Luke Yang
> [email protected]
>

2006-09-21 16:06:47

by Randy Dunlap

[permalink] [raw]
Subject: Re: [PATCH 2/4] Blackfin: Serial driver for Blackfin arch on 2.6.18

On Thu, 21 Sep 2006 17:57:03 +0800 Luke Yang wrote:

> Great thanks. Here is the new patch:
>
> drivers/serial/Kconfig | 44 ++
> drivers/serial/Makefile | 1
> drivers/serial/bfin_5xx.c | 906 ++++++++++++++++++++++++++++++++++++++++++++
> include/linux/serial_core.h | 3
> 4 files changed, 954 insertions(+)
>

> +#ifdef CONFIG_SERIAL_BFIN_DMA
> +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->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;
> + }
> +
> + local_irq_save(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;

The line above still needs to be indented one more tab stop.
Otherwise looks OK to me.

---
~Randy

2006-09-22 02:03:24

by Oleg Verych

[permalink] [raw]
Subject: Re: [PATCH 2/4] Blackfin: Serial driver for Blackfin arch on 2.6.18

Hallo, Luke Yang, who wrote:
> +static void bfin_serial_mctrl_check(struct bfin_serial_port *uart);
> +
> +/*
> + * interrupts disabled on entry
> + */

spelling error: _are_ disabled
please grep && sed all patches

> +static void bfin_serial_stop_tx(struct uart_port *port)
> +{
> + struct bfin_serial_port *uart = (struct bfin_serial_port *)port;
> + unsigned short ier;
> + ier = UART_GET_IER(uart);
> + ier &= ~ETBEI;
> + UART_PUT_IER(uart, ier);
> +#ifdef CONFIG_SERIAL_BFIN_DMA
> + disable_dma(uart->tx_dma_channel);
> +#endif
> +}

one blank line after local variables; you are using this in some functions, in
some you are not...

> +
> +static void bfin_serial_shutdown(struct uart_port *port)
> +{
> + struct bfin_serial_port *uart = (struct bfin_serial_port *)port;
> +

yes, one more space will be nicer

> + free_irq(uart->port.irq+1, uart);

here and the like


--
-o--=O`C
#oo'L O 5 years ago TT and WTC7 were assassinated
<___=E M learn more how (tm) <http://911research.com>

2006-09-22 02:27:54

by Aubrey Li

[permalink] [raw]
Subject: Re: [PATCH 2/4] Blackfin: Serial driver for Blackfin arch on 2.6.18

On 9/22/06, Oleg Verych <[email protected]> wrote:
> Hallo, Luke Yang, who wrote:
> > +static void bfin_serial_mctrl_check(struct bfin_serial_port *uart);
> > +
> > +/*
> > + * interrupts disabled on entry
> > + */
>
> spelling error: _are_ disabled
> please grep && sed all patches
>

Thanks for your comments.
Yeah, the driver is based on an existing driver: sa1100.c, which has
the spelling issue you mentioned too, but I think in the code comment,
It's __OK__.
Anyway, I'll change it and submit a new patch.

> > +static void bfin_serial_stop_tx(struct uart_port *port)
> > +{
> > + struct bfin_serial_port *uart = (struct bfin_serial_port *)port;
> > + unsigned short ier;
> > + ier = UART_GET_IER(uart);
> > + ier &= ~ETBEI;
> > + UART_PUT_IER(uart, ier);
> > +#ifdef CONFIG_SERIAL_BFIN_DMA
> > + disable_dma(uart->tx_dma_channel);
> > +#endif
> > +}
>
> one blank line after local variables; you are using this in some functions, in
> some you are not...

Good suggestion, thanks, I'll fix it.

>
> > +
> > +static void bfin_serial_shutdown(struct uart_port *port)
> > +{
> > + struct bfin_serial_port *uart = (struct bfin_serial_port *)port;
> > +
>
> yes, one more space will be nicer
>
> > + free_irq(uart->port.irq+1, uart);
>
> here and the like
>
>
> --
> -o--=O`C
> #oo'L O 5 years ago TT and WTC7 were assassinated
> <___=E M learn more how (tm) <http://911research.com>
>
> -
> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html
> Please read the FAQ at http://www.tux.org/lkml/
>