mcf.c revision 9671f09921d93e722a28ae9610d478e092ac5466
149aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer/****************************************************************************/ 249aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer 349aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer/* 449aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer * mcf.c -- Freescale ColdFire UART driver 549aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer * 649aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer * (C) Copyright 2003-2007, Greg Ungerer <gerg@snapgear.com> 749aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer * 849aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer * This program is free software; you can redistribute it and/or modify 949aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer * it under the terms of the GNU General Public License as published by 1049aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer * the Free Software Foundation; either version 2 of the License, or 1149aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer * (at your option) any later version. 1249aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer */ 1349aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer 1449aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer/****************************************************************************/ 1549aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer 1649aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer#include <linux/kernel.h> 1749aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer#include <linux/init.h> 1849aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer#include <linux/interrupt.h> 1949aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer#include <linux/module.h> 2049aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer#include <linux/console.h> 2149aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer#include <linux/tty.h> 2249aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer#include <linux/tty_flip.h> 2349aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer#include <linux/serial.h> 2449aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer#include <linux/serial_core.h> 2549aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer#include <linux/io.h> 2649aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer#include <asm/coldfire.h> 2749aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer#include <asm/mcfsim.h> 2849aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer#include <asm/mcfuart.h> 2949aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer#include <asm/nettel.h> 3049aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer 3149aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer/****************************************************************************/ 3249aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer 3349aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer/* 3449aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer * Some boards implement the DTR/DCD lines using GPIO lines, most 3549aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer * don't. Dummy out the access macros for those that don't. Those 3649aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer * that do should define these macros somewhere in there board 3749aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer * specific inlude files. 3849aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer */ 3949aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer#if !defined(mcf_getppdcd) 4049aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer#define mcf_getppdcd(p) (1) 4149aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer#endif 4249aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer#if !defined(mcf_getppdtr) 4349aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer#define mcf_getppdtr(p) (1) 4449aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer#endif 4549aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer#if !defined(mcf_setppdtr) 4649aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer#define mcf_setppdtr(p, v) do { } while (0) 4749aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer#endif 4849aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer 4949aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer/****************************************************************************/ 5049aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer 5149aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer/* 5249aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer * Local per-uart structure. 5349aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer */ 5449aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungererstruct mcf_uart { 5549aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer struct uart_port port; 5649aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer unsigned int sigs; /* Local copy of line sigs */ 5749aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer unsigned char imr; /* Local IMR mirror */ 5849aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer}; 5949aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer 6049aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer/****************************************************************************/ 6149aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer 6249aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungererstatic unsigned int mcf_tx_empty(struct uart_port *port) 6349aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer{ 6449aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer return (readb(port->membase + MCFUART_USR) & MCFUART_USR_TXEMPTY) ? 6549aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer TIOCSER_TEMT : 0; 6649aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer} 6749aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer 6849aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer/****************************************************************************/ 6949aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer 7049aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungererstatic unsigned int mcf_get_mctrl(struct uart_port *port) 7149aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer{ 729f69ba86d66297189916ceae401fe0944a207714Greg Ungerer struct mcf_uart *pp = container_of(port, struct mcf_uart, port); 7349aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer unsigned int sigs; 7449aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer 7549aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer sigs = (readb(port->membase + MCFUART_UIPR) & MCFUART_UIPR_CTS) ? 7649aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer 0 : TIOCM_CTS; 7749aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer sigs |= (pp->sigs & TIOCM_RTS); 7849aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer sigs |= (mcf_getppdcd(port->line) ? TIOCM_CD : 0); 7949aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer sigs |= (mcf_getppdtr(port->line) ? TIOCM_DTR : 0); 800ac8382e9cfdd723445692fc97aaa4643929750dYury Georgievskiy 8149aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer return sigs; 8249aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer} 8349aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer 8449aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer/****************************************************************************/ 8549aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer 8649aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungererstatic void mcf_set_mctrl(struct uart_port *port, unsigned int sigs) 8749aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer{ 889f69ba86d66297189916ceae401fe0944a207714Greg Ungerer struct mcf_uart *pp = container_of(port, struct mcf_uart, port); 8949aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer 9049aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer pp->sigs = sigs; 9149aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer mcf_setppdtr(port->line, (sigs & TIOCM_DTR)); 9249aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer if (sigs & TIOCM_RTS) 9349aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer writeb(MCFUART_UOP_RTS, port->membase + MCFUART_UOP1); 9449aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer else 9549aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer writeb(MCFUART_UOP_RTS, port->membase + MCFUART_UOP0); 9649aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer} 9749aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer 9849aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer/****************************************************************************/ 9949aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer 10049aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungererstatic void mcf_start_tx(struct uart_port *port) 10149aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer{ 1029f69ba86d66297189916ceae401fe0944a207714Greg Ungerer struct mcf_uart *pp = container_of(port, struct mcf_uart, port); 10349aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer 10449aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer pp->imr |= MCFUART_UIR_TXREADY; 10549aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer writeb(pp->imr, port->membase + MCFUART_UIMR); 10649aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer} 10749aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer 10849aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer/****************************************************************************/ 10949aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer 11049aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungererstatic void mcf_stop_tx(struct uart_port *port) 11149aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer{ 1129f69ba86d66297189916ceae401fe0944a207714Greg Ungerer struct mcf_uart *pp = container_of(port, struct mcf_uart, port); 11349aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer 11449aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer pp->imr &= ~MCFUART_UIR_TXREADY; 11549aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer writeb(pp->imr, port->membase + MCFUART_UIMR); 11649aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer} 11749aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer 11849aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer/****************************************************************************/ 11949aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer 12049aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungererstatic void mcf_stop_rx(struct uart_port *port) 12149aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer{ 1229f69ba86d66297189916ceae401fe0944a207714Greg Ungerer struct mcf_uart *pp = container_of(port, struct mcf_uart, port); 12349aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer 12449aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer pp->imr &= ~MCFUART_UIR_RXREADY; 12549aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer writeb(pp->imr, port->membase + MCFUART_UIMR); 12649aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer} 12749aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer 12849aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer/****************************************************************************/ 12949aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer 13049aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungererstatic void mcf_break_ctl(struct uart_port *port, int break_state) 13149aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer{ 13249aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer unsigned long flags; 13349aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer 13449aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer spin_lock_irqsave(&port->lock, flags); 13549aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer if (break_state == -1) 13649aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer writeb(MCFUART_UCR_CMDBREAKSTART, port->membase + MCFUART_UCR); 13749aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer else 13849aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer writeb(MCFUART_UCR_CMDBREAKSTOP, port->membase + MCFUART_UCR); 13949aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer spin_unlock_irqrestore(&port->lock, flags); 14049aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer} 14149aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer 14249aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer/****************************************************************************/ 14349aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer 14449aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungererstatic void mcf_enable_ms(struct uart_port *port) 14549aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer{ 14649aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer} 14749aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer 14849aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer/****************************************************************************/ 14949aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer 15049aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungererstatic int mcf_startup(struct uart_port *port) 15149aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer{ 1529f69ba86d66297189916ceae401fe0944a207714Greg Ungerer struct mcf_uart *pp = container_of(port, struct mcf_uart, port); 15349aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer unsigned long flags; 15449aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer 15549aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer spin_lock_irqsave(&port->lock, flags); 15649aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer 15749aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer /* Reset UART, get it into known state... */ 15849aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer writeb(MCFUART_UCR_CMDRESETRX, port->membase + MCFUART_UCR); 15949aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer writeb(MCFUART_UCR_CMDRESETTX, port->membase + MCFUART_UCR); 16049aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer 16149aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer /* Enable the UART transmitter and receiver */ 16249aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer writeb(MCFUART_UCR_RXENABLE | MCFUART_UCR_TXENABLE, 16349aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer port->membase + MCFUART_UCR); 16449aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer 16549aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer /* Enable RX interrupts now */ 16649aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer pp->imr = MCFUART_UIR_RXREADY; 16749aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer writeb(pp->imr, port->membase + MCFUART_UIMR); 16849aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer 16949aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer spin_unlock_irqrestore(&port->lock, flags); 17049aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer 17149aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer return 0; 17249aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer} 17349aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer 17449aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer/****************************************************************************/ 17549aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer 17649aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungererstatic void mcf_shutdown(struct uart_port *port) 17749aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer{ 1789f69ba86d66297189916ceae401fe0944a207714Greg Ungerer struct mcf_uart *pp = container_of(port, struct mcf_uart, port); 17949aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer unsigned long flags; 18049aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer 18149aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer spin_lock_irqsave(&port->lock, flags); 18249aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer 18349aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer /* Disable all interrupts now */ 18449aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer pp->imr = 0; 18549aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer writeb(pp->imr, port->membase + MCFUART_UIMR); 18649aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer 18749aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer /* Disable UART transmitter and receiver */ 18849aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer writeb(MCFUART_UCR_CMDRESETRX, port->membase + MCFUART_UCR); 18949aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer writeb(MCFUART_UCR_CMDRESETTX, port->membase + MCFUART_UCR); 19049aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer 19149aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer spin_unlock_irqrestore(&port->lock, flags); 19249aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer} 19349aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer 19449aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer/****************************************************************************/ 19549aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer 19649aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungererstatic void mcf_set_termios(struct uart_port *port, struct ktermios *termios, 19749aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer struct ktermios *old) 19849aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer{ 19949aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer unsigned long flags; 20049aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer unsigned int baud, baudclk; 20126a4bc66a6f57299027e04d90b14fe56a44c6d2bJohn Adamson#if defined(CONFIG_M5272) 20226a4bc66a6f57299027e04d90b14fe56a44c6d2bJohn Adamson unsigned int baudfr; 20326a4bc66a6f57299027e04d90b14fe56a44c6d2bJohn Adamson#endif 20449aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer unsigned char mr1, mr2; 20549aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer 20649aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer baud = uart_get_baud_rate(port, termios, old, 0, 230400); 20726a4bc66a6f57299027e04d90b14fe56a44c6d2bJohn Adamson#if defined(CONFIG_M5272) 20826a4bc66a6f57299027e04d90b14fe56a44c6d2bJohn Adamson baudclk = (MCF_BUSCLK / baud) / 32; 20926a4bc66a6f57299027e04d90b14fe56a44c6d2bJohn Adamson baudfr = (((MCF_BUSCLK / baud) + 1) / 2) % 16; 21026a4bc66a6f57299027e04d90b14fe56a44c6d2bJohn Adamson#else 21149aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer baudclk = ((MCF_BUSCLK / baud) + 16) / 32; 21226a4bc66a6f57299027e04d90b14fe56a44c6d2bJohn Adamson#endif 21349aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer 21449aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer mr1 = MCFUART_MR1_RXIRQRDY | MCFUART_MR1_RXERRCHAR; 21549aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer mr2 = 0; 21649aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer 21749aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer switch (termios->c_cflag & CSIZE) { 21849aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer case CS5: mr1 |= MCFUART_MR1_CS5; break; 21949aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer case CS6: mr1 |= MCFUART_MR1_CS6; break; 22049aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer case CS7: mr1 |= MCFUART_MR1_CS7; break; 22149aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer case CS8: 22249aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer default: mr1 |= MCFUART_MR1_CS8; break; 22349aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer } 22449aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer 22549aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer if (termios->c_cflag & PARENB) { 22649aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer if (termios->c_cflag & CMSPAR) { 22749aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer if (termios->c_cflag & PARODD) 22849aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer mr1 |= MCFUART_MR1_PARITYMARK; 22949aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer else 23049aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer mr1 |= MCFUART_MR1_PARITYSPACE; 23149aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer } else { 23249aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer if (termios->c_cflag & PARODD) 23349aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer mr1 |= MCFUART_MR1_PARITYODD; 23449aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer else 23549aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer mr1 |= MCFUART_MR1_PARITYEVEN; 23649aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer } 23749aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer } else { 23849aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer mr1 |= MCFUART_MR1_PARITYNONE; 23949aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer } 24049aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer 24149aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer if (termios->c_cflag & CSTOPB) 24249aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer mr2 |= MCFUART_MR2_STOP2; 24349aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer else 24449aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer mr2 |= MCFUART_MR2_STOP1; 24549aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer 24649aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer if (termios->c_cflag & CRTSCTS) { 24749aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer mr1 |= MCFUART_MR1_RXRTS; 24849aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer mr2 |= MCFUART_MR2_TXCTS; 24949aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer } 25049aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer 25149aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer spin_lock_irqsave(&port->lock, flags); 2523732b68f22857201fa09cb82b128f295096a2375Philippe De Muyter uart_update_timeout(port, termios->c_cflag, baud); 25349aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer writeb(MCFUART_UCR_CMDRESETRX, port->membase + MCFUART_UCR); 25449aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer writeb(MCFUART_UCR_CMDRESETTX, port->membase + MCFUART_UCR); 25549aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer writeb(MCFUART_UCR_CMDRESETMRPTR, port->membase + MCFUART_UCR); 25649aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer writeb(mr1, port->membase + MCFUART_UMR); 25749aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer writeb(mr2, port->membase + MCFUART_UMR); 25849aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer writeb((baudclk & 0xff00) >> 8, port->membase + MCFUART_UBG1); 25949aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer writeb((baudclk & 0xff), port->membase + MCFUART_UBG2); 26026a4bc66a6f57299027e04d90b14fe56a44c6d2bJohn Adamson#if defined(CONFIG_M5272) 26126a4bc66a6f57299027e04d90b14fe56a44c6d2bJohn Adamson writeb((baudfr & 0x0f), port->membase + MCFUART_UFPD); 26226a4bc66a6f57299027e04d90b14fe56a44c6d2bJohn Adamson#endif 26349aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer writeb(MCFUART_UCSR_RXCLKTIMER | MCFUART_UCSR_TXCLKTIMER, 26449aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer port->membase + MCFUART_UCSR); 26549aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer writeb(MCFUART_UCR_RXENABLE | MCFUART_UCR_TXENABLE, 26649aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer port->membase + MCFUART_UCR); 26749aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer spin_unlock_irqrestore(&port->lock, flags); 26849aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer} 26949aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer 27049aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer/****************************************************************************/ 27149aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer 27249aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungererstatic void mcf_rx_chars(struct mcf_uart *pp) 27349aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer{ 2749f69ba86d66297189916ceae401fe0944a207714Greg Ungerer struct uart_port *port = &pp->port; 27549aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer unsigned char status, ch, flag; 27649aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer 27749aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer while ((status = readb(port->membase + MCFUART_USR)) & MCFUART_USR_RXREADY) { 27849aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer ch = readb(port->membase + MCFUART_URB); 27949aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer flag = TTY_NORMAL; 28049aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer port->icount.rx++; 28149aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer 28249aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer if (status & MCFUART_USR_RXERR) { 28349aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer writeb(MCFUART_UCR_CMDRESETERR, 28449aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer port->membase + MCFUART_UCR); 28549aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer 28649aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer if (status & MCFUART_USR_RXBREAK) { 28749aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer port->icount.brk++; 28849aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer if (uart_handle_break(port)) 28949aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer continue; 29049aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer } else if (status & MCFUART_USR_RXPARITY) { 29149aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer port->icount.parity++; 29249aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer } else if (status & MCFUART_USR_RXOVERRUN) { 29349aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer port->icount.overrun++; 29449aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer } else if (status & MCFUART_USR_RXFRAMING) { 29549aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer port->icount.frame++; 29649aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer } 29749aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer 29849aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer status &= port->read_status_mask; 29949aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer 30049aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer if (status & MCFUART_USR_RXBREAK) 30149aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer flag = TTY_BREAK; 30249aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer else if (status & MCFUART_USR_RXPARITY) 30349aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer flag = TTY_PARITY; 30449aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer else if (status & MCFUART_USR_RXFRAMING) 30549aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer flag = TTY_FRAME; 30649aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer } 30749aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer 30849aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer if (uart_handle_sysrq_char(port, ch)) 30949aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer continue; 31049aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer uart_insert_char(port, status, MCFUART_USR_RXOVERRUN, ch, flag); 31149aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer } 31249aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer 313ebd2c8f6d2ec4012c267ecb95e72a57b8355a705Alan Cox tty_flip_buffer_push(port->state->port.tty); 31449aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer} 31549aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer 31649aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer/****************************************************************************/ 31749aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer 31849aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungererstatic void mcf_tx_chars(struct mcf_uart *pp) 31949aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer{ 3209f69ba86d66297189916ceae401fe0944a207714Greg Ungerer struct uart_port *port = &pp->port; 321ebd2c8f6d2ec4012c267ecb95e72a57b8355a705Alan Cox struct circ_buf *xmit = &port->state->xmit; 32249aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer 32349aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer if (port->x_char) { 32449aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer /* Send special char - probably flow control */ 32549aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer writeb(port->x_char, port->membase + MCFUART_UTB); 32649aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer port->x_char = 0; 32749aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer port->icount.tx++; 32849aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer return; 32949aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer } 33049aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer 33149aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer while (readb(port->membase + MCFUART_USR) & MCFUART_USR_TXREADY) { 33249aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer if (xmit->head == xmit->tail) 33349aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer break; 33449aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer writeb(xmit->buf[xmit->tail], port->membase + MCFUART_UTB); 33549aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE -1); 33649aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer port->icount.tx++; 33749aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer } 33849aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer 33949aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) 34049aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer uart_write_wakeup(port); 34149aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer 34249aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer if (xmit->head == xmit->tail) { 34349aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer pp->imr &= ~MCFUART_UIR_TXREADY; 34449aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer writeb(pp->imr, port->membase + MCFUART_UIMR); 34549aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer } 34649aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer} 34749aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer 34849aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer/****************************************************************************/ 34949aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer 35049aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungererstatic irqreturn_t mcf_interrupt(int irq, void *data) 35149aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer{ 35249aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer struct uart_port *port = data; 3539f69ba86d66297189916ceae401fe0944a207714Greg Ungerer struct mcf_uart *pp = container_of(port, struct mcf_uart, port); 35449aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer unsigned int isr; 3550ac8382e9cfdd723445692fc97aaa4643929750dYury Georgievskiy irqreturn_t ret = IRQ_NONE; 35649aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer 35749aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer isr = readb(port->membase + MCFUART_UISR) & pp->imr; 3580ac8382e9cfdd723445692fc97aaa4643929750dYury Georgievskiy 3590ac8382e9cfdd723445692fc97aaa4643929750dYury Georgievskiy spin_lock(&port->lock); 3600ac8382e9cfdd723445692fc97aaa4643929750dYury Georgievskiy if (isr & MCFUART_UIR_RXREADY) { 36149aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer mcf_rx_chars(pp); 3620ac8382e9cfdd723445692fc97aaa4643929750dYury Georgievskiy ret = IRQ_HANDLED; 3630ac8382e9cfdd723445692fc97aaa4643929750dYury Georgievskiy } 3640ac8382e9cfdd723445692fc97aaa4643929750dYury Georgievskiy if (isr & MCFUART_UIR_TXREADY) { 36549aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer mcf_tx_chars(pp); 3660ac8382e9cfdd723445692fc97aaa4643929750dYury Georgievskiy ret = IRQ_HANDLED; 3670ac8382e9cfdd723445692fc97aaa4643929750dYury Georgievskiy } 3680ac8382e9cfdd723445692fc97aaa4643929750dYury Georgievskiy spin_unlock(&port->lock); 3690ac8382e9cfdd723445692fc97aaa4643929750dYury Georgievskiy 3700ac8382e9cfdd723445692fc97aaa4643929750dYury Georgievskiy return ret; 37149aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer} 37249aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer 37349aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer/****************************************************************************/ 37449aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer 37549aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungererstatic void mcf_config_port(struct uart_port *port, int flags) 37649aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer{ 37749aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer port->type = PORT_MCF; 3783732b68f22857201fa09cb82b128f295096a2375Philippe De Muyter port->fifosize = MCFUART_TXFIFOSIZE; 37949aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer 38049aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer /* Clear mask, so no surprise interrupts. */ 38149aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer writeb(0, port->membase + MCFUART_UIMR); 38249aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer 3839cfb5c05fee914cc65d4706801f6bc424082b5f5Yong Zhang if (request_irq(port->irq, mcf_interrupt, 0, "UART", port)) 38449aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer printk(KERN_ERR "MCF: unable to attach ColdFire UART %d " 38549aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer "interrupt vector=%d\n", port->line, port->irq); 38649aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer} 38749aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer 38849aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer/****************************************************************************/ 38949aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer 39049aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungererstatic const char *mcf_type(struct uart_port *port) 39149aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer{ 39249aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer return (port->type == PORT_MCF) ? "ColdFire UART" : NULL; 39349aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer} 39449aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer 39549aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer/****************************************************************************/ 39649aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer 39749aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungererstatic int mcf_request_port(struct uart_port *port) 39849aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer{ 39949aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer /* UARTs always present */ 40049aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer return 0; 40149aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer} 40249aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer 40349aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer/****************************************************************************/ 40449aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer 40549aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungererstatic void mcf_release_port(struct uart_port *port) 40649aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer{ 40749aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer /* Nothing to release... */ 40849aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer} 40949aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer 41049aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer/****************************************************************************/ 41149aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer 41249aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungererstatic int mcf_verify_port(struct uart_port *port, struct serial_struct *ser) 41349aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer{ 41449aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer if ((ser->type != PORT_UNKNOWN) && (ser->type != PORT_MCF)) 41549aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer return -EINVAL; 41649aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer return 0; 41749aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer} 41849aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer 41949aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer/****************************************************************************/ 42049aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer 42149aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer/* 42249aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer * Define the basic serial functions we support. 42349aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer */ 4243732b68f22857201fa09cb82b128f295096a2375Philippe De Muyterstatic const struct uart_ops mcf_uart_ops = { 42549aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer .tx_empty = mcf_tx_empty, 42649aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer .get_mctrl = mcf_get_mctrl, 42749aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer .set_mctrl = mcf_set_mctrl, 42849aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer .start_tx = mcf_start_tx, 42949aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer .stop_tx = mcf_stop_tx, 43049aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer .stop_rx = mcf_stop_rx, 43149aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer .enable_ms = mcf_enable_ms, 43249aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer .break_ctl = mcf_break_ctl, 43349aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer .startup = mcf_startup, 43449aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer .shutdown = mcf_shutdown, 43549aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer .set_termios = mcf_set_termios, 43649aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer .type = mcf_type, 43749aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer .request_port = mcf_request_port, 43849aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer .release_port = mcf_release_port, 43949aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer .config_port = mcf_config_port, 44049aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer .verify_port = mcf_verify_port, 44149aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer}; 44249aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer 4432545cf6e94b4eb5a2c48dd55751aa9a70ff1ff9dPhilippe De Muyterstatic struct mcf_uart mcf_ports[4]; 44449aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer 44516791963ff7dd6a108251f5fa4b273cf1ffe531fGreg Ungerer#define MCF_MAXPORTS ARRAY_SIZE(mcf_ports) 44649aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer 44749aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer/****************************************************************************/ 44849aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer#if defined(CONFIG_SERIAL_MCF_CONSOLE) 44949aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer/****************************************************************************/ 45049aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer 45149aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungererint __init early_mcf_setup(struct mcf_platform_uart *platp) 45249aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer{ 45349aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer struct uart_port *port; 45449aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer int i; 45549aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer 45649aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer for (i = 0; ((i < MCF_MAXPORTS) && (platp[i].mapbase)); i++) { 45749aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer port = &mcf_ports[i].port; 45849aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer 45949aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer port->line = i; 46049aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer port->type = PORT_MCF; 46149aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer port->mapbase = platp[i].mapbase; 46249aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer port->membase = (platp[i].membase) ? platp[i].membase : 46349aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer (unsigned char __iomem *) port->mapbase; 46449aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer port->iotype = SERIAL_IO_MEM; 46549aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer port->irq = platp[i].irq; 46649aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer port->uartclk = MCF_BUSCLK; 46749aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer port->flags = ASYNC_BOOT_AUTOCONF; 46849aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer port->ops = &mcf_uart_ops; 46949aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer } 47049aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer 47149aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer return 0; 47249aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer} 47349aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer 47449aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer/****************************************************************************/ 47549aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer 47649aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungererstatic void mcf_console_putc(struct console *co, const char c) 47749aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer{ 47849aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer struct uart_port *port = &(mcf_ports + co->index)->port; 47949aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer int i; 48049aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer 48149aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer for (i = 0; (i < 0x10000); i++) { 48249aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer if (readb(port->membase + MCFUART_USR) & MCFUART_USR_TXREADY) 48349aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer break; 48449aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer } 48549aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer writeb(c, port->membase + MCFUART_UTB); 48649aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer for (i = 0; (i < 0x10000); i++) { 48749aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer if (readb(port->membase + MCFUART_USR) & MCFUART_USR_TXREADY) 48849aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer break; 48949aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer } 49049aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer} 49149aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer 49249aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer/****************************************************************************/ 49349aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer 49449aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungererstatic void mcf_console_write(struct console *co, const char *s, unsigned int count) 49549aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer{ 49649aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer for (; (count); count--, s++) { 49749aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer mcf_console_putc(co, *s); 49849aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer if (*s == '\n') 49949aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer mcf_console_putc(co, '\r'); 50049aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer } 50149aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer} 50249aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer 50349aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer/****************************************************************************/ 50449aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer 50549aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungererstatic int __init mcf_console_setup(struct console *co, char *options) 50649aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer{ 50749aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer struct uart_port *port; 50849aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer int baud = CONFIG_SERIAL_MCF_BAUDRATE; 50949aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer int bits = 8; 51049aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer int parity = 'n'; 51149aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer int flow = 'n'; 51249aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer 5134330e179a96bc9310d36e9b858bc8f275f329312Len Sorensen if ((co->index < 0) || (co->index >= MCF_MAXPORTS)) 51449aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer co->index = 0; 51549aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer port = &mcf_ports[co->index].port; 51649aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer if (port->membase == 0) 51749aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer return -ENODEV; 51849aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer 51949aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer if (options) 52049aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer uart_parse_options(options, &baud, &parity, &bits, &flow); 52149aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer 52249aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer return uart_set_options(port, co, baud, parity, bits, flow); 52349aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer} 52449aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer 52549aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer/****************************************************************************/ 52649aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer 52749aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungererstatic struct uart_driver mcf_driver; 52849aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer 52949aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungererstatic struct console mcf_console = { 53049aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer .name = "ttyS", 53149aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer .write = mcf_console_write, 53249aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer .device = uart_console_device, 53349aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer .setup = mcf_console_setup, 53449aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer .flags = CON_PRINTBUFFER, 53549aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer .index = -1, 53649aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer .data = &mcf_driver, 53749aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer}; 53849aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer 53949aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungererstatic int __init mcf_console_init(void) 54049aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer{ 54149aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer register_console(&mcf_console); 54249aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer return 0; 54349aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer} 54449aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer 54549aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungererconsole_initcall(mcf_console_init); 54649aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer 54749aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer#define MCF_CONSOLE &mcf_console 54849aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer 54949aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer/****************************************************************************/ 55049aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer#else 55149aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer/****************************************************************************/ 55249aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer 55349aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer#define MCF_CONSOLE NULL 55449aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer 55549aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer/****************************************************************************/ 55649aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer#endif /* CONFIG_MCF_CONSOLE */ 55749aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer/****************************************************************************/ 55849aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer 55949aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer/* 56049aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer * Define the mcf UART driver structure. 56149aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer */ 56249aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungererstatic struct uart_driver mcf_driver = { 56349aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer .owner = THIS_MODULE, 56449aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer .driver_name = "mcf", 56549aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer .dev_name = "ttyS", 56649aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer .major = TTY_MAJOR, 56749aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer .minor = 64, 56849aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer .nr = MCF_MAXPORTS, 56949aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer .cons = MCF_CONSOLE, 57049aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer}; 57149aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer 57249aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer/****************************************************************************/ 57349aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer 5749671f09921d93e722a28ae9610d478e092ac5466Bill Pembertonstatic int mcf_probe(struct platform_device *pdev) 57549aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer{ 57649aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer struct mcf_platform_uart *platp = pdev->dev.platform_data; 57749aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer struct uart_port *port; 57849aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer int i; 57949aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer 58049aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer for (i = 0; ((i < MCF_MAXPORTS) && (platp[i].mapbase)); i++) { 58149aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer port = &mcf_ports[i].port; 58249aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer 58349aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer port->line = i; 58449aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer port->type = PORT_MCF; 58549aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer port->mapbase = platp[i].mapbase; 58649aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer port->membase = (platp[i].membase) ? platp[i].membase : 58749aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer (unsigned char __iomem *) platp[i].mapbase; 58849aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer port->iotype = SERIAL_IO_MEM; 58949aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer port->irq = platp[i].irq; 59049aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer port->uartclk = MCF_BUSCLK; 59149aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer port->ops = &mcf_uart_ops; 59249aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer port->flags = ASYNC_BOOT_AUTOCONF; 59349aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer 59449aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer uart_add_one_port(&mcf_driver, port); 59549aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer } 59649aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer 59749aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer return 0; 59849aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer} 59949aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer 60049aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer/****************************************************************************/ 60149aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer 602d6f80e3a2a8c49f3e9c350b15f510c6eb8c1770dUwe Kleine-Königstatic int __devexit mcf_remove(struct platform_device *pdev) 60349aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer{ 60449aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer struct uart_port *port; 60549aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer int i; 60649aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer 60749aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer for (i = 0; (i < MCF_MAXPORTS); i++) { 60849aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer port = &mcf_ports[i].port; 60949aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer if (port) 61049aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer uart_remove_one_port(&mcf_driver, port); 61149aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer } 61249aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer 61349aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer return 0; 61449aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer} 61549aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer 61649aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer/****************************************************************************/ 61749aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer 61849aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungererstatic struct platform_driver mcf_platform_driver = { 61949aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer .probe = mcf_probe, 6202d47b7160243b1422006b91debf438484a4fde58Bill Pemberton .remove = mcf_remove, 62149aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer .driver = { 62249aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer .name = "mcfuart", 62349aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer .owner = THIS_MODULE, 62449aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer }, 62549aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer}; 62649aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer 62749aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer/****************************************************************************/ 62849aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer 62949aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungererstatic int __init mcf_init(void) 63049aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer{ 63149aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer int rc; 63249aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer 63349aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer printk("ColdFire internal UART serial driver\n"); 63449aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer 63549aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer rc = uart_register_driver(&mcf_driver); 63649aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer if (rc) 63749aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer return rc; 63849aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer rc = platform_driver_register(&mcf_platform_driver); 63949aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer if (rc) 64049aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer return rc; 64149aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer return 0; 64249aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer} 64349aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer 64449aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer/****************************************************************************/ 64549aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer 64649aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungererstatic void __exit mcf_exit(void) 64749aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer{ 64849aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer platform_driver_unregister(&mcf_platform_driver); 64949aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer uart_unregister_driver(&mcf_driver); 65049aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer} 65149aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer 65249aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer/****************************************************************************/ 65349aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer 65449aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerermodule_init(mcf_init); 65549aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerermodule_exit(mcf_exit); 65649aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer 65749aa49bfd40d718095669c1c70c9d167b814e29bGreg UngererMODULE_AUTHOR("Greg Ungerer <gerg@snapgear.com>"); 65849aa49bfd40d718095669c1c70c9d167b814e29bGreg UngererMODULE_DESCRIPTION("Freescale ColdFire UART driver"); 65949aa49bfd40d718095669c1c70c9d167b814e29bGreg UngererMODULE_LICENSE("GPL"); 660e169c139642fb4c682ec12a409725508dbefa520Kay SieversMODULE_ALIAS("platform:mcfuart"); 66149aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer 66249aa49bfd40d718095669c1c70c9d167b814e29bGreg Ungerer/****************************************************************************/ 663