From: Paul Mundt This ports the sh-sci driver to the new API. sh and h8 both use this. The intention is to leave the drivers/char sh-sci in place for a short period of time until all the h8 people are on the new driver (sh no longer uses the drivers/char version). --- 25-akpm/drivers/serial/Kconfig | 12 25-akpm/drivers/serial/Makefile | 1 25-akpm/drivers/serial/sh-sci.c | 1503 ++++++++++++++++++++++++++++++++++++ 25-akpm/drivers/serial/sh-sci.h | 435 ++++++++++ 25-akpm/include/linux/serial_core.h | 5 5 files changed, 1955 insertions(+), 1 deletion(-) diff -puN drivers/serial/Kconfig~sh-02-sh-sci drivers/serial/Kconfig --- 25/drivers/serial/Kconfig~sh-02-sh-sci 2004-03-23 02:05:25.884168504 -0800 +++ 25-akpm/drivers/serial/Kconfig 2004-03-23 02:05:25.889167744 -0800 @@ -1,7 +1,7 @@ # # Serial device configuration # -# $Id: Config.in,v 1.15 2002/07/06 17:16:24 rmk Exp $ +# $Id: Kconfig,v 1.11 2004/03/11 18:08:04 lethal Exp $ # menu "Serial drivers" @@ -503,6 +503,16 @@ config SERIAL98_CONSOLE depends on SERIAL98=y select SERIAL_CORE_CONSOLE +config SERIAL_SH_SCI + tristate "SH SCI(F) serial port support" + depends on SUPERH + select SERIAL_CORE + +config SERIAL_SH_SCI_CONSOLE + bool "Support for console on SH SCI(F)" + depends on SERIAL_SH_SCI=y + select SERIAL_CORE_CONSOLE + config SERIAL_AU1X00 bool "Enable Au1x00 UART Support" depends on MIPS && SOC_AU1X00 diff -puN drivers/serial/Makefile~sh-02-sh-sci drivers/serial/Makefile --- 25/drivers/serial/Makefile~sh-02-sh-sci 2004-03-23 02:05:25.886168200 -0800 +++ 25-akpm/drivers/serial/Makefile 2004-03-23 02:05:25.890167592 -0800 @@ -36,3 +36,4 @@ obj-$(CONFIG_SERIAL98) += serial98.o obj-$(CONFIG_SERIAL_PMACZILOG) += pmac_zilog.o obj-$(CONFIG_SERIAL_AU1X00) += au1x00_uart.o obj-$(CONFIG_SERIAL_DZ) += dz.o +obj-$(CONFIG_SERIAL_SH_SCI) += sh-sci.o diff -puN /dev/null drivers/serial/sh-sci.c --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25-akpm/drivers/serial/sh-sci.c 2004-03-23 02:05:25.897166528 -0800 @@ -0,0 +1,1503 @@ +/* + * drivers/serial/sh-sci.c + * + * SuperH on-chip serial module support. (SCI with no FIFO / with FIFO) + * + * Copyright (C) 2002, 2003 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). + * + * 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. + */ + +#define DEBUG +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_CPU_FREQ +#include +#include +#endif + +#include +#include +#include +#include +#include + +#include + +#ifdef CONFIG_SH_STANDARD_BIOS +#include +#endif + +#if defined(CONFIG_SERIAL_SH_SCI_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) +#define SUPPORT_SYSRQ +#endif + +#include "sh-sci.h" + +#ifdef CONFIG_SH_KGDB +#include + +static int kgdb_get_char(struct sci_port *port); +static void kgdb_put_char(struct sci_port *port, char c); +static void kgdb_handle_error(struct sci_port *port); +static struct sci_port *kgdb_sci_port; +#endif /* CONFIG_SH_KGDB */ + +#ifdef CONFIG_SERIAL_SH_SCI_CONSOLE +static struct sci_port *serial_console_port = 0; +#endif /* CONFIG_SERIAL_SH_SCI_CONSOLE */ + +/* Function prototypes */ +static void sci_stop_tx(struct uart_port *port, unsigned int tty_stop); +static void sci_start_tx(struct uart_port *port, unsigned int tty_start); +static void sci_start_rx(struct uart_port *port, unsigned int tty_start); +static void sci_stop_rx(struct uart_port *port); +static int sci_request_irq(struct sci_port *port); +static void sci_free_irq(struct sci_port *port); + +static struct sci_port sci_ports[SCI_NPORTS]; +static struct uart_driver sci_uart_driver; + +#if defined(CONFIG_SH_STANDARD_BIOS) || defined(CONFIG_SH_KGDB) + +static void handle_error(struct sci_port *port) +{ /* Clear error flags */ + sci_out(port, SCxSR, SCxSR_ERROR_CLEAR(port)); +} + +static int get_char(struct sci_port *port) +{ + unsigned long flags; + unsigned short status; + int c; + + local_irq_save(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)); + local_irq_restore(flags); + + return c; +} + +/* Taken from sh-stub.c of GDB 4.18 */ +static const char hexchars[] = "0123456789abcdef"; + +static __inline__ char highhex(int x) +{ + return hexchars[(x >> 4) & 0xf]; +} + +static __inline__ char lowhex(int x) +{ + return hexchars[x & 0xf]; +} + +#endif /* CONFIG_SH_STANDARD_BIOS || CONFIG_SH_KGDB */ + +/* + * Send the packet in buffer. The host gets one chance to read it. + * This routine does not wait for a positive acknowledge. + */ + +#ifdef CONFIG_SERIAL_SH_SCI_CONSOLE +static void put_char(struct uart_port *port, char c) +{ + unsigned long flags; + unsigned short status; + + local_irq_save(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)); + + local_irq_restore(flags); +} + +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 && (port == kgdb_sci_port)); +#endif + + if (usegdb) { + /* $#. */ + do { + unsigned char c; + put_char(port, '$'); + put_char(port, 'O'); /* 'O'utput to console */ + checksum = 'O'; + + for (i=0; ibase - SMR0) >> 3; + unsigned char mask = 1 << (ch+1); + + if (ctrl == sci_disable) { + *mstpcrl |= mask; + } else { + *mstpcrl &= ~mask; + } +} +#endif + +#if defined(SCI_ONLY) || defined(SCI_AND_SCIF) +#if defined(__H8300H__) || defined(__H8300S__) +static void sci_init_pins_sci(struct sci_port* port, unsigned int cflag) +{ + int ch = (port->base - 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 +static void sci_init_pins_sci(struct uart_port *port, unsigned int cflag) +{ +} +#endif +#endif + +#if defined(SCIF_ONLY) || defined(SCI_AND_SCIF) +#if defined(CONFIG_CPU_SH3) +/* For 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; + else { + unsigned short data; + + /* We need to set SCPCR to enable RTS/CTS */ + data = ctrl_inw(SCPCR); + /* Clear out SCP7MD1,0, SCP4MD1,0, + Set SCP6MD1,0 = {01} (output) */ + ctrl_outw((data&0x0fcf)|0x1000, SCPCR); + + data = ctrl_inb(SCPDR); + /* Set /RTS2 (bit6) = 0 */ + ctrl_outb(data&0xbf, SCPDR); + } + sci_out(port, SCFCR, fcr_val); +} + +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 + +/* For SH7750 */ +static void sci_init_pins_scif(struct uart_port *port, unsigned int cflag) +{ + unsigned int fcr_val = 0; + + if (cflag & CRTSCTS) { + fcr_val |= SCFCR_MCE; + } else { + ctrl_outw(0x0080, SCSPTR2); /* Set RTS = 1 */ + } + sci_out(port, SCFCR, fcr_val); +} + +#endif +#endif /* SCIF_ONLY || SCI_AND_SCIF */ + +/* ********************************************************************** * + * the interrupt related routines * + * ********************************************************************** */ + +static void sci_transmit_chars(struct uart_port *port) +{ + struct circ_buf *xmit = &port->info->xmit; + unsigned int stopped = uart_tx_stopped(port); + unsigned long flags; + unsigned short status; + unsigned short ctrl; + int count, txroom; + + status = sci_in(port, SCxSR); + if (!(status & SCxSR_TDxE(port))) { + local_irq_save(flags); + ctrl = sci_in(port, SCSCR); + if (uart_circ_empty(xmit)) { + ctrl &= ~SCI_CTRL_FLAGS_TIE; + } else { + ctrl |= SCI_CTRL_FLAGS_TIE; + } + sci_out(port, SCSCR, ctrl); + local_irq_restore(flags); + return; + } + + if (port->type == PORT_SCIF) { + txroom = 16 - (sci_in(port, SCFDR)>>8); + } else { + txroom = (sci_in(port, SCxSR) & SCI_TDRE)?1:0; + } + + count = txroom; + + do { + unsigned char c; + + if (port->x_char) { + c = port->x_char; + port->x_char = 0; + } else if (!uart_circ_empty(xmit) && !stopped) { + c = xmit->buf[xmit->tail]; + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + } else { + break; + } + + sci_out(port, SCxTDR, c); + + port->icount.tx++; + } while (--count > 0); + + sci_out(port, SCxSR, SCxSR_TDxE_CLEAR(port)); + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(port); + if (uart_circ_empty(xmit)) { + sci_stop_tx(port, 0); + } else { + local_irq_save(flags); + ctrl = sci_in(port, SCSCR); + + if (port->type == PORT_SCIF) { + sci_in(port, SCxSR); /* Dummy read */ + sci_out(port, SCxSR, SCxSR_TDxE_CLEAR(port)); + } + + ctrl |= SCI_CTRL_FLAGS_TIE; + sci_out(port, SCSCR, ctrl); + local_irq_restore(flags); + } +} + +/* On SH3, SCIF may read end-of-break as a space->mark char */ +#define STEPFN(c) ({int __c=(c); (((__c-1)|(__c)) == -1); }) + +static inline void sci_receive_chars(struct uart_port *port, + struct pt_regs *regs) +{ + struct tty_struct *tty = port->info->tty; + int i, count, copied = 0; + unsigned short status; + + status = sci_in(port, SCxSR); + if (!(status & SCxSR_RDxF(port))) + return; + + while (1) { + if (port->type == PORT_SCIF) { + count = sci_in(port, SCFDR)&0x001f; + } else { + count = (sci_in(port, SCxSR)&SCxSR_RDxF(port))?1:0; + } + + /* Don't copy more bytes than there is room for in the buffer */ + if (tty->flip.count + count > TTY_FLIPBUF_SIZE) + count = TTY_FLIPBUF_SIZE - tty->flip.count; + + /* If for any reason we can't copy more data, we're done! */ + if (count == 0) + break; + + if (port->type == PORT_SCI) { + char c = sci_in(port, SCxRDR); + if(((struct sci_port *)port)->break_flag + || uart_handle_sysrq_char(port, c, regs)) { + count = 0; + } else { + tty->flip.char_buf_ptr[0] = c; + tty->flip.flag_buf_ptr[0] = TTY_NORMAL; + } + } else { + for (i=0; ibreak_flag) { + if ((c == 0) && + (status & SCxSR_FER(port))) { + count--; i--; + continue; + } + /* Nonzero => end-of-break */ + pr_debug("scif: debounce<%02x>\n", c); + ((struct sci_port *)port)->break_flag = 0; + if (STEPFN(c)) { + count--; i--; + continue; + } + } +#endif /* CONFIG_CPU_SH3 */ + if (uart_handle_sysrq_char(port, c, regs)) { + count--; i--; + continue; + } + + /* Store data and status */ + tty->flip.char_buf_ptr[i] = c; + if (status&SCxSR_FER(port)) { + tty->flip.flag_buf_ptr[i] = TTY_FRAME; + pr_debug("sci: frame error\n"); + } else if (status&SCxSR_PER(port)) { + tty->flip.flag_buf_ptr[i] = TTY_PARITY; + pr_debug("sci: parity error\n"); + } else { + tty->flip.flag_buf_ptr[i] = TTY_NORMAL; + } + } + } + + sci_in(port, SCxSR); /* dummy read */ + sci_out(port, SCxSR, SCxSR_RDxF_CLEAR(port)); + + /* Update the kernel buffer end */ + tty->flip.count += count; + tty->flip.char_buf_ptr += count; + tty->flip.flag_buf_ptr += count; + copied += count; + port->icount.rx += count; + } + + if (copied) { + /* Tell the rest of the system the news. New characters! */ + tty_flip_buffer_push(tty); + } else { + sci_in(port, SCxSR); /* dummy read */ + sci_out(port, SCxSR, SCxSR_RDxF_CLEAR(port)); + } +} + +#define SCI_BREAK_JIFFIES (HZ/20) +/* The sci generates interrupts during the break, + * 1 per millisecond or so during the break period, for 9600 baud. + * So dont bother disabling interrupts. + * But dont want more than 1 break event. + * Use a kernel timer to periodically poll the rx line until + * the break is finished. + */ +static void sci_schedule_break_timer(struct sci_port *port) +{ + port->break_timer.expires = jiffies + SCI_BREAK_JIFFIES; + add_timer(&port->break_timer); +} +/* Ensure that two consecutive samples find the break over. */ +static void sci_break_timer(unsigned long data) +{ + struct sci_port * port = (struct sci_port *)data; + if(sci_rxd_in(&port->port) == 0) { + port->break_flag = 1; + sci_schedule_break_timer(port); + } else if(port->break_flag == 1){ + /* break is over. */ + port->break_flag = 2; + sci_schedule_break_timer(port); + } else port->break_flag = 0; +} + +static inline int sci_handle_errors(struct uart_port *port) +{ + int copied = 0; + unsigned short status = sci_in(port, SCxSR); + struct tty_struct *tty = port->info->tty; + + if (status&SCxSR_ORER(port) && tty->flip.countflip.flag_buf_ptr++ = TTY_OVERRUN; + pr_debug("sci: overrun error\n"); + } + + if (status&SCxSR_FER(port) && tty->flip.countbreak_flag) { + sci_port->break_flag = 1; + sci_schedule_break_timer((struct sci_port *)port); + /* Do sysrq handling. */ + if(uart_handle_break(port)) { + return 0; + } + pr_debug("sci: BREAK detected\n"); + copied++; + *tty->flip.flag_buf_ptr++ = TTY_BREAK; + } + } + else { + /* frame error */ + copied++; + *tty->flip.flag_buf_ptr++ = TTY_FRAME; + pr_debug("sci: frame error\n"); + } + } + + if (status&SCxSR_PER(port) && tty->flip.countflip.flag_buf_ptr++ = TTY_PARITY; + pr_debug("sci: parity error\n"); + } + + if (copied) { + tty->flip.count += copied; + tty_flip_buffer_push(tty); + } + + return copied; +} + +static inline int sci_handle_breaks(struct uart_port *port) +{ + int copied = 0; + unsigned short status = sci_in(port, SCxSR); + struct tty_struct *tty = port->info->tty; + struct sci_port *s = &sci_ports[port->line]; + + if (!s->break_flag && status & SCxSR_BRK(port) && + tty->flip.count < TTY_FLIPBUF_SIZE) { +#if defined(CONFIG_CPU_SH3) + /* Debounce break */ + s->break_flag = 1; +#endif + /* Notify of BREAK */ + copied++; + *tty->flip.flag_buf_ptr++ = TTY_BREAK; + pr_debug("sci: BREAK detected\n"); + } + +#if defined(CONFIG_CPU_SUBTYPE_SH7750) || defined(CONFIG_CPU_SUBTYPE_ST40STB1) || \ + defined(CONFIG_CPU_SUBTYPE_SH7760) + /* XXX: Handle SCIF overrun error */ + if (port->type == PORT_SCIF && (sci_in(port, SCLSR) & SCIF_ORER) != 0) { + sci_out(port, SCLSR, 0); + if(tty->flip.countflip.flag_buf_ptr++ = TTY_OVERRUN; + pr_debug("sci: overrun error\n"); + } + } +#endif + + if (copied) { + tty->flip.count += copied; + tty_flip_buffer_push(tty); + } + + return copied; +} + +static irqreturn_t sci_rx_interrupt(int irq, void *ptr, struct pt_regs *regs) +{ + struct uart_port *port = ptr; + + /* I think sci_receive_chars has to be called irrespective + * of whether the I_IXOFF is set, otherwise, how is the interrupt + * to be disabled? + */ + sci_receive_chars(port, regs); + + return IRQ_HANDLED; +} + +static irqreturn_t sci_tx_interrupt(int irq, void *ptr, struct pt_regs *regs) +{ + struct uart_port *port = ptr; + + sci_transmit_chars(port); + + return IRQ_HANDLED; +} + +static irqreturn_t sci_er_interrupt(int irq, void *ptr, struct pt_regs *regs) +{ + struct uart_port *port = ptr; + + /* Handle errors */ + if (port->type == PORT_SCI) { + if (sci_handle_errors(port)) { + /* discard character in rx buffer */ + sci_in(port, SCxSR); + sci_out(port, SCxSR, SCxSR_RDxF_CLEAR(port)); + } + } else { + sci_rx_interrupt(irq, ptr, regs); + } + + sci_out(port, SCxSR, SCxSR_ERROR_CLEAR(port)); + + /* Kick the transmission */ + sci_tx_interrupt(irq, ptr, regs); + + return IRQ_HANDLED; +} + +static irqreturn_t sci_br_interrupt(int irq, void *ptr, struct pt_regs *regs) +{ + struct uart_port *port = ptr; + + /* Handle BREAKs */ + sci_handle_breaks(port); + sci_out(port, SCxSR, SCxSR_BREAK_CLEAR(port)); + + return IRQ_HANDLED; +} + +#ifdef CONFIG_CPU_FREQ +/* + * Here we define a transistion notifier so that we can update all of our + * ports' baud rate when the peripheral clock changes. + */ +static int sci_notifier(struct notifier_block *self, unsigned long phase, void *p) +{ + struct cpufreq_freqs *freqs = p; + int i; + + if (phase == CPUFREQ_POSTCHANGE) { + for (i = 0; i < SCI_NPORTS; i++) { + struct uart_port *port = &sci_ports[i]; + + /* + * Update the uartclk per-port if frequency has + * changed, since it will no longer necessarily be + * consistent with the old frequency. + * + * Really we want to be able to do something like + * uart_change_speed() or something along those lines + * here to implicitly reset the per-port baud rate.. + * + * Clean this up later.. + */ + port->uartclk = current_cpu_data.module_clock * 16; + } + + printk("%s: got a postchange notification for cpu %d (old %d, new %d)\n", + __FUNCTION__, freqs->cpu, freqs->old, freqs->new); + } + + return NOTIFY_OK; +} + +static struct notifier_block sci_nb = { &sci_notifier, NULL, 0 }; +#endif /* CONFIG_CPU_FREQ */ + +static int sci_request_irq(struct sci_port *port) +{ + int i; + irqreturn_t (*handlers[4])(int irq, void *ptr, struct pt_regs *regs) = { + sci_er_interrupt, sci_rx_interrupt, sci_tx_interrupt, + sci_br_interrupt, + }; + const char *desc[] = { "SCI Receive Error", "SCI Receive Data Full", + "SCI Transmit Data Empty", "SCI Break" }; + + for (i = 0; i < ARRAY_SIZE(handlers); i++) { + if (!port->irqs[i]) + continue; + if (request_irq(port->irqs[i], handlers[i], SA_INTERRUPT, + desc[i], port)) { + printk(KERN_ERR "sci: Cannot allocate irq.\n"); + return -ENODEV; + } + } + + return 0; +} + +static void sci_free_irq(struct sci_port *port) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(port->irqs); i++) { + if (!port->irqs[i]) + continue; + + free_irq(port->irqs[i], port); + } +} + +static unsigned int sci_tx_empty(struct uart_port *port) +{ + /* Can't detect */ + return TIOCSER_TEMT; +} + +static void sci_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ + /* This routine is used for seting signals of: DTR, DCD, CTS/RTS */ + /* We use SCIF's hardware for CTS/RTS, so don't need any for that. */ + /* If you have signals for DTR and DCD, please implement here. */ +} + +static unsigned int sci_get_mctrl(struct uart_port *port) +{ + /* This routine is used for geting signals of: DTR, DCD, DSR, RI, + and CTS/RTS */ + + return TIOCM_DTR | TIOCM_RTS | TIOCM_DSR; +} + +static void sci_start_tx(struct uart_port *port, unsigned int tty_start) +{ + struct sci_port *s = &sci_ports[port->line]; + + disable_irq(s->irqs[SCIx_TXI_IRQ]); + sci_transmit_chars(port); + enable_irq(s->irqs[SCIx_TXI_IRQ]); +} + +static void sci_stop_tx(struct uart_port *port, unsigned int tty_stop) +{ + unsigned long flags; + unsigned short ctrl; + + /* Clear TIE (Transmit Interrupt Enable) bit in SCSCR */ + local_irq_save(flags); + ctrl = sci_in(port, SCSCR); + ctrl &= ~SCI_CTRL_FLAGS_TIE; + sci_out(port, SCSCR, ctrl); + local_irq_restore(flags); +} + +static void sci_start_rx(struct uart_port *port, unsigned int tty_start) +{ + unsigned long flags; + unsigned short ctrl; + + /* Set RIE (Receive Interrupt Enable) bit in SCSCR */ + local_irq_save(flags); + ctrl = sci_in(port, SCSCR); + ctrl |= SCI_CTRL_FLAGS_RIE; + sci_out(port, SCSCR, ctrl); + local_irq_restore(flags); +} + +static void sci_stop_rx(struct uart_port *port) +{ + unsigned long flags; + unsigned short ctrl; + + /* Clear RIE (Receive Interrupt Enable) bit in SCSCR */ + local_irq_save(flags); + ctrl = sci_in(port, SCSCR); + ctrl &= ~SCI_CTRL_FLAGS_RIE; + sci_out(port, SCSCR, ctrl); + local_irq_restore(flags); +} + +static void sci_enable_ms(struct uart_port *port) +{ + /* Nothing here yet .. */ +} + +static void sci_break_ctl(struct uart_port *port, int break_state) +{ + /* Nothing here yet .. */ +} + +static int sci_startup(struct uart_port *port) +{ + struct sci_port *s = &sci_ports[port->line]; + + sci_request_irq(s); + sci_start_tx(port, 1); + sci_start_rx(port, 1); + +#if defined(__H8300S__) + h8300_sci_enable(port, sci_enable); +#endif + + return 0; +} + +static void sci_shutdown(struct uart_port *port) +{ + struct sci_port *s = &sci_ports[port->line]; + + sci_stop_rx(port); + sci_stop_tx(port, 1); + sci_free_irq(s); + +#if defined(__H8300S__) + h8300_sci_enable(port, sci_disable); +#endif +} + +static void sci_set_termios(struct uart_port *port, struct termios *termios, + struct termios *old) +{ + struct sci_port *s = &sci_ports[port->line]; + unsigned int status, baud, smr_val; + unsigned long flags; + int t; + + baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16); + + spin_lock_irqsave(&port->lock, flags); + + do { + status = sci_in(port, SCxSR); + } while (!(status & SCxSR_TEND(port))); + + sci_out(port, SCSCR, 0x00); /* TE=0, RE=0, CKE1=0 */ + + if (port->type == PORT_SCIF) { + sci_out(port, SCFCR, SCFCR_RFRST | SCFCR_TFRST); + } + + smr_val = sci_in(port, SCSMR) & 3; + if ((termios->c_cflag & CSIZE) == CS7) + smr_val |= 0x40; + if (termios->c_cflag & PARENB) + smr_val |= 0x20; + if (termios->c_cflag & PARODD) + smr_val |= 0x30; + if (termios->c_cflag & CSTOPB) + smr_val |= 0x08; + + uart_update_timeout(port, termios->c_cflag, baud); + + sci_out(port, SCSMR, smr_val); + + switch (baud) { + case 0: t = -1; break; + case 2400: t = BPS_2400; break; + case 4800: t = BPS_4800; break; + case 9600: t = BPS_9600; break; + case 19200: t = BPS_19200; break; + case 38400: t = BPS_38400; break; + case 57600: t = BPS_57600; break; + case 115200: t = BPS_115200; break; + default: t = BPS_115200; break; + } + + if (t > 0) { + if(t >= 256) { + sci_out(port, SCSMR, (sci_in(port, SCSMR) & ~3) | 1); + t >>= 2; + } else { + sci_out(port, SCSMR, sci_in(port, SCSMR) & ~3); + } + sci_out(port, SCBRR, t); + udelay((1000000+(baud-1)) / baud); /* Wait one bit interval */ + } + + s->init_pins(port, termios->c_cflag); + sci_out(port, SCSCR, SCSCR_INIT(port)); + + if ((termios->c_cflag & CREAD) != 0) + sci_start_rx(port,0); + + spin_unlock_irqrestore(&port->lock, flags); +} + +static const char *sci_type(struct uart_port *port) +{ + switch (port->type) { + case PORT_SCI: return "sci"; + case PORT_SCIF: return "scif"; + case PORT_IRDA: return "irda"; + } + + return 0; +} + +static void sci_release_port(struct uart_port *port) +{ + /* Nothing here yet .. */ +} + +static int sci_request_port(struct uart_port *port) +{ + /* Nothing here yet .. */ + return 0; +} + +static void sci_config_port(struct uart_port *port, int flags) +{ + struct sci_port *s = &sci_ports[port->line]; + + port->type = s->type; +} + +static int sci_verify_port(struct uart_port *port, struct serial_struct *ser) +{ + struct sci_port *s = &sci_ports[port->line]; + + if (ser->irq != s->irqs[SCIx_TXI_IRQ] || ser->irq > NR_IRQS) + return -EINVAL; + if (ser->baud_base < 2400) + /* No paper tape reader for Mitch.. */ + return -EINVAL; + + return 0; +} + +static struct uart_ops sci_uart_ops = { + .tx_empty = sci_tx_empty, + .set_mctrl = sci_set_mctrl, + .get_mctrl = sci_get_mctrl, + .start_tx = sci_start_tx, + .stop_tx = sci_stop_tx, + .stop_rx = sci_stop_rx, + .enable_ms = sci_enable_ms, + .break_ctl = sci_break_ctl, + .startup = sci_startup, + .shutdown = sci_shutdown, + .set_termios = sci_set_termios, + .type = sci_type, + .release_port = sci_release_port, + .request_port = sci_request_port, + .config_port = sci_config_port, + .verify_port = sci_verify_port, +}; + +static struct sci_port sci_ports[SCI_NPORTS] = { +#if defined(CONFIG_CPU_SUBTYPE_SH7708) + { + .port = { + .membase = (void *)0xfffffe80, + .mapbase = 0xfffffe80, + .iotype = SERIAL_IO_MEM, + .irq = 25, + .ops = &sci_uart_ops, + .flags = ASYNC_BOOT_AUTOCONF, + .line = 0, + }, + .type = PORT_SCI, + .irqs = SCI_IRQS, + .init_pins = sci_init_pins_sci, + }, +#elif defined(CONFIG_CPU_SUBTYPE_SH7707) || defined(CONFIG_CPU_SUBTYPE_SH7709) + { + .port = { + .membase = (void *)0xfffffe80, + .mapbase = 0xfffffe80, + .iotype = SERIAL_IO_MEM, + .irq = 25, + .ops = &sci_uart_ops, + .flags = ASYNC_BOOT_AUTOCONF, + .line = 0, + }, + .type = PORT_SCI, + .irqs = SCI_IRQS, + .init_pins = sci_init_pins_sci, + }, + { + .port = { + .membase = (void *)0xa4000150, + .mapbase = 0xa4000150, + .iotype = SERIAL_IO_MEM, + .irq = 59, + .ops = &sci_uart_ops, + .flags = ASYNC_BOOT_AUTOCONF, + .line = 1, + }, + .type = PORT_SCIF, + .irqs = SH3_SCIF_IRQS, + .init_pins = sci_init_pins_scif, + }, + { + .port = { + .membase = (void *)0xa4000140, + .mapbase = 0xa4000140, + .iotype = SERIAL_IO_MEM, + .irq = 55, + .ops = &sci_uart_ops, + .flags = ASYNC_BOOT_AUTOCONF, + .line = 2, + }, + .type = PORT_IRDA, + .irqs = SH3_IRDA_IRQS, + .init_pins = sci_init_pins_irda, + } +#elif defined(CONFIG_CPU_SUBTYPE_SH7750) || defined(CONFIG_CPU_SUBTYPE_SH7751) + { + .port = { + .membase = (void *)0xffe00000, + .mapbase = 0xffe00000, + .iotype = SERIAL_IO_MEM, + .irq = 25, + .ops = &sci_uart_ops, + .flags = ASYNC_BOOT_AUTOCONF, + .line = 0, + }, + .type = PORT_SCI, + .irqs = SCI_IRQS, + .init_pins = sci_init_pins_sci, + }, + { + .port = { + .membase = (void *)0xffe80000, + .mapbase = 0xffe80000, + .iotype = SERIAL_IO_MEM, + .irq = 43, + .ops = &sci_uart_ops, + .flags = ASYNC_BOOT_AUTOCONF, + .line = 1, + }, + .type = PORT_SCIF, + .irqs = SH4_SCIF_IRQS, + .init_pins = sci_init_pins_scif, + }, +#elif defined(CONFIG_CPU_SUBTYPE_SH7760) + { + .port = { + .membase = (void *)0xfe600000, + .mapbase = 0xfe600000, + .iotype = SERIAL_IO_MEM, + .irq = 55, + .ops = &sci_uart_ops, + .flags = ASYNC_BOOT_AUTOCONF, + .line = 0, + }, + .type = PORT_SCIF, + .irqs = SH7760_SCIF0_IRQS, + .init_pins = sci_init_pins_scif, + }, + { + .port = { + .membase = (void *)0xfe610000, + .mapbase = 0xfe610000, + .iotype = SERIAL_IO_MEM, + .irq = 75, + .ops = &sci_uart_ops, + .flags = ASYNC_BOOT_AUTOCONF, + .line = 1, + }, + .type = PORT_SCIF, + .irqs = SH7760_SCIF1_IRQS, + .init_pins = sci_init_pins_scif, + }, + { + .port = { + .membase = (void *)0xfe620000, + .mapbase = 0xfe620000, + .iotype = SERIAL_IO_MEM, + .irq = 79, + .ops = &sci_uart_ops, + .flags = ASYNC_BOOT_AUTOCONF, + .line = 2, + }, + .type = PORT_SCIF, + .irqs = SH7760_SCIF2_IRQS, + .init_pins = sci_init_pins_scif, + }, +#elif defined(CONFIG_CPU_SUBTYPE_ST40STB1) + { + .port = { + .membase = (void *)0xffe00000, + .mapbase = 0xffe00000, + .iotype = SERIAL_IO_MEM, + .irq = 26, + .ops = &sci_uart_ops, + .flags = ASYNC_BOOT_AUTOCONF, + .line = 0, + }, + .type = PORT_SCIF, + .irqs = STB1_SCIF1_IRQS, + .init_pins = sci_init_pins_scif, + }, + { + .port = { + .membase = (void *)0xffe80000, + .mapbase = 0xffe80000, + .iotype = SERIAL_IO_MEM, + .irq = 43, + .ops = &sci_uart_ops, + .flags = ASYNC_BOOT_AUTOCONF, + .line = 1, + }, + .type = PORT_SCIF, + .irqs = SH4_SCIF_IRQS, + .init_pins = sci_init_pins_scif, + }, +#elif defined(CONFIG_H83007) || defined(CONFIG_H83068) + { + .port = { + .membase = (void *)0x00ffffb0, + .mapbase = 0x00ffffb0, + .iotype = SERIAL_IO_MEM, + .irq = 54, + .ops = &sci_uart_ops, + .flags = ASYNC_BOOT_AUTOCONF, + .line = 0, + }, + .type = PORT_SCI, + .irqs = H8300H_SCI_IRQS0, + .init_pins = sci_init_pins_sci, + }, + { + .port = { + .membase = (void *)0x00ffffb8, + .mapbase = 0x00ffffb8, + .iotype = SERIAL_IO_MEM, + .irq = 58, + .ops = &sci_uart_ops, + .flags = ASYNC_BOOT_AUTOCONF, + .line = 1, + }, + .type = PORT_SCI, + .irqs = H8300H_SCI_IRQS1, + .init_pins = sci_init_pins_sci, + }, + { + .port = { + .membase = (void *)0x00ffffc0, + .mapbase = 0x00ffffc0, + .iotype = SERIAL_IO_MEM, + .irq = 62, + .ops = &sci_uart_ops, + .flags = ASYNC_BOOT_AUTOCONF, + .line = 2, + }, + .type = PORT_SCI, + .irqs = H8300H_SCI_IRQS2, + .init_pins = sci_init_pins_sci, + }, +#elif defined(CONFIG_H8S2678) + { + .port = { + .membase = (void *)0x00ffff78, + .mapbase = 0x00ffff78, + .iotype = SERIAL_IO_MEM, + .irq = 90, + .ops = &sci_uart_ops, + .flags = ASYNC_BOOT_AUTOCONF, + .line = 0, + }, + .type = PORT_SCI, + .irqs = H8S_SCI_IRQS0, + .init_pins = sci_init_pins_sci, + }, + { + .port = { + .membase = (void *)0x00ffff80, + .mapbase = 0x00ffff80, + .iotype = SERIAL_IO_MEM, + .irq = 94, + .ops = &sci_uart_ops, + .flags = ASYNC_BOOT_AUTOCONF, + .line = 1, + }, + .type = PORT_SCI, + .irqs = H8S_IRQS1, + .init_pins = sci_init_pins_sci, + }, + { + .port = { + .membase = (void *)0x00ffff88, + .mapbase = 0x00ffff88, + .iotype = SERIAL_IO_MEM, + .irq = 98, + .ops = &sci_uart_ops, + .flags = ASYNC_BOOT_AUTOCONF, + .line = 2, + }, + .type = PORT_SCI, + .irqs = H8S_IRQS2, + .init_pins = sci_init_pins_sci, + }, +#else +#error "CPU subtype not defined" +#endif +}; + +#ifdef CONFIG_SERIAL_SH_SCI_CONSOLE +/* + * Print a string to the serial port trying not to disturb + * any possible real use of the port... + */ +static void serial_console_write(struct console *co, const char *s, + unsigned count) +{ + put_string(serial_console_port, s, count); +} + +static int __init serial_console_setup(struct console *co, char *options) +{ + struct uart_port *port; + int baud = 115200; + int bits = 8; + int parity = 'n'; + int flow = 'n'; + + if (co->index >= SCI_NPORTS) + co->index = 0; + + serial_console_port = &sci_ports[co->index]; + port = &serial_console_port->port; + port->type = serial_console_port->type; + + /* + * We need to set the initial uartclk here, since otherwise it will + * only ever be setup at sci_init() time. + */ + port->uartclk = current_cpu_data.module_clock * 16; + + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + + return uart_set_options(port, co, baud, parity, bits, flow); +} + +static struct console serial_console = { + .name = "ttySC", + .device = uart_console_device, + .write = serial_console_write, + .setup = serial_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, + .data = &sci_uart_driver, +}; + +static int __init sci_console_init(void) +{ +#ifdef CONFIG_SH_EARLY_PRINTK + extern void sh_console_unregister(void); + + /* + * Now that the real console is available, unregister the one we + * used while first booting. + */ + sh_console_unregister(); +#endif + + register_console(&serial_console); + + return 0; +} + +console_initcall(sci_console_init); +#endif /* CONFIG_SERIAL_SH_SCI_CONSOLE */ + +#ifdef CONFIG_SH_KGDB +/* + * FIXME: Most of this can go away.. at the moment, we rely on + * arch/sh/kernel/setup.c to do the command line parsing for kgdb, though + * most of that can easily be done here instead. + * + * For the time being, just accept the values that were parsed earlier.. + */ +static void __init kgdb_console_get_options(struct uart_port *port, int *baud, + int *parity, int *bits) +{ + *baud = kgdb_baud; + *parity = tolower(kgdb_parity); + *bits = kgdb_bits - '0'; +} + +/* + * The naming here is somewhat misleading, since kgdb_console_setup() takes + * care of the early-on initialization for kgdb, regardless of whether we + * actually use kgdb as a console or not. + * + * On the plus side, this lets us kill off the old kgdb_sci_setup() nonsense. + */ +int __init kgdb_console_setup(struct console *co, char *options) +{ + struct uart_port *port = &sci_ports[kgdb_portnum].port; + int baud = 38400; + int bits = 8; + int parity = 'n'; + int flow = 'n'; + + if (co->index >= SCI_NPORTS || co->index != kgdb_portnum) + co->index = kgdb_portnum; + + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + else + kgdb_console_get_options(port, &baud, &parity, &bits); + + kgdb_getchar = kgdb_sci_getchar; + kgdb_putchar = kgdb_sci_putchar; + + return uart_set_options(port, co, baud, parity, bits, flow); +} +#endif /* CONFIG_SH_KGDB */ + +#ifdef CONFIG_SH_KGDB_CONSOLE +static struct console kgdb_console = { + .name = "ttySC", + .write = kgdb_console_write, + .setup = kgdb_console_setup, + .flags = CON_PRINTBUFFER | CON_ENABLED, + .index = -1, + .data = &sci_uart_driver, +}; + +/* Register the KGDB console so we get messages (d'oh!) */ +static int __init kgdb_console_init(void) +{ + register_console(&kgdb_console); + + return 0; +} + +console_initcall(kgdb_console_init); +#endif /* CONFIG_SH_KGDB_CONSOLE */ + +#if defined(CONFIG_SH_KGDB_CONSOLE) +#define SCI_CONSOLE &kgdb_console +#elif defined(CONFIG_SERIAL_SH_SCI_CONSOLE) +#define SCI_CONSOLE &serial_console +#else +#define SCI_CONSOLE 0 +#endif + +static char banner[] __initdata = + KERN_INFO "SuperH SCI(F) driver initialized\n"; + +static struct uart_driver sci_uart_driver = { + .owner = THIS_MODULE, + .driver_name = "sci", +#ifdef CONFIG_DEVFS_FS + .devfs_name = "ttsc/", +#endif + .dev_name = "ttySC", + .major = SCI_MAJOR, + .minor = SCI_MINOR_START, + .nr = SCI_NPORTS, + .cons = SCI_CONSOLE, +}; + +static int __init sci_init(void) +{ + int chan, ret; + + printk("%s", banner); + + ret = uart_register_driver(&sci_uart_driver); + if (ret == 0) { + for (chan = 0; chan < SCI_NPORTS; chan++) { + struct sci_port *sciport = &sci_ports[chan]; + + sciport->port.uartclk = (current_cpu_data.module_clock * 16); + uart_add_one_port(&sci_uart_driver, &sciport->port); + sciport->break_timer.data = (unsigned long)sciport; + sciport->break_timer.function = sci_break_timer; + init_timer(&sciport->break_timer); + } + } + +#ifdef CONFIG_CPU_FREQ + cpufreq_register_notifier(&sci_nb, CPUFREQ_TRANSITION_NOTIFIER); + printk("sci: CPU frequency notifier registered\n"); +#endif + +#ifdef CONFIG_SH_STANDARD_BIOS + sh_bios_gdb_detach(); +#endif + + return ret; +} + +static void __exit sci_exit(void) +{ + int chan; + + for (chan = 0; chan < SCI_NPORTS; chan++) + uart_remove_one_port(&sci_uart_driver, &sci_ports[chan].port); + + uart_unregister_driver(&sci_uart_driver); +} + +module_init(sci_init); +module_exit(sci_exit); + diff -puN /dev/null drivers/serial/sh-sci.h --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25-akpm/drivers/serial/sh-sci.h 2004-03-23 02:05:25.899166224 -0800 @@ -0,0 +1,435 @@ +/* $Id: sh-sci.h,v 1.4 2004/02/19 16:43:56 lethal Exp $ + * + * linux/drivers/serial/sh-sci.h + * + * SuperH on-chip serial module support. (SCI with no FIFO / with FIFO) + * Copyright (C) 1999, 2000 Niibe Yutaka + * Copyright (C) 2000 Greg Banks + * Copyright (C) 2002, 2003 Paul Mundt + * Modified to support multiple serial ports. Stuart Menefy (May 2000). + * Modified to support H8/300 Series Yoshinori Sato (Feb 2004). + */ +#include +#include + +#if defined(__H8300H__) || defined(__H8300S__) +#include +#if defined(CONFIG_H83007) || defined(CONFIG_H83068) +#include +#endif +#if defined(CONFIG_H8S2678) +#include +#endif +#endif + +/* Offsets into the sci_port->irqs array */ +#define SCIx_ERI_IRQ 0 +#define SCIx_RXI_IRQ 1 +#define SCIx_TXI_IRQ 2 + +/* ERI, RXI, TXI, BRI */ +#define SCI_IRQS { 23, 24, 25, 0 } +#define SH3_SCIF_IRQS { 56, 57, 59, 58 } +#define SH3_IRDA_IRQS { 52, 53, 55, 54 } +#define SH4_SCIF_IRQS { 40, 41, 43, 42 } +#define STB1_SCIF1_IRQS {23, 24, 26, 25 } +#define SH7760_SCIF0_IRQS { 52, 53, 55, 54 } +#define SH7760_SCIF1_IRQS { 72, 73, 75, 74 } +#define SH7760_SCIF2_IRQS { 76, 77, 79, 78 } +#define H8300H_SCI_IRQS0 {52, 53, 54, 0 } +#define H8300H_SCI_IRQS1 {56, 57, 58, 0 } +#define H8300H_SCI_IRQS2 {60, 61, 62, 0 } +#define H8S_SCI_IRQS0 {88, 89, 90, 0 } +#define H8S_SCI_IRQS1 {92, 93, 94, 0 } +#define H8S_SCI_IRQS2 {96, 97, 98, 0 } + +#if defined(CONFIG_CPU_SUBTYPE_SH7708) +# define SCI_NPORTS 1 +# define SCSPTR 0xffffff7c /* 8 bit */ +# define SCSCR_INIT(port) 0x30 /* TIE=0,RIE=0,TE=1,RE=1 */ +# define SCI_ONLY +#elif defined(CONFIG_CPU_SUBTYPE_SH7707) || defined(CONFIG_CPU_SUBTYPE_SH7709) +# define SCI_NPORTS 3 +# define SCPCR 0xA4000116 /* 16 bit SCI and SCIF */ +# define SCPDR 0xA4000136 /* 8 bit SCI and SCIF */ +# define SCSCR_INIT(port) 0x30 /* TIE=0,RIE=0,TE=1,RE=1 */ +# define SCI_AND_SCIF +#elif defined(CONFIG_CPU_SUBTYPE_SH7750) || defined(CONFIG_CPU_SUBTYPE_SH7751) +# define SCI_NPORTS 2 +# define SCSPTR1 0xffe0001c /* 8 bit SCI */ +# define SCSPTR2 0xFFE80020 /* 16 bit SCIF */ +# define SCIF_ORER 0x0001 /* overrun error bit */ +# define SCSCR_INIT(port) (((port)->type == PORT_SCI) ? \ + 0x30 /* TIE=0,RIE=0,TE=1,RE=1 */ : \ + 0x38 /* TIE=0,RIE=0,TE=1,RE=1,REIE=1 */ ) +# define SCI_AND_SCIF +#elif defined(CONFIG_CPU_SUBTYPE_SH7760) +# define SCI_NPORTS 3 +# define SCSPTR0 0xfe600000 /* 16 bit SCIF */ +# define SCSPTR1 0xfe610000 /* 16 bit SCIF */ +# define SCSPTR2 0xfe620000 /* 16 bit SCIF */ +# define SCIF_ORDER 0x0001 /* overrun error bit */ +# define SCSCR_INIT(port) 0x38 /* TIE=0,RIE=0,TE=1,RE=1,REIE=1 */ +# define SCIF_ONLY +#elif defined(CONFIG_CPU_SUBTYPE_ST40STB1) +# define SCI_NPORTS 2 +# define SCSPTR1 0xffe00020 /* 16 bit SCIF */ +# define SCSPTR2 0xffe80020 /* 16 bit SCIF */ +# define SCIF_ORER 0x0001 /* overrun error bit */ +# define SCSCR_INIT(port) 0x38 /* TIE=0,RIE=0,TE=1,RE=1,REIE=1 */ +# define SCIF_ONLY +#elif defined(CONFIG_H83007) || defined(CONFIG_H83068) +# define SCI_NPORTS 3 +# define SCSCR_INIT(port) 0x30 /* TIE=0,RIE=0,TE=1,RE=1 */ +# define SCI_ONLY +# define H8300_SCI_DR(ch) *(volatile char *)(P1DR + h8300_sci_pins[ch].port) +#elif defined(CONFIG_H8S2678) +# define SCI_NPORTS 3 +# define SCSCR_INIT(port) 0x30 /* TIE=0,RIE=0,TE=1,RE=1 */ +# define SCI_ONLY +# define H8300_SCI_DR(ch) *(volatile char *)(P1DR + h8300_sci_pins[ch].port) +#else +# error CPU subtype not defined +#endif + +/* SCSCR */ +#define SCI_CTRL_FLAGS_TIE 0x80 /* all */ +#define SCI_CTRL_FLAGS_RIE 0x40 /* all */ +#define SCI_CTRL_FLAGS_TE 0x20 /* all */ +#define SCI_CTRL_FLAGS_RE 0x10 /* all */ +/* SCI_CTRL_FLAGS_REIE 0x08 * 7750 SCIF */ +/* SCI_CTRL_FLAGS_MPIE 0x08 * 7707 SCI, 7708 SCI, 7709 SCI, 7750 SCI */ +/* SCI_CTRL_FLAGS_TEIE 0x04 * 7707 SCI, 7708 SCI, 7709 SCI, 7750 SCI */ +/* SCI_CTRL_FLAGS_CKE1 0x02 * all */ +/* SCI_CTRL_FLAGS_CKE0 0x01 * 7707 SCI/SCIF, 7708 SCI, 7709 SCI/SCIF, 7750 SCI */ + +/* SCxSR SCI */ +#define SCI_TDRE 0x80 /* 7707 SCI, 7708 SCI, 7709 SCI, 7750 SCI */ +#define SCI_RDRF 0x40 /* 7707 SCI, 7708 SCI, 7709 SCI, 7750 SCI */ +#define SCI_ORER 0x20 /* 7707 SCI, 7708 SCI, 7709 SCI, 7750 SCI */ +#define SCI_FER 0x10 /* 7707 SCI, 7708 SCI, 7709 SCI, 7750 SCI */ +#define SCI_PER 0x08 /* 7707 SCI, 7708 SCI, 7709 SCI, 7750 SCI */ +#define SCI_TEND 0x04 /* 7707 SCI, 7708 SCI, 7709 SCI, 7750 SCI */ +/* SCI_MPB 0x02 * 7707 SCI, 7708 SCI, 7709 SCI, 7750 SCI */ +/* SCI_MPBT 0x01 * 7707 SCI, 7708 SCI, 7709 SCI, 7750 SCI */ + +#define SCI_ERRORS ( SCI_PER | SCI_FER | SCI_ORER) + +/* SCxSR SCIF */ +#define SCIF_ER 0x0080 /* 7707 SCIF, 7709 SCIF, 7750 SCIF */ +#define SCIF_TEND 0x0040 /* 7707 SCIF, 7709 SCIF, 7750 SCIF */ +#define SCIF_TDFE 0x0020 /* 7707 SCIF, 7709 SCIF, 7750 SCIF */ +#define SCIF_BRK 0x0010 /* 7707 SCIF, 7709 SCIF, 7750 SCIF */ +#define SCIF_FER 0x0008 /* 7707 SCIF, 7709 SCIF, 7750 SCIF */ +#define SCIF_PER 0x0004 /* 7707 SCIF, 7709 SCIF, 7750 SCIF */ +#define SCIF_RDF 0x0002 /* 7707 SCIF, 7709 SCIF, 7750 SCIF */ +#define SCIF_DR 0x0001 /* 7707 SCIF, 7709 SCIF, 7750 SCIF */ + +#define SCIF_ERRORS ( SCIF_PER | SCIF_FER | SCIF_ER | SCIF_BRK) + +#if defined(SCI_ONLY) +# define SCxSR_TEND(port) SCI_TEND +# define SCxSR_ERRORS(port) SCI_ERRORS +# define SCxSR_RDxF(port) SCI_RDRF +# define SCxSR_TDxE(port) SCI_TDRE +# define SCxSR_ORER(port) SCI_ORER +# define SCxSR_FER(port) SCI_FER +# define SCxSR_PER(port) SCI_PER +# define SCxSR_BRK(port) 0x00 +# define SCxSR_RDxF_CLEAR(port) 0xbc +# define SCxSR_ERROR_CLEAR(port) 0xc4 +# define SCxSR_TDxE_CLEAR(port) 0x78 +# define SCxSR_BREAK_CLEAR(port) 0xc4 +#elif defined(SCIF_ONLY) +# define SCxSR_TEND(port) SCIF_TEND +# define SCxSR_ERRORS(port) SCIF_ERRORS +# define SCxSR_RDxF(port) SCIF_RDF +# define SCxSR_TDxE(port) SCIF_TDFE +# define SCxSR_ORER(port) 0x0000 +# define SCxSR_FER(port) SCIF_FER +# define SCxSR_PER(port) SCIF_PER +# define SCxSR_BRK(port) SCIF_BRK +# define SCxSR_RDxF_CLEAR(port) 0x00fc +# define SCxSR_ERROR_CLEAR(port) 0x0073 +# define SCxSR_TDxE_CLEAR(port) 0x00df +# define SCxSR_BREAK_CLEAR(port) 0x00e3 +#else +# define SCxSR_TEND(port) (((port)->type == PORT_SCI) ? SCI_TEND : SCIF_TEND) +# define SCxSR_ERRORS(port) (((port)->type == PORT_SCI) ? SCI_ERRORS : SCIF_ERRORS) +# define SCxSR_RDxF(port) (((port)->type == PORT_SCI) ? SCI_RDRF : SCIF_RDF) +# define SCxSR_TDxE(port) (((port)->type == PORT_SCI) ? SCI_TDRE : SCIF_TDFE) +# define SCxSR_ORER(port) (((port)->type == PORT_SCI) ? SCI_ORER : 0x0000) +# define SCxSR_FER(port) (((port)->type == PORT_SCI) ? SCI_FER : SCIF_FER) +# define SCxSR_PER(port) (((port)->type == PORT_SCI) ? SCI_PER : SCIF_PER) +# define SCxSR_BRK(port) (((port)->type == PORT_SCI) ? 0x00 : SCIF_BRK) +# define SCxSR_RDxF_CLEAR(port) (((port)->type == PORT_SCI) ? 0xbc : 0x00fc) +# define SCxSR_ERROR_CLEAR(port) (((port)->type == PORT_SCI) ? 0xc4 : 0x0073) +# define SCxSR_TDxE_CLEAR(port) (((port)->type == PORT_SCI) ? 0x78 : 0x00df) +# define SCxSR_BREAK_CLEAR(port) (((port)->type == PORT_SCI) ? 0xc4 : 0x00e3) +#endif + +/* SCFCR */ +#define SCFCR_RFRST 0x0002 +#define SCFCR_TFRST 0x0004 +#define SCFCR_MCE 0x0008 + +#define SCI_MAJOR 204 +#define SCI_MINOR_START 8 + +/* Generic serial flags */ +#define SCI_RX_THROTTLE 0x0000001 + +#define SCI_MAGIC 0xbabeface + +/* + * Events are used to schedule things to happen at timer-interrupt + * time, instead of at rs interrupt time. + */ +#define SCI_EVENT_WRITE_WAKEUP 0 + +struct sci_port { + struct uart_port port; + int type; + unsigned char irqs[4]; /* ERI, RXI, TXI, BRI */ + void (*init_pins)(struct uart_port *port, unsigned int cflag); + int break_flag; + struct timer_list break_timer; +}; + +#define SCI_IN(size, offset) \ + unsigned int addr = port->mapbase + (offset); \ + if ((size) == 8) { \ + return ctrl_inb(addr); \ + } else { \ + return ctrl_inw(addr); \ + } +#define SCI_OUT(size, offset, value) \ + unsigned int addr = port->mapbase + (offset); \ + if ((size) == 8) { \ + ctrl_outb(value, addr); \ + } else { \ + ctrl_outw(value, addr); \ + } + +#define CPU_SCIx_FNS(name, sci_offset, sci_size, scif_offset, scif_size)\ + static inline unsigned int sci_##name##_in(struct uart_port *port) \ + { \ + if (port->type == PORT_SCI) { \ + SCI_IN(sci_size, sci_offset) \ + } else { \ + SCI_IN(scif_size, scif_offset); \ + } \ + } \ + static inline void sci_##name##_out(struct uart_port *port, unsigned int value) \ + { \ + if (port->type == PORT_SCI) { \ + SCI_OUT(sci_size, sci_offset, value) \ + } else { \ + SCI_OUT(scif_size, scif_offset, value); \ + } \ + } + +#define CPU_SCIF_FNS(name, scif_offset, scif_size) \ + static inline unsigned int sci_##name##_in(struct uart_port *port) \ + { \ + SCI_IN(scif_size, scif_offset); \ + } \ + static inline void sci_##name##_out(struct uart_port *port, unsigned int value) \ + { \ + SCI_OUT(scif_size, scif_offset, value); \ + } + +#define CPU_SCI_FNS(name, sci_offset, sci_size) \ + static inline unsigned int sci_##name##_in(struct sci_port* port) \ + { \ + SCI_IN(sci_size, sci_offset); \ + } \ + static inline void sci_##name##_out(struct sci_port* port, unsigned int value) \ + { \ + SCI_OUT(sci_size, sci_offset, value); \ + } + +#ifdef CONFIG_CPU_SH3 +#define SCIx_FNS(name, sh3_sci_offset, sh3_sci_size, sh4_sci_offset, sh4_sci_size, \ + sh3_scif_offset, sh3_scif_size, sh4_scif_offset, sh4_scif_size, \ + h8_sci_offset, h8_sci_size) \ + CPU_SCIx_FNS(name, sh3_sci_offset, sh3_sci_size, sh3_scif_offset, sh3_scif_size) +#define SCIF_FNS(name, sh3_scif_offset, sh3_scif_size, sh4_scif_offset, sh4_scif_size) \ + CPU_SCIF_FNS(name, sh3_scif_offset, sh3_scif_size) +#elif defined(__H8300H__) || defined(__H8300S__) +#define SCIx_FNS(name, sh3_sci_offset, sh3_sci_size, sh4_sci_offset, sh4_sci_size, \ + sh3_scif_offset, sh3_scif_size, sh4_scif_offset, sh4_scif_size, \ + h8_sci_offset, h8_sci_size) \ + CPU_SCI_FNS(name, h8_sci_offset, h8_sci_size) +#define SCIF_FNS(name, sh3_scif_offset, sh3_scif_size, sh4_scif_offset, sh4_scif_size) +#else +#define SCIx_FNS(name, sh3_sci_offset, sh3_sci_size, sh4_sci_offset, sh4_sci_size, \ + sh3_scif_offset, sh3_scif_size, sh4_scif_offset, sh4_scif_size, \ + h8_sci_offset, h8_sci_size) \ + CPU_SCIx_FNS(name, sh4_sci_offset, sh4_sci_size, sh4_scif_offset, sh4_scif_size) +#define SCIF_FNS(name, sh3_scif_offset, sh3_scif_size, sh4_scif_offset, sh4_scif_size) \ + CPU_SCIF_FNS(name, sh4_scif_offset, sh4_scif_size) +#endif + +/* reg SCI/SH3 SCI/SH4 SCIF/SH3 SCIF/SH4 SCI/H8*/ +/* name off sz off sz off sz off sz off sz*/ +SCIx_FNS(SCSMR, 0x00, 8, 0x00, 8, 0x00, 8, 0x00, 16, 0x00, 8) +SCIx_FNS(SCBRR, 0x02, 8, 0x04, 8, 0x02, 8, 0x04, 8, 0x01, 8) +SCIx_FNS(SCSCR, 0x04, 8, 0x08, 8, 0x04, 8, 0x08, 16, 0x02, 8) +SCIx_FNS(SCxTDR, 0x06, 8, 0x0c, 8, 0x06, 8, 0x0C, 8, 0x03, 8) +SCIx_FNS(SCxSR, 0x08, 8, 0x10, 8, 0x08, 16, 0x10, 16, 0x04, 8) +SCIx_FNS(SCxRDR, 0x0a, 8, 0x14, 8, 0x0A, 8, 0x14, 8, 0x05, 8) +SCIF_FNS(SCFCR, 0x0c, 8, 0x18, 16) +SCIF_FNS(SCFDR, 0x0e, 16, 0x1C, 16) +SCIF_FNS(SCLSR, 0, 0, 0x24, 16) + +#define sci_in(port, reg) sci_##reg##_in(port) +#define sci_out(port, reg, value) sci_##reg##_out(port, value) + +/* H8/300 series SCI pins assignment */ +#if defined(__H8300H__) || defined(__H8300S__) +static const struct __attribute__((packed)) { + int port; /* GPIO port no */ + unsigned short rx,tx; /* GPIO bit no */ +} h8300_sci_pins[] = { +#if defined(CONFIG_H83007) || defined(CONFIG_H83068) + { /* SCI0 */ + .port = H8300_GPIO_P9, + .rx = H8300_GPIO_B2, + .tx = H8300_GPIO_B0, + }, + { /* SCI1 */ + .port = H8300_GPIO_P9, + .rx = H8300_GPIO_B3, + .tx = H8300_GPIO_B1, + }, + { /* SCI2 */ + .port = H8300_GPIO_PB, + .rx = H8300_GPIO_B7, + .tx = H8300_GPIO_B6, + } +#elif defined(CONFIG_H8S2678) + { /* SCI0 */ + .port = H8300_GPIO_P3, + .rx = H8300_GPIO_B2, + .tx = H8300_GPIO_B0, + }, + { /* SCI1 */ + .port = H8300_GPIO_P3, + .rx = H8300_GPIO_B3, + .tx = H8300_GPIO_B1, + }, + { /* SCI2 */ + .port = H8300_GPIO_P5, + .rx = H8300_GPIO_B1, + .tx = H8300_GPIO_B0, + } +#endif +}; +#endif + +#if defined(CONFIG_CPU_SUBTYPE_SH7708) +static inline int sci_rxd_in(struct uart_port *port) +{ + if (port->mapbase == 0xfffffe80) + return ctrl_inb(SCSPTR)&0x01 ? 1 : 0; /* SCI */ + return 1; +} +#elif defined(CONFIG_CPU_SUBTYPE_SH7707) || defined(CONFIG_CPU_SUBTYPE_SH7709) +static inline int sci_rxd_in(struct uart_port *port) +{ + if (port->mapbase == 0xfffffe80) + return ctrl_inb(SCPDR)&0x01 ? 1 : 0; /* SCI */ + if (port->mapbase == 0xa4000150) + return ctrl_inb(SCPDR)&0x10 ? 1 : 0; /* SCIF */ + if (port->mapbase == 0xa4000140) + return ctrl_inb(SCPDR)&0x04 ? 1 : 0; /* IRDA */ + return 1; +} +#elif defined(CONFIG_CPU_SUBTYPE_SH7750) || defined(CONFIG_CPU_SUBTYPE_SH7751) +static inline int sci_rxd_in(struct uart_port *port) +{ +#ifndef SCIF_ONLY + if (port->mapbase == 0xffe00000) + return ctrl_inb(SCSPTR1)&0x01 ? 1 : 0; /* SCI */ +#endif +#ifndef SCI_ONLY + if (port->mapbase == 0xffe80000) + return ctrl_inw(SCSPTR2)&0x0001 ? 1 : 0; /* SCIF */ +#endif + return 1; +} +#elif defined(CONFIG_CPU_SUBTYPE_SH7760) +static inline int sci_rxd_in(struct uart_port *port) +{ + if (port->mapbase == 0xfe600000) + return ctrl_inw(SCSPTR0) & 0x0001 ? 1 : 0; /* SCIF */ + if (port->mapbase == 0xfe610000) + return ctrl_inw(SCSPTR1) & 0x0001 ? 1 : 0; /* SCIF */ + if (port->mapbase == 0xfe620000) + return ctrl_inw(SCSPTR2) & 0x0001 ? 1 : 0; /* SCIF */ +} +#elif defined(CONFIG_CPU_SUBTYPE_ST40STB1) +static inline int sci_rxd_in(struct uart_port *port) +{ + if (port->mapbase == 0xffe00000) + return ctrl_inw(SCSPTR1)&0x0001 ? 1 : 0; /* SCIF */ + else + return ctrl_inw(SCSPTR2)&0x0001 ? 1 : 0; /* SCIF */ + +} +#elif defined(__H8300H__) || defined(__H8300S__) +static inline int sci_rxd_in(struct sci_port *port) +{ + int ch = (port->base - SMR0) >> 3; + return (H8300_SCI_DR(ch) & h8300_sci_pins[ch].rx) ? 1 : 0; +} +#endif + +/* + * Values for the BitRate Register (SCBRR) + * + * The values are actually divisors for a frequency which can + * be internal to the SH3 (14.7456MHz) or derived from an external + * clock source. This driver assumes the internal clock is used; + * to support using an external clock source, config options or + * possibly command-line options would need to be added. + * + * Also, to support speeds below 2400 (why?) the lower 2 bits of + * the SCSMR register would also need to be set to non-zero values. + * + * -- Greg Banks 27Feb2000 + * + * Answer: The SCBRR register is only eight bits, and the value in + * it gets larger with lower baud rates. At around 2400 (depending on + * the peripherial module clock) you run out of bits. However the + * lower two bits of SCSMR allow the module clock to be divided down, + * scaling the value which is needed in SCBRR. + * + * -- Stuart Menefy - 23 May 2000 + * + * I meant, why would anyone bother with bitrates below 2400. + * + * -- Greg Banks - 7Jul2000 + * + * You "speedist"! How will I use my 110bps ASR-33 teletype with paper + * tape reader as a console! + * + * -- Mitch Davis - 15 Jul 2000 + */ + +#define PCLK (current_cpu_data.module_clock) + +#if !defined(__H8300H__) && !defined(__H8300S__) +#define SCBRR_VALUE(bps) ((PCLK+16*bps)/(32*bps)-1) +#else +#define SCBRR_VALUE(bps) (((CONFIG_CPU_CLOCK*1000/32)/bps)-1) +#endif +#define BPS_2400 SCBRR_VALUE(2400) +#define BPS_4800 SCBRR_VALUE(4800) +#define BPS_9600 SCBRR_VALUE(9600) +#define BPS_19200 SCBRR_VALUE(19200) +#define BPS_38400 SCBRR_VALUE(38400) +#define BPS_57600 SCBRR_VALUE(57600) +#define BPS_115200 SCBRR_VALUE(115200) + diff -puN include/linux/serial_core.h~sh-02-sh-sci include/linux/serial_core.h --- 25/include/linux/serial_core.h~sh-02-sh-sci 2004-03-23 02:05:25.887168048 -0800 +++ 25-akpm/include/linux/serial_core.h 2004-03-23 02:05:25.900166072 -0800 @@ -78,6 +78,11 @@ #define PORT_MAC_ZILOG 50 /* m68k : not yet implemented */ #define PORT_PMAC_ZILOG 51 +/* SH-SCI */ +#define PORT_SCI 52 +#define PORT_SCIF 53 +#define PORT_IRDA 54 + #ifdef __KERNEL__ #include _