/*
* drivers/serial/sh-sci.c
*
* SuperH on-chip serial module support. (SCI with no FIFO / with FIFO)
*
* Copyright (C) 2002 - 2006 Paul Mundt
*
* based off of the old drivers/char/sh-sci.c by:
*
* Copyright (C) 1999, 2000 Niibe Yutaka
* Copyright (C) 2000 Sugioka Toshinobu
* Modified to support multiple serial ports. Stuart Menefy (May 2000).
* Modified to support SecureEdge. David McCullough (2002)
* Modified to support SH7300 SCIF. Takashi Kusuda (Jun 2003).
* Removed SH7300 support (Jul 2007).
*
* 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.
*/
#if defined(CONFIG_SERIAL_SH_SCI_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
#define SUPPORT_SYSRQ
#endif
#undef DEBUG
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/timer.h>
#include <linux/interrupt.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
#include <linux/serial.h>
#include <linux/major.h>
#include <linux/string.h>
#include <linux/sysrq.h>
#include <linux/ioport.h>
#include <linux/mm.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/console.h>
#include <linux/platform_device.h>
#ifdef CONFIG_CPU_FREQ
#include <linux/notifier.h>
#include <linux/cpufreq.h>
#endif
#if defined(CONFIG_SUPERH) && !defined(CONFIG_SUPERH64)
#include <linux/ctype.h>
#include <asm/clock.h>
#include <asm/sh_bios.h>
#include <asm/kgdb.h>
#endif
#include <asm/sci.h>
#include "sh-sci.h"
struct sci_port {
struct uart_port port;
/* Port type */
unsigned int type;
/* Port IRQs: ERI, RXI, TXI, BRI (optional) */
unsigned int irqs[SCIx_NR_IRQS];
/* Port pin configuration */
void (*init_pins)(struct uart_port *port,
unsigned int cflag);
/* Port enable callback */
void (*enable)(struct uart_port *port);
/* Port disable callback */
void (*disable)(struct uart_port *port);
/* Break timer */
struct timer_list break_timer;
int break_flag;
#if defined(CONFIG_SUPERH) && !defined(CONFIG_SUPERH64)
/* Port clock */
struct clk *clk;
#endif
};
#ifdef CONFIG_SH_KGDB
static struct sci_port *kgdb_sci_port;
#endif
#ifdef CONFIG_SERIAL_SH_SCI_CONSOLE
static struct sci_port *serial_console_port;
#endif
/* Function prototypes */
static void sci_stop_tx(struct uart_port *port);
#define SCI_NPORTS CONFIG_SERIAL_SH_SCI_NR_UARTS
static struct sci_port sci_ports[SCI_NPORTS];
static struct uart_driver sci_uart_driver;
#if defined(CONFIG_SERIAL_SH_SCI_CONSOLE) && \
defined(CONFIG_SH_STANDARD_BIOS) || defined(CONFIG_SH_KGDB)
static inline void handle_error(struct uart_port *port)
{
/* Clear error flags */
sci_out(port, SCxSR, SCxSR_ERROR_CLEAR(port));
}
static int get_char(struct uart_port *port)
{
unsigned long flags;
unsigned short status;
int c;
spin_lock_irqsave(&port->lock, flags);
do {
status = sci_in(port, SCxSR);
if (status & SCxSR_ERRORS(port)) {
handle_error(port);
continue;
}
} while (!(status & SCxSR_RDxF(port)));
c = sci_in(port, SCxRDR);
sci_in(port, SCxSR); /* Dummy read */
sci_out(port, SCxSR, SCxSR_RDxF_CLEAR(port));
spin_unlock_irqrestore(&port->lock, flags);
return c;
}
#endif /* CONFIG_SH_STANDARD_BIOS || CONFIG_SH_KGDB */
#if defined(CONFIG_SERIAL_SH_SCI_CONSOLE) || defined(CONFIG_SH_KGDB)
static void put_char(struct uart_port *port, char c)
{
unsigned long flags;
unsigned short status;
spin_lock_irqsave(&port->lock, flags);
do {
status = sci_in(port, SCxSR);
} while (!(status & SCxSR_TDxE(port)));
sci_out(port, SCxTDR, c);
sci_in(port, SCxSR); /* Dummy read */
sci_out(port, SCxSR, SCxSR_TDxE_CLEAR(port));
spin_unlock_irqrestore(&port->lock, flags);
}
#endif
#ifdef CONFIG_SERIAL_SH_SCI_CONSOLE
static void put_string(struct sci_port *sci_port, const char *buffer, int count)
{
struct uart_port *port = &sci_port->port;
const unsigned char *p = buffer;
int i;
#if defined(CONFIG_SH_STANDARD_BIOS) || defined(CONFIG_SH_KGDB)
int checksum;
int usegdb=0;
#ifdef CONFIG_SH_STANDARD_BIOS
/* This call only does a trap the first time it is
* called, and so is safe to do here unconditionally
*/
usegdb |= sh_bios_in_gdb_mode();
#endif
#ifdef CONFIG_SH_KGDB
usegdb |= (kgdb_in_gdb_mode && (sci_port == kgdb_sci_port));
#endif
if (usegdb) {
/* $<packet info>#<checksum>. */
do {
unsigned char c;
put_char(port, '$');
put_char(port, 'O'); /* 'O'utput to console */
checksum = 'O';
for (i=0; i<count; i++) { /* Don't use run length encoding */
int h, l;
c = *p++;
h = highhex(c);
l = lowhex(c);
put_char(port, h);
put_char(port, l);
checksum += h + l;
}
put_char(port, '#');
put_char(port, highhex(checksum));
put_char(port, lowhex(checksum));
} while (get_char(port) != '+');
} else
#endif /* CONFIG_SH_STANDARD_BIOS || CONFIG_SH_KGDB */
for (i=0; i<count; i++) {
if (*p == 10)
put_char(port, '\r');
put_char(port, *p++);
}
}
#endif /* CONFIG_SERIAL_SH_SCI_CONSOLE */
#ifdef CONFIG_SH_KGDB
static int kgdb_sci_getchar(void)
{
int c;
/* Keep trying to read a character, this could be neater */
while ((c = get_char(&kgdb_sci_port->port)) < 0)
cpu_relax();
return c;
}
static inline void kgdb_sci_putchar(int c)
{
put_char(&kgdb_sci_port->port, c);
}
#endif /* CONFIG_SH_KGDB */
#if defined(__H8300S__)
enum { sci_disable, sci_enable };
static void h8300_sci_config(struct uart_port* port, unsigned int ctrl)
{
volatile unsigned char *mstpcrl=(volatile unsigned char *)MSTPCRL;
int ch = (port->mapbase - SMR0) >> 3;
unsigned char mask = 1 << (ch+1);
if (ctrl == sci_disable) {
*mstpcrl |= mask;
} else {
*mstpcrl &= ~mask;
}
}
static inline void h8300_sci_enable(struct uart_port *port)
{
h8300_sci_config(port, sci_enable);
}
static inline void h8300_sci_disable(struct uart_port *port)
{
h8300_sci_config(port, sci_disable);
}
#endif
#if defined(SCI_ONLY) || defined(SCI_AND_SCIF) && \
defined(__H8300H__) || defined(__H8300S__)
static void sci_init_pins_sci(struct uart_port* port, unsigned int cflag)
{
int ch = (port->mapbase - SMR0) >> 3;
/* set DDR regs */
H8300_GPIO_DDR(h8300_sci_pins[ch].port,
h8300_sci_pins[ch].rx,
H8300_GPIO_INPUT);
H8300_GPIO_DDR(h8300_sci_pins[ch].port,
h8300_sci_pins[ch].tx,
H8300_GPIO_OUTPUT);
/* tx mark output*/
H8300_SCI_DR(ch) |= h8300_sci_pins[ch].tx;
}
#else
#define sci_init_pins_sci NULL
#endif
#if defined(CONFIG_CPU_SUBTYPE_SH7707) || defined(CONFIG_CPU_SUBTYPE_SH7709)
static void sci_init_pins_irda(struct uart_port *port, unsigned int cflag)
{
unsigned int fcr_val = 0;
if (cflag & CRTSCTS)
fcr_val |= SCFCR_MCE;
sci_out(port, SCFCR, fcr_val);
}
#else
#define sci_init_pins_irda NULL
#endif
#ifdef SCI_ONLY
#define sci_init_pins_scif NULL
#endif
#if defined(SCIF_ONLY) || defined(SCI_AND_SCIF)
#if defined(CONFIG_CPU_SUBTYPE_SH7710) || defined(CONFIG_CPU_SUBTYPE_SH7712)
static void sci_init_pins_scif(struct uart_port* port, unsigned int cflag)
{
unsigned int fcr_val = 0;
set_sh771x_scif_pfc(port);
if (cflag & CRTSCTS) {
fcr_val |= SCFCR_MCE;
}
sci_out(port, SCFCR, fcr_val);
}
#elif defined(CONFIG_CPU_SH3)
/* For SH7705, SH7706, SH7707, SH7709, SH7709A, SH7729 */
static void sci_init_pins_scif(struct uart_port *port, unsigned int cflag)
{
unsigned int fcr_val = 0;
unsigned short data;
/* We need to set SCPCR to enable RTS/CTS */
data = ctrl_inw(SCPCR);
/* Clear out SCP7MD1,0, SCP6MD1,0, SCP4MD1,0*/
ctrl_outw(data & 0x0fcf, SCPCR);
if (cflag & CRTSCTS)
fcr_val |= SCFCR_MCE;
|