/*
* m32r_sio.c
*
* Driver for M32R serial ports
*
* Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o.
* Based on drivers/serial/8250.c.
*
* Copyright (C) 2001 Russell King.
* Copyright (C) 2004 Hirokazu Takata <takata at linux-m32r.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
/*
* A note about mapbase / membase
*
* mapbase is the physical address of the IO port. Currently, we don't
* support this very well, and it may well be dropped from this driver
* in future. As such, mapbase should be NULL.
*
* membase is an 'ioremapped' cookie. This is compatible with the old
* serial.c driver, and is currently the preferred form.
*/
#if defined(CONFIG_SERIAL_M32R_SIO_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
#define SUPPORT_SYSRQ
#endif
#include <linux/module.h>
#include <linux/tty.h>
#include <linux/ioport.h>
#include <linux/init.h>
#include <linux/console.h>
#include <linux/sysrq.h>
#include <linux/serial.h>
#include <linux/serialP.h>
#include <linux/delay.h>
#include <asm/m32r.h>
#include <asm/io.h>
#include <asm/irq.h>
#define PORT_M32R_BASE PORT_M32R_SIO
#define PORT_INDEX(x) (x - PORT_M32R_BASE + 1)
#define BAUD_RATE 115200
#include <linux/serial_core.h>
#include "m32r_sio.h"
#include "m32r_sio_reg.h"
/*
* Debugging.
*/
#if 0
#define DEBUG_AUTOCONF(fmt...) printk(fmt)
#else
#define DEBUG_AUTOCONF(fmt...) do { } while (0)
#endif
#if 0
#define DEBUG_INTR(fmt...) printk(fmt)
#else
#define DEBUG_INTR(fmt...) do { } while (0)
#endif
#define PASS_LIMIT 256
/*
* We default to IRQ0 for the "no irq" hack. Some
* machine types want others as well - they're free
* to redefine this in their header file.
*/
#define is_real_interrupt(irq) ((irq) != 0)
#define BASE_BAUD 115200
/* Standard COM flags */
#define STD_COM_FLAGS (UPF_BOOT_AUTOCONF | UPF_SKIP_TEST)
/*
* SERIAL_PORT_DFNS tells us about built-in ports that have no
* standard enumeration mechanism. Platforms that can find all
* serial ports via mechanisms like ACPI or PCI need not supply it.
*/
#if defined(CONFIG_PLAT_USRV)
#define SERIAL_PORT_DFNS \
/* UART CLK PORT IRQ FLAGS */ \
{ 0, BASE_BAUD, 0x3F8, PLD_IRQ_UART0, STD_COM_FLAGS }, /* ttyS0 */ \
{ 0, BASE_BAUD, 0x2F8, PLD_IRQ_UART1, STD_COM_FLAGS }, /* ttyS1 */
#else /* !CONFIG_PLAT_USRV */
#if defined(CONFIG_SERIAL_M32R_PLDSIO)
#define SERIAL_PORT_DFNS \
{ 0, BASE_BAUD, ((unsigned long)PLD_ESIO0CR), PLD_IRQ_SIO0_RCV, \
STD_COM_FLAGS }, /* ttyS0 */
#else
#define SERIAL_PORT_DFNS \
{ 0, BASE_BAUD, M32R_SIO_OFFSET, M32R_IRQ_SIO0_R, \
STD_COM_FLAGS }, /* ttyS0 */
#endif
#endif /* !CONFIG_PLAT_USRV */
static struct old_serial_port old_serial_port[] = {
SERIAL_PORT_DFNS
};
#define UART_NR ARRAY_SIZE(old_serial_port)
struct uart_sio_port {
struct uart_port port;
struct timer_list timer; /* "no irq" timer */
struct list_head list; /* ports on this IRQ */
unsigned short rev;
unsigned char acr;
unsigned char ier;
unsigned char lcr;
unsigned char mcr_mask; /* mask of user bits */
unsigned char mcr_force; /* mask of forced bits */
unsigned char lsr_break_flag;
/*
* We provide a per-port pm hook.
*/
void (*pm)(struct uart_port *port,
unsigned int state, unsigned int old);
};
struct irq_info {
spinlock_t lock;
struct list_head *head;
};
static struct irq_info irq_lists[NR_IRQS];
/*
* Here we define the default xmit fifo size used for each type of UART.
*/
static const struct serial_uart_config uart_config[] = {
[PORT_UNKNOWN] = {
.name = "unknown",
.dfl_xmit_fifo_size = 1,
.flags = 0,
},
[PORT_INDEX(PORT_M32R_SIO)] = {
.name = "M32RSIO",
.dfl_xmit_fifo_size = 1,
.flags = 0,
},
};
#ifdef CONFIG_SERIAL_M32R_PLDSIO
#define __sio_in(x) inw((unsigned long)(x))
#define __sio_out(v,x) outw((v),(unsigned long)(x))
static inline void sio_set_baud_rate(unsigned long baud)
{
unsigned short sbaud;
sbaud = (boot_cpu_data.bus_clock / (baud * 4))-1;
__sio_out(sbaud, PLD_ESIO0BAUR);
}
static void sio_reset(void)
{
unsigned short tmp;
tmp = __sio_in(PLD_ESIO0RXB);
tmp = __sio_in(PLD_ESIO0RXB);
tmp = __sio_in(PLD_ESIO0CR);
sio_set_baud_rate(BAUD_RATE);
__sio_out(0x0300, PLD_ESIO0CR);