// SPDX-License-Identifier: GPL-2.0+
/*
* 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/tty.h>
#include <linux/tty_flip.h>
#include <linux/ioport.h>
#include <linux/init.h>
#include <linux/console.h>
#include <linux/sysrq.h>
#include <linux/serial.h>
#include <linux/delay.h>
#include <asm/m32r.h>
#include <asm/io.h>
#include <asm/irq.h>
#define BAUD_RATE 115200
#include <linux/serial_core.h>
#include "m32r_sio_reg.h"
#define PASS_LIMIT 256
static const struct {
unsigned int port;
unsigned int irq;
} old_serial_port[] = {
#if defined(CONFIG_PLAT_USRV)
/* PORT IRQ FLAGS */
{ 0x3F8, PLD_IRQ_UART0 }, /* ttyS0 */
{ 0x2F8, PLD_IRQ_UART1 }, /* ttyS1 */
#elif defined(CONFIG_SERIAL_M32R_PLDSIO)
{ ((unsigned long)PLD_ESIO0CR), PLD_IRQ_SIO0_RCV }, /* ttyS0 */
#else
{ M32R_SIO_OFFSET, M32R_IRQ_SIO0_R }, /* ttyS0 */
#endif
};
#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 char ier;
};
struct irq_info {
spinlock_t lock;
struct list_head *head;
};
static struct irq_info irq_lists[NR_IRQS];
#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);
__sio_out(0x0003, PLD_ESIO0CR);
}
static void sio_init(void)
{
unsigned short tmp;
tmp = __sio_in(PLD_ESIO0RXB);
tmp = __sio_in(PLD_ESIO0RXB);
tmp = __sio_in(PLD_ESIO0CR);
__sio_out(0x0300, PLD_ESIO0CR);
__sio_out(0x0003, PLD_ESIO0CR);
}
static void sio_error(int *status)
{
printk("SIO0 error[%04x]\n", *status);
do {
sio_init();
} while ((*status = __sio_in(PLD_ESIO0CR)) != 3);
}
#else /* not CONFIG_SERIAL_M32R_PLDSIO */
#define __sio_in(x) inl(x)
#define __sio_out(v,x) outl((v),(x))
static inline void sio_set_baud_rate(unsigned long baud)
{
unsigned long i, j;
i = boot_cpu_data.bus_clock / (baud * 16);
j = (boot_cpu_data.bus_clock - (i * baud * 16)) / baud;
i -= 1;
j = (j + 1) >> 1;
__sio_out(i, M32R_SIO0_BAUR_PORTL);
__sio_out(j, M32R_SIO0_RBAUR_PORTL);
}
static void sio_reset(void)
{
__sio_out(0x00000300, M32R_SIO0_CR_PORTL); /* init status */
__sio_out(0x00000800, M32R_SIO0_MOD1_PORTL); /* 8bit */
__sio_out(0x00000080, M32R_SIO0_MOD0_PORTL); /* 1stop non */
sio_set_baud_rate(BAUD_RATE);
__sio_out(0x00000000, M32R_SIO0_TRCR_PORTL);
__sio_out(0x00000003, M32R_SIO0_CR_PORTL); /* RXCEN */
}
static void sio_init(void)
{
unsigned int tmp;
tmp = __sio_in(M32R_SIO0_RXB_PORTL);
tmp = __sio_in(M32R_SIO0_RXB_PORTL);
tmp = __sio_in(M32R_SIO0_STS_PORTL);
__sio_out(0x00000003, M32R_SIO0_CR_PORTL);
}
static void sio_error(int *status)
{
printk("SIO0 error[%04x]\n", *status);
do {
sio_init();
} while ((*status = __sio_in(M32R_SIO0_CR_PORTL)) != 3);
}
#endif /* CONFIG_SERIAL_M32R_PLDSIO */
static unsigned int sio_in(struct uart_sio_port *up, int offset)
{
return __sio_in(up->port.iobase + offset);
}
static void sio_out(struct uart_sio_port *up, int offset, int value)
{
__sio_out(value, up->port.iobase + offset);
}
static unsigned int serial_in(struct uart_sio_port *up, int offset)
{
if (!offset)
return 0;
return __sio_in(offset);
}
static void serial_out(struct uart_sio_port *up, int offset, int value)
{
if (!offset)
return;
__sio_out(value, offset);
}
static void m32r_sio_stop_tx(struct uart_port *port)
{
struct uart_sio_port *up =
container_of(port, struct uart_sio_port, port);
if (up->ier