From 5ee9afcc22911391ea439353b4e8e984b8976f38 Mon Sep 17 00:00:00 2001 From: florian Date: Fri, 17 Oct 2008 22:53:56 +0000 Subject: New Broadcom BCM63xx codebase, huge thanks to Maxime ;) git-svn-id: svn://svn.openwrt.org/openwrt/trunk@13001 3c298f89-4303-0410-b956-a3cf2f4a3e73 --- .../003-add_serial_driver_for_bcm63xx_integr.patch | 1038 ++++++++++++++++++++ 1 file changed, 1038 insertions(+) create mode 100644 target/linux/brcm63xx/patches-2.6.27/003-add_serial_driver_for_bcm63xx_integr.patch (limited to 'target/linux/brcm63xx/patches-2.6.27/003-add_serial_driver_for_bcm63xx_integr.patch') diff --git a/target/linux/brcm63xx/patches-2.6.27/003-add_serial_driver_for_bcm63xx_integr.patch b/target/linux/brcm63xx/patches-2.6.27/003-add_serial_driver_for_bcm63xx_integr.patch new file mode 100644 index 000000000..32c66ba0f --- /dev/null +++ b/target/linux/brcm63xx/patches-2.6.27/003-add_serial_driver_for_bcm63xx_integr.patch @@ -0,0 +1,1038 @@ +From 6c489656b09998ed6a6f857e01ccf630e29358dd Mon Sep 17 00:00:00 2001 +From: Maxime Bizon +Date: Fri, 18 Jul 2008 19:35:55 +0200 +Subject: [PATCH] [MIPS] BCM63XX: Add serial driver for bcm63xx integrated UART. + +Signed-off-by: Maxime Bizon +--- + arch/mips/bcm63xx/Makefile | 1 + + arch/mips/bcm63xx/dev-uart.c | 41 + + drivers/serial/Kconfig | 19 + + drivers/serial/Makefile | 1 + + drivers/serial/bcm63xx_uart.c | 890 ++++++++++++++++++++++ + include/asm-mips/mach-bcm63xx/bcm63xx_dev_uart.h | 6 + + include/linux/serial_core.h | 2 + + 7 files changed, 960 insertions(+), 0 deletions(-) + create mode 100644 arch/mips/bcm63xx/dev-uart.c + create mode 100644 drivers/serial/bcm63xx_uart.c + create mode 100644 include/asm-mips/mach-bcm63xx/bcm63xx_dev_uart.h + +diff --git a/arch/mips/bcm63xx/Makefile b/arch/mips/bcm63xx/Makefile +index 4fc0a1c..8f3299e 100644 +--- a/arch/mips/bcm63xx/Makefile ++++ b/arch/mips/bcm63xx/Makefile +@@ -1,2 +1,3 @@ + obj-y += clk.o cpu.o cs.o gpio.o irq.o prom.o setup.o timer.o ++obj-y += dev-uart.o + obj-$(CONFIG_EARLY_PRINTK) += early_printk.o +diff --git a/arch/mips/bcm63xx/dev-uart.c b/arch/mips/bcm63xx/dev-uart.c +new file mode 100644 +index 0000000..5f3d89c +--- /dev/null ++++ b/arch/mips/bcm63xx/dev-uart.c +@@ -0,0 +1,41 @@ ++/* ++ * This file is subject to the terms and conditions of the GNU General Public ++ * License. See the file "COPYING" in the main directory of this archive ++ * for more details. ++ * ++ * Copyright (C) 2008 Maxime Bizon ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++static struct resource uart_resources[] = { ++ { ++ .start = -1, /* filled at runtime */ ++ .end = -1, /* filled at runtime */ ++ .flags = IORESOURCE_MEM, ++ }, ++ { ++ .start = -1, /* filled at runtime */ ++ .flags = IORESOURCE_IRQ, ++ }, ++}; ++ ++static struct platform_device bcm63xx_uart_device = { ++ .name = "bcm63xx_uart", ++ .id = 0, ++ .num_resources = ARRAY_SIZE(uart_resources), ++ .resource = uart_resources, ++}; ++ ++int __init bcm63xx_uart_register(void) ++{ ++ uart_resources[0].start = bcm63xx_regset_address(RSET_UART0); ++ uart_resources[0].end = uart_resources[0].start; ++ uart_resources[0].end += RSET_UART_SIZE - 1; ++ uart_resources[1].start = bcm63xx_get_irq_number(IRQ_UART0); ++ return platform_device_register(&bcm63xx_uart_device); ++} +diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig +index 77cb342..52b31a4 100644 +--- a/drivers/serial/Kconfig ++++ b/drivers/serial/Kconfig +@@ -1421,4 +1421,23 @@ config SPORT_BAUD_RATE + default 19200 if (SERIAL_SPORT_BAUD_RATE_19200) + default 9600 if (SERIAL_SPORT_BAUD_RATE_9600) + ++config SERIAL_BCM63XX ++ tristate "bcm63xx serial port support" ++ select SERIAL_CORE ++ depends on BCM63XX ++ help ++ If you have a bcm63xx CPU, you can enable its onboard ++ serial port by enabling this options. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called bcm963xx_uart. ++ ++config SERIAL_BCM63XX_CONSOLE ++ bool "Console on bcm63xx serial port" ++ depends on SERIAL_BCM63XX ++ select SERIAL_CORE_CONSOLE ++ help ++ If you have enabled the serial port on the bcm63xx CPU ++ you can make it the console by answering Y to this option. ++ + endmenu +diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile +index 7e7383e..84f6cfc 100644 +--- a/drivers/serial/Makefile ++++ b/drivers/serial/Makefile +@@ -24,6 +24,7 @@ obj-$(CONFIG_SERIAL_CLPS711X) += clps711x.o + obj-$(CONFIG_SERIAL_PXA) += pxa.o + obj-$(CONFIG_SERIAL_PNX8XXX) += pnx8xxx_uart.o + obj-$(CONFIG_SERIAL_SA1100) += sa1100.o ++obj-$(CONFIG_SERIAL_BCM63XX) += bcm63xx_uart.o + obj-$(CONFIG_SERIAL_BFIN) += bfin_5xx.o + obj-$(CONFIG_SERIAL_BFIN_SPORT) += bfin_sport_uart.o + obj-$(CONFIG_SERIAL_SAMSUNG) += samsung.o +diff --git a/drivers/serial/bcm63xx_uart.c b/drivers/serial/bcm63xx_uart.c +new file mode 100644 +index 0000000..606f4d6 +--- /dev/null ++++ b/drivers/serial/bcm63xx_uart.c +@@ -0,0 +1,890 @@ ++/* ++ * This file is subject to the terms and conditions of the GNU General Public ++ * License. See the file "COPYING" in the main directory of this archive ++ * for more details. ++ * ++ * Derived from many drivers using generic_serial interface. ++ * ++ * Copyright (C) 2008 Maxime Bizon ++ * ++ * Serial driver for BCM63xx integrated UART. ++ * ++ * Hardware flow control was _not_ tested since I only have RX/TX on ++ * my board. ++ */ ++ ++#if defined(CONFIG_SERIAL_BCM63XX_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) ++#define SUPPORT_SYSRQ ++#endif ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++#define BCM63XX_NR_UARTS 1 ++ ++static struct uart_port ports[BCM63XX_NR_UARTS]; ++ ++/* ++ * rx interrupt mask / stat ++ * ++ * mask: ++ * - rx fifo full ++ * - rx fifo above threshold ++ * - rx fifo not empty for too long ++ */ ++#define UART_RX_INT_MASK (UART_IR_MASK(UART_IR_RXOVER) | \ ++ UART_IR_MASK(UART_IR_RXTHRESH) | \ ++ UART_IR_MASK(UART_IR_RXTIMEOUT)) ++ ++#define UART_RX_INT_STAT (UART_IR_STAT(UART_IR_RXOVER) | \ ++ UART_IR_STAT(UART_IR_RXTHRESH) | \ ++ UART_IR_STAT(UART_IR_RXTIMEOUT)) ++ ++/* ++ * tx interrupt mask / stat ++ * ++ * mask: ++ * - tx fifo empty ++ * - tx fifo below threshold ++ */ ++#define UART_TX_INT_MASK (UART_IR_MASK(UART_IR_TXEMPTY) | \ ++ UART_IR_MASK(UART_IR_TXTRESH)) ++ ++#define UART_TX_INT_STAT (UART_IR_STAT(UART_IR_TXEMPTY) | \ ++ UART_IR_STAT(UART_IR_TXTRESH)) ++ ++/* ++ * external input interrupt ++ * ++ * mask: any edge on CTS, DCD ++ */ ++#define UART_EXTINP_INT_MASK (UART_EXTINP_IRMASK(UART_EXTINP_IR_CTS) | \ ++ UART_EXTINP_IRMASK(UART_EXTINP_IR_DCD)) ++ ++/* ++ * handy uart register accessor ++ */ ++static inline unsigned int bcm_uart_readl(struct uart_port *port, ++ unsigned int offset) ++{ ++ return bcm_readl(port->membase + offset); ++} ++ ++static inline void bcm_uart_writel(struct uart_port *port, ++ unsigned int value, unsigned int offset) ++{ ++ bcm_writel(value, port->membase + offset); ++} ++ ++/* ++ * serial core request to check if uart tx fifo is empty ++ */ ++static unsigned int bcm_uart_tx_empty(struct uart_port *port) ++{ ++ unsigned int val; ++ ++ val = bcm_uart_readl(port, UART_IR_REG); ++ return (val & UART_IR_STAT(UART_IR_TXEMPTY)) ? 1 : 0; ++} ++ ++/* ++ * serial core request to set RTS and DTR pin state and loopback mode ++ */ ++static void bcm_uart_set_mctrl(struct uart_port *port, unsigned int mctrl) ++{ ++ unsigned int val; ++ ++ val = bcm_uart_readl(port, UART_MCTL_REG); ++ val &= ~(UART_MCTL_DTR_MASK | UART_MCTL_RTS_MASK); ++ /* invert of written value is reflected on the pin */ ++ if (!(mctrl & TIOCM_DTR)) ++ val |= UART_MCTL_DTR_MASK; ++ if (!(mctrl & TIOCM_RTS)) ++ val |= UART_MCTL_RTS_MASK; ++ bcm_uart_writel(port, val, UART_MCTL_REG); ++ ++ val = bcm_uart_readl(port, UART_CTL_REG); ++ if (mctrl & TIOCM_LOOP) ++ val |= UART_CTL_LOOPBACK_MASK; ++ else ++ val &= ~UART_CTL_LOOPBACK_MASK; ++ bcm_uart_writel(port, val, UART_CTL_REG); ++} ++ ++/* ++ * serial core request to return RI, CTS, DCD and DSR pin state ++ */ ++static unsigned int bcm_uart_get_mctrl(struct uart_port *port) ++{ ++ unsigned int val, mctrl; ++ ++ mctrl = 0; ++ val = bcm_uart_readl(port, UART_EXTINP_REG); ++ if (val & UART_EXTINP_RI_MASK) ++ mctrl |= TIOCM_RI; ++ if (val & UART_EXTINP_CTS_MASK) ++ mctrl |= TIOCM_CTS; ++ if (val & UART_EXTINP_DCD_MASK) ++ mctrl |= TIOCM_CD; ++ if (val & UART_EXTINP_DSR_MASK) ++ mctrl |= TIOCM_DSR; ++ return mctrl; ++} ++ ++/* ++ * serial core request to disable tx ASAP (used for flow control) ++ */ ++static void bcm_uart_stop_tx(struct uart_port *port) ++{ ++ unsigned int val; ++ ++ val = bcm_uart_readl(port, UART_CTL_REG); ++ val &= ~(UART_CTL_TXEN_MASK); ++ bcm_uart_writel(port, val, UART_CTL_REG); ++ ++ val = bcm_uart_readl(port, UART_IR_REG); ++ val &= ~UART_TX_INT_MASK; ++ bcm_uart_writel(port, val, UART_IR_REG); ++} ++ ++/* ++ * serial core request to (re)enable tx ++ */ ++static void bcm_uart_start_tx(struct uart_port *port) ++{ ++ unsigned int val; ++ ++ val = bcm_uart_readl(port, UART_IR_REG); ++ val |= UART_TX_INT_MASK; ++ bcm_uart_writel(port, val, UART_IR_REG); ++ ++ val = bcm_uart_readl(port, UART_CTL_REG); ++ val |= UART_CTL_TXEN_MASK; ++ bcm_uart_writel(port, val, UART_CTL_REG); ++} ++ ++/* ++ * serial core request to stop rx, called before port shutdown ++ */ ++static void bcm_uart_stop_rx(struct uart_port *port) ++{ ++ unsigned int val; ++ ++ val = bcm_uart_readl(port, UART_IR_REG); ++ val &= ~UART_RX_INT_MASK; ++ bcm_uart_writel(port, val, UART_IR_REG); ++} ++ ++/* ++ * serial core request to enable modem status interrupt reporting ++ */ ++static void bcm_uart_enable_ms(struct uart_port *port) ++{ ++ unsigned int val; ++ ++ val = bcm_uart_readl(port, UART_IR_REG); ++ val |= UART_IR_MASK(UART_IR_EXTIP); ++ bcm_uart_writel(port, val, UART_IR_REG); ++} ++ ++/* ++ * serial core request to start/stop emitting break char ++ */ ++static void bcm_uart_break_ctl(struct uart_port *port, int ctl) ++{ ++ unsigned long flags; ++ unsigned int val; ++ ++ spin_lock_irqsave(&port->lock, flags); ++ ++ val = bcm_uart_readl(port, UART_CTL_REG); ++ if (ctl) ++ val |= UART_CTL_XMITBRK_MASK; ++ else ++ val &= ~UART_CTL_XMITBRK_MASK; ++ bcm_uart_writel(port, val, UART_CTL_REG); ++ ++ spin_unlock_irqrestore(&port->lock, flags); ++} ++ ++/* ++ * return port type in string format ++ */ ++static const char *bcm_uart_type(struct uart_port *port) ++{ ++ return (port->type == PORT_BCM63XX) ? "bcm63xx_uart" : NULL; ++} ++ ++/* ++ * read all chars in rx fifo and send them to core ++ */ ++static void bcm_uart_do_rx(struct uart_port *port) ++{ ++ struct tty_struct *tty; ++ unsigned int max_count; ++ ++ /* limit number of char read in interrupt, should not be ++ * higher than fifo size anyway since we're much faster than ++ * serial port */ ++ max_count = 32; ++ tty = port->info->port.tty; ++ do { ++ unsigned int iestat, c, cstat; ++ char flag; ++ ++ /* get overrun/fifo empty information from ier ++ * register */ ++ iestat = bcm_uart_readl(port, UART_IR_REG); ++ if (!(iestat & UART_IR_STAT(UART_IR_RXNOTEMPTY))) ++ break; ++ ++ cstat = c = bcm_uart_readl(port, UART_FIFO_REG); ++ port->icount.rx++; ++ flag = TTY_NORMAL; ++ c &= 0xff; ++ ++ if (unlikely((cstat & UART_FIFO_ANYERR_MASK))) { ++ /* do stats first */ ++ if (cstat & UART_FIFO_BRKDET_MASK) { ++ port->icount.brk++; ++ if (uart_handle_break(port)) ++ continue; ++ } ++ ++ if (cstat & UART_FIFO_PARERR_MASK) ++ port->icount.parity++; ++ if (cstat & UART_FIFO_FRAMEERR_MASK) ++ port->icount.frame++; ++ ++ /* update flag wrt read_status_mask */ ++ cstat &= port->read_status_mask; ++ if (cstat & UART_FIFO_BRKDET_MASK) ++ flag = TTY_BREAK; ++ if (cstat & UART_FIFO_FRAMEERR_MASK) ++ flag = TTY_FRAME; ++ if (cstat & UART_FIFO_PARERR_MASK) ++ flag = TTY_PARITY; ++ } ++ ++ if (uart_handle_sysrq_char(port, c)) ++ continue; ++ ++ if (unlikely(iestat & UART_IR_STAT(UART_IR_RXOVER))) { ++ port->icount.overrun++; ++ tty_insert_flip_char(tty, 0, TTY_OVERRUN); ++ } ++ ++ if ((cstat & port->ignore_status_mask) == 0) ++ tty_insert_flip_char(tty, c, flag); ++ ++ } while (--max_count); ++ ++ tty_flip_buffer_push(tty); ++} ++ ++/* ++ * fill tx fifo with chars to send, stop when fifo is about to be full ++ * or when all chars have been sent. ++ */ ++static void bcm_uart_do_tx(struct uart_port *port) ++{ ++ struct circ_buf *xmit; ++ unsigned int val, max_count; ++ ++ if (port->x_char) { ++ bcm_uart_writel(port, port->x_char, UART_FIFO_REG); ++ port->icount.tx++; ++ port->x_char = 0; ++ return; ++ } ++ ++ if (uart_tx_stopped(port)) { ++ bcm_uart_stop_tx(port); ++ return; ++ } ++ ++ xmit = &port->info->xmit; ++ if (uart_circ_empty(xmit)) ++ goto txq_empty; ++ ++ val = bcm_uart_readl(port, UART_MCTL_REG); ++ val = (val & UART_MCTL_TXFIFOFILL_MASK) >> UART_MCTL_TXFIFOFILL_SHIFT; ++ max_count = port->fifosize - val; ++ ++ while (max_count--) { ++ unsigned int c; ++ ++ c = xmit->buf[xmit->tail]; ++ bcm_uart_writel(port, c, UART_FIFO_REG); ++ xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); ++ port->icount.tx++; ++ if (uart_circ_empty(xmit)) ++ break; ++ } ++ ++ if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) ++ uart_write_wakeup(port); ++ ++ if (uart_circ_empty(xmit)) ++ goto txq_empty; ++ return; ++ ++txq_empty: ++ /* nothing to send, disable transmit interrupt */ ++ val = bcm_uart_readl(port, UART_IR_REG); ++ val &= ~UART_TX_INT_MASK; ++ bcm_uart_writel(port, val, UART_IR_REG); ++ return; ++} ++ ++/* ++ * process uart interrupt ++ */ ++static irqreturn_t bcm_uart_interrupt(int irq, void *dev_id) ++{ ++ struct uart_port *port; ++ unsigned int irqstat; ++ ++ port = dev_id; ++ spin_lock(&port->lock); ++ ++ irqstat = bcm_uart_readl(port, UART_IR_REG); ++ if (irqstat & UART_RX_INT_STAT) ++ bcm_uart_do_rx(port); ++ ++ if (irqstat & UART_TX_INT_STAT) ++ bcm_uart_do_tx(port); ++ ++ if (irqstat & UART_IR_MASK(UART_IR_EXTIP)) { ++ unsigned int estat; ++ ++ estat = bcm_uart_readl(port, UART_EXTINP_REG); ++ if (estat & UART_EXTINP_IRSTAT(UART_EXTINP_IR_CTS)) ++ uart_handle_cts_change(port, ++ estat & UART_EXTINP_CTS_MASK); ++ if (estat & UART_EXTINP_IRSTAT(UART_EXTINP_IR_DCD)) ++ uart_handle_dcd_change(port, ++ estat & UART_EXTINP_DCD_MASK); ++ } ++ ++ spin_unlock(&port->lock); ++ return IRQ_HANDLED; ++} ++ ++/* ++ * enable rx & tx operation on uart ++ */ ++static void bcm_uart_enable(struct uart_port *port) ++{ ++ unsigned int val; ++ ++ val = bcm_uart_readl(port, UART_CTL_REG); ++ val |= (UART_CTL_BRGEN_MASK | UART_CTL_TXEN_MASK | UART_CTL_RXEN_MASK); ++ bcm_uart_writel(port, val, UART_CTL_REG); ++} ++ ++/* ++ * disable rx & tx operation on uart ++ */ ++static void bcm_uart_disable(struct uart_port *port) ++{ ++ unsigned int val; ++ ++ val = bcm_uart_readl(port, UART_CTL_REG); ++ val &= ~(UART_CTL_BRGEN_MASK | UART_CTL_TXEN_MASK | ++ UART_CTL_RXEN_MASK); ++ bcm_uart_writel(port, val, UART_CTL_REG); ++} ++ ++/* ++ * clear all unread data in rx fifo and unsent data in tx fifo ++ */ ++static void bcm_uart_flush(struct uart_port *port) ++{ ++ unsigned int val; ++ ++ /* empty rx and tx fifo */ ++ val = bcm_uart_readl(port, UART_CTL_REG); ++ val |= UART_CTL_RSTRXFIFO_MASK | UART_CTL_RSTTXFIFO_MASK; ++ bcm_uart_writel(port, val, UART_CTL_REG); ++ ++ /* read any pending char to make sure all irq status are ++ * cleared */ ++ (void)bcm_uart_readl(port, UART_FIFO_REG); ++} ++ ++/* ++ * serial core request to initialize uart and start rx operation ++ */ ++static int bcm_uart_startup(struct uart_port *port) ++{ ++ unsigned int val; ++ int ret; ++ ++ /* mask all irq and flush port */ ++ bcm_uart_disable(port); ++ bcm_uart_writel(port, 0, UART_IR_REG); ++ bcm_uart_flush(port); ++ ++ /* clear any pending external input interrupt */ ++ (void)bcm_uart_readl(port, UART_EXTINP_REG); ++ ++ /* set rx/tx fifo thresh to fifo half size */ ++ val = bcm_uart_readl(port, UART_MCTL_REG); ++ val &= ~(UART_MCTL_RXFIFOTHRESH_MASK | UART_MCTL_TXFIFOTHRESH_MASK); ++ val |= (port->fifosize / 2) << UART_MCTL_RXFIFOTHRESH_SHIFT; ++ val |= (port->fifosize / 2) << UART_MCTL_TXFIFOTHRESH_SHIFT; ++ bcm_uart_writel(port, val, UART_MCTL_REG); ++ ++ /* set rx fifo timeout to 1 char time */ ++ val = bcm_uart_readl(port, UART_CTL_REG); ++ val &= ~UART_CTL_RXTMOUTCNT_MASK; ++ val |= 1 << UART_CTL_RXTMOUTCNT_SHIFT; ++ bcm_uart_writel(port, val, UART_CTL_REG); ++ ++ /* report any edge on dcd and cts */ ++ val = UART_EXTINP_INT_MASK; ++ val |= UART_EXTINP_DCD_NOSENSE_MASK; ++ val |= UART_EXTINP_CTS_NOSENSE_MASK; ++ bcm_uart_writel(port, val, UART_EXTINP_REG); ++ ++ /* register irq and enable rx interrupts */ ++ ret = request_irq(port->irq, bcm_uart_interrupt, 0, ++ bcm_uart_type(port), port); ++ if (ret) ++ return ret; ++ bcm_uart_writel(port, UART_RX_INT_MASK, UART_IR_REG); ++ bcm_uart_enable(port); ++ return 0; ++} ++ ++/* ++ * serial core request to flush & disable uart ++ */ ++static void bcm_uart_shutdown(struct uart_port *port) ++{ ++ unsigned long flags; ++ ++ spin_lock_irqsave(&port->lock, flags); ++ bcm_uart_writel(port, 0, UART_IR_REG); ++ spin_unlock_irqrestore(&port->lock, flags); ++ ++ bcm_uart_disable(port); ++ bcm_uart_flush(port); ++ free_irq(port->irq, port); ++} ++ ++/* ++ * serial core request to change current uart setting ++ */ ++static void bcm_uart_set_termios(struct uart_port *port, ++ struct ktermios *new, ++ struct ktermios *old) ++{ ++ unsigned int ctl, baud, quot, ier; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&port->lock, flags); ++ ++ /* disable uart while changing speed */ ++ bcm_uart_disable(port); ++ bcm_uart_flush(port); ++ ++ /* update Control register */ ++ ctl = bcm_uart_readl(port, UART_CTL_REG); ++ ctl &= ~UART_CTL_BITSPERSYM_MASK; ++ ++ switch (new->c_cflag & CSIZE) { ++ case CS5: ++ ctl |= (0 << UART_CTL_BITSPERSYM_SHIFT); ++ break; ++ case CS6: ++ ctl |= (1 << UART_CTL_BITSPERSYM_SHIFT); ++ break; ++ case CS7: ++ ctl |= (2 << UART_CTL_BITSPERSYM_SHIFT); ++ break; ++ default: ++ ctl |= (3 << UART_CTL_BITSPERSYM_SHIFT); ++ break; ++ } ++ ++ ctl &= ~UART_CTL_STOPBITS_MASK; ++ if (new->c_cflag & CSTOPB) ++ ctl |= UART_CTL_STOPBITS_2; ++ else ++ ctl |= UART_CTL_STOPBITS_1; ++ ++ ctl &= ~(UART_CTL_RXPAREN_MASK | UART_CTL_TXPAREN_MASK); ++ if (new->c_cflag & PARENB) ++ ctl |= (UART_CTL_RXPAREN_MASK | UART_CTL_TXPAREN_MASK); ++ ctl &= ~(UART_CTL_RXPAREVEN_MASK | UART_CTL_TXPAREVEN_MASK); ++ if (new->c_cflag & PARODD) ++ ctl |= (UART_CTL_RXPAREVEN_MASK | UART_CTL_TXPAREVEN_MASK); ++ bcm_uart_writel(port, ctl, UART_CTL_REG); ++ ++ /* update Baudword register */ ++ baud = uart_get_baud_rate(port, new, old, 0, port->uartclk / 16); ++ quot = uart_get_divisor(port, baud) - 1; ++ bcm_uart_writel(port, quot, UART_BAUD_REG); ++ ++ /* update Interrupt register */ ++ ier = bcm_uart_readl(port, UART_IR_REG); ++ ++ ier &= ~UART_IR_MASK(UART_IR_EXTIP); ++ if (UART_ENABLE_MS(port, new->c_cflag)) ++ ier |= UART_IR_MASK(UART_IR_EXTIP); ++ ++ bcm_uart_writel(port, ier, UART_IR_REG); ++ ++ /* update read/ignore mask */ ++ port->read_status_mask = UART_FIFO_VALID_MASK; ++ if (new->c_iflag & INPCK) { ++ port->read_status_mask |= UART_FIFO_FRAMEERR_MASK; ++ port->read_status_mask |= UART_FIFO_PARERR_MASK; ++ } ++ if (new->c_iflag & (BRKINT)) ++ port->read_status_mask |= UART_FIFO_BRKDET_MASK; ++ ++ port->ignore_status_mask = 0; ++ if (new->c_iflag & IGNPAR) ++ port->ignore_status_mask |= UART_FIFO_PARERR_MASK; ++ if (new->c_iflag & IGNBRK) ++ port->ignore_status_mask |= UART_FIFO_BRKDET_MASK; ++ if (!(new->c_cflag & CREAD)) ++ port->ignore_status_mask |= UART_FIFO_VALID_MASK; ++ ++ uart_update_timeout(port, new->c_cflag, baud); ++ bcm_uart_enable(port); ++ spin_unlock_irqrestore(&port->lock, flags); ++} ++ ++/* ++ * serial core request to claim uart iomem ++ */ ++static int bcm_uart_request_port(struct uart_port *port) ++{ ++ unsigned int size; ++ ++ size = RSET_UART_SIZE; ++ if (!request_mem_region(port->mapbase, size, "bcm63xx")) { ++ dev_err(port->dev, "Memory region busy\n"); ++ return -EBUSY; ++ } ++ ++ port->membase = ioremap(port->mapbase, size); ++ if (!port->membase) { ++ dev_err(port->dev, "Unable to map registers\n"); ++ release_mem_region(port->mapbase, size); ++ return -EBUSY; ++ } ++ return 0; ++} ++ ++/* ++ * serial core request to release uart iomem ++ */ ++static void bcm_uart_release_port(struct uart_port *port) ++{ ++ release_mem_region(port->mapbase, RSET_UART_SIZE); ++ iounmap(port->membase); ++} ++ ++/* ++ * serial core request to do any port required autoconfiguration ++ */ ++static void bcm_uart_config_port(struct uart_port *port, int flags) ++{ ++ if (flags & UART_CONFIG_TYPE) { ++ if (bcm_uart_request_port(port)) ++ return; ++ port->type = PORT_BCM63XX; ++ } ++} ++ ++/* ++ * serial core request to check that port information in serinfo are ++ * suitable ++ */ ++static int bcm_uart_verify_port(struct uart_port *port, ++ struct serial_struct *serinfo) ++{ ++ if (port->type != PORT_BCM63XX) ++ return -EINVAL; ++ if (port->irq != serinfo->irq) ++ return -EINVAL; ++ if (port->iotype != serinfo->io_type) ++ return -EINVAL; ++ if (port->mapbase != (unsigned long)serinfo->iomem_base) ++ return -EINVAL; ++ return 0; ++} ++ ++/* serial core callbacks */ ++static struct uart_ops bcm_uart_ops = { ++ .tx_empty = bcm_uart_tx_empty, ++ .get_mctrl = bcm_uart_get_mctrl, ++ .set_mctrl = bcm_uart_set_mctrl, ++ .start_tx = bcm_uart_start_tx, ++ .stop_tx = bcm_uart_stop_tx, ++ .stop_rx = bcm_uart_stop_rx, ++ .enable_ms = bcm_uart_enable_ms, ++ .break_ctl = bcm_uart_break_ctl, ++ .startup = bcm_uart_startup, ++ .shutdown = bcm_uart_shutdown, ++ .set_termios = bcm_uart_set_termios, ++ .type = bcm_uart_type, ++ .release_port = bcm_uart_release_port, ++ .request_port = bcm_uart_request_port, ++ .config_port = bcm_uart_config_port, ++ .verify_port = bcm_uart_verify_port, ++}; ++ ++ ++ ++#ifdef CONFIG_SERIAL_BCM63XX_CONSOLE ++static inline void wait_for_xmitr(struct uart_port *port) ++{ ++ unsigned int tmout; ++ ++ /* Wait up to 10ms for the character(s) to be sent. */ ++ tmout = 10000; ++ while (--tmout) { ++ unsigned int val; ++ ++ val = bcm_uart_readl(port, UART_IR_REG); ++ if (val & UART_IR_STAT(UART_IR_TXEMPTY)) ++ break; ++ udelay(1); ++ } ++ ++ /* Wait up to 1s for flow control if necessary */ ++ if (port->flags & UPF_CONS_FLOW) { ++ tmout = 1000000; ++ while (--tmout) { ++ unsigned int val; ++ ++ val = bcm_uart_readl(port, UART_EXTINP_REG); ++ if (val & UART_EXTINP_CTS_MASK) ++ break; ++ udelay(1); ++ } ++ } ++} ++ ++/* ++ * output given char ++ */ ++static void bcm_console_putchar(struct uart_port *port, int ch) ++{ ++ wait_for_xmitr(port); ++ bcm_uart_writel(port, ch, UART_FIFO_REG); ++} ++ ++/* ++ * console core request to output given string ++ */ ++static void bcm_console_write(struct console *co, const char *s, ++ unsigned int count) ++{ ++ struct uart_port *port; ++ unsigned long flags; ++ int locked; ++ ++ port = &ports[co->index]; ++ ++ local_irq_save(flags); ++ if (port->sysrq) { ++ /* bcm_uart_interrupt() already took the lock */ ++ locked = 0; ++ } else if (oops_in_progress) { ++ locked = spin_trylock(&port->lock); ++ } else { ++ spin_lock(&port->lock); ++ locked = 1; ++ } ++ ++ /* call helper to deal with \r\n */ ++ uart_console_write(port, s, count, bcm_console_putchar); ++ ++ /* and wait for char to be transmitted */ ++ wait_for_xmitr(port); ++ ++ if (locked) ++ spin_unlock(&port->lock); ++ local_irq_restore(flags); ++} ++ ++/* ++ * console core request to setup given console, find matching uart ++ * port and setup it. ++ */ ++static int bcm_console_setup(struct console *co, char *options) ++{ ++ struct uart_port *port; ++ int baud = 9600; ++ int bits = 8; ++ int parity = 'n'; ++ int flow = 'n'; ++ ++ if (co->index < 0 || co->index >= BCM63XX_NR_UARTS) ++ return -EINVAL; ++ port = &ports[co->index]; ++ if (!port->membase) ++ return -ENODEV; ++ if (options) ++ uart_parse_options(options, &baud, &parity, &bits, &flow); ++ ++ return uart_set_options(port, co, baud, parity, bits, flow); ++} ++ ++static struct uart_driver bcm_uart_driver; ++ ++static struct console bcm63xx_console = { ++ .name = "ttyS", ++ .write = bcm_console_write, ++ .device = uart_console_device, ++ .setup = bcm_console_setup, ++ .flags = CON_PRINTBUFFER, ++ .index = -1, ++ .data = &bcm_uart_driver, ++}; ++ ++static int __init bcm63xx_console_init(void) ++{ ++ register_console(&bcm63xx_console); ++ return 0; ++} ++ ++console_initcall(bcm63xx_console_init); ++ ++#define BCM63XX_CONSOLE &bcm63xx_console ++#else ++#define BCM63XX_CONSOLE NULL ++#endif /* CONFIG_SERIAL_BCM63XX_CONSOLE */ ++ ++static struct uart_driver bcm_uart_driver = { ++ .owner = THIS_MODULE, ++ .driver_name = "bcm63xx_uart", ++ .dev_name = "ttyS", ++ .major = TTY_MAJOR, ++ .minor = 64, ++ .nr = 1, ++ .cons = BCM63XX_CONSOLE, ++}; ++ ++/* ++ * platform driver probe/remove callback ++ */ ++static int __devinit bcm_uart_probe(struct platform_device *pdev) ++{ ++ struct resource *res_mem, *res_irq; ++ struct uart_port *port; ++ struct clk *clk; ++ int ret; ++ ++ if (pdev->id < 0 || pdev->id >= BCM63XX_NR_UARTS) ++ return -EINVAL; ++ ++ if (ports[pdev->id].membase) ++ return -EBUSY; ++ ++ res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ if (!res_mem) ++ return -ENODEV; ++ ++ res_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); ++ if (!res_irq) ++ return -ENODEV; ++ ++ clk = clk_get(&pdev->dev, "periph"); ++ if (IS_ERR(clk)) ++ return -ENODEV; ++ ++ port = &ports[pdev->id]; ++ memset(port, 0, sizeof(*port)); ++ port->iotype = UPIO_MEM; ++ port->mapbase = res_mem->start; ++ port->irq = res_irq->start; ++ port->ops = &bcm_uart_ops; ++ port->flags = UPF_BOOT_AUTOCONF; ++ port->dev = &pdev->dev; ++ port->fifosize = 16; ++ port->uartclk = clk_get_rate(clk) / 2; ++ clk_put(clk); ++ ++ ret = uart_add_one_port(&bcm_uart_driver, port); ++ if (ret) { ++ kfree(port); ++ return ret; ++ } ++ platform_set_drvdata(pdev, port); ++ return 0; ++} ++ ++static int __devexit bcm_uart_remove(struct platform_device *pdev) ++{ ++ struct uart_port *port; ++ ++ port = platform_get_drvdata(pdev); ++ uart_remove_one_port(&bcm_uart_driver, port); ++ platform_set_drvdata(pdev, NULL); ++ /* mark port as free */ ++ ports[pdev->id].membase = 0; ++ return 0; ++} ++ ++/* ++ * platform driver stuff ++ */ ++static struct platform_driver bcm_uart_platform_driver = { ++ .probe = bcm_uart_probe, ++ .remove = __devexit_p(bcm_uart_remove), ++ .driver = { ++ .owner = THIS_MODULE, ++ .name = "bcm63xx_uart", ++ }, ++}; ++ ++static int __init bcm_uart_init(void) ++{ ++ int ret; ++ ++ ret = uart_register_driver(&bcm_uart_driver); ++ if (ret) ++ return ret; ++ ++ ret = platform_driver_register(&bcm_uart_platform_driver); ++ if (ret) ++ uart_unregister_driver(&bcm_uart_driver); ++ ++ return ret; ++} ++ ++static void __exit bcm_uart_exit(void) ++{ ++ platform_driver_unregister(&bcm_uart_platform_driver); ++ uart_unregister_driver(&bcm_uart_driver); ++} ++ ++module_init(bcm_uart_init); ++module_exit(bcm_uart_exit); ++ ++MODULE_AUTHOR("Maxime Bizon "); ++MODULE_DESCRIPTION("Broadcom 63 +-- +1.5.4.3 + -- cgit v1.2.3