/*
* Driver for the PSC of the Freescale MPC52xx PSCs configured as UARTs.
*
* FIXME According to the usermanual the status bits in the status register
* are only updated when the peripherals access the FIFO and not when the
* CPU access them. So since we use this bits to know when we stop writing
* and reading, they may not be updated in-time and a race condition may
* exists. But I haven't be able to prove this and I don't care. But if
* any problem arises, it might worth checking. The TX/RX FIFO Stats
* registers should be used in addition.
* Update: Actually, they seem updated ... At least the bits we use.
*
*
* Maintainer : Sylvain Munaut <tnt@246tNt.com>
*
* Some of the code has been inspired/copied from the 2.4 code written
* by Dale Farnsworth <dfarnsworth@mvista.com>.
*
* Copyright (C) 2008 Freescale Semiconductor Inc.
* John Rigby <jrigby@gmail.com>
* Added support for MPC5121
* Copyright (C) 2006 Secret Lab Technologies Ltd.
* Grant Likely <grant.likely@secretlab.ca>
* Copyright (C) 2004-2006 Sylvain Munaut <tnt@246tNt.com>
* Copyright (C) 2003 MontaVista, Software, Inc.
*
* This file is licensed under the terms of the GNU General Public License
* version 2. This program is licensed "as is" without any warranty of any
* kind, whether express or implied.
*/
#undef DEBUG
#include <linux/device.h>
#include <linux/module.h>
#include <linux/tty.h>
#include <linux/serial.h>
#include <linux/sysrq.h>
#include <linux/console.h>
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/clk.h>
#include <asm/mpc52xx.h>
#include <asm/mpc52xx_psc.h>
#if defined(CONFIG_SERIAL_MPC52xx_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
#define SUPPORT_SYSRQ
#endif
#include <linux/serial_core.h>
/* We've been assigned a range on the "Low-density serial ports" major */
#define SERIAL_PSC_MAJOR 204
#define SERIAL_PSC_MINOR 148
#define ISR_PASS_LIMIT 256 /* Max number of iteration in the interrupt */
static struct uart_port mpc52xx_uart_ports[MPC52xx_PSC_MAXNUM];
/* Rem: - We use the read_status_mask as a shadow of
* psc->mpc52xx_psc_imr
* - It's important that is array is all zero on start as we
* use it to know if it's initialized or not ! If it's not sure
* it's cleared, then a memset(...,0,...) should be added to
* the console_init
*/
/* lookup table for matching device nodes to index numbers */
static struct device_node *mpc52xx_uart_nodes[MPC52xx_PSC_MAXNUM];
static void mpc52xx_uart_of_enumerate(void);
#define PSC(port) ((struct mpc52xx_psc __iomem *)((port)->membase))
/* Forward declaration of the interruption handling routine */
static irqreturn_t mpc52xx_uart_int(int irq, void *dev_id);
static irqreturn_t mpc5xxx_uart_process_int(struct uart_port *port);
/* Simple macro to test if a port is console or not. This one is taken
* for serial_core.c and maybe should be moved to serial_core.h ? */
#ifdef CONFIG_SERIAL_CORE_CONSOLE
#define uart_console(port) \
((port)->cons && (port)->cons->index == (port)->line)
#else
#define uart_console(port) (0)
#endif
/* ======================================================================== */
/* PSC fifo operations for isolating differences between 52xx and 512x */
/* ======================================================================== */
struct psc_ops {
void (*fifo_init)(struct uart_port *port);
int (*raw_rx_rdy)(struct uart_port *port);
int (*raw_tx_rdy)(struct uart_port *port);
int (*rx_rdy)(struct uart_port *port);
int (*tx_rdy)(struct uart_port *port);
int (*tx_empty)(struct uart_port *port);
void (*stop_rx)(struct uart_port *port);
void (*start_tx)(struct uart_port *port);
void (*stop_tx)(struct uart_port *port);
void (*rx_clr_irq)(struct uart_port *port);
void (*tx_clr_irq)(struct uart_port *port);
void (*write_char)(struct uart_port *port, unsigned char c);
unsigned char (*read_char)(struct uart_port *port);
void (*cw_disable_ints)(struct uart_port *port);
void (*cw_restore_ints)(struct uart_port *port);
unsigned int (*set_baudrate)(struct uart_port *port,
struct ktermios *new,
struct ktermios *old);
int (*clock)(struct uart_port *port, int enable);
int (*fifoc_init)(void);
void (*fifoc_uninit)(void);
void (*get_irq)(struct uart_port *, struct device_node *);
irqreturn_t (*handle_irq)(struct uart_port *port);
};
/* setting the prescaler and divisor reg is common for all chips */
static inline void mpc52xx_set_divisor(struct mpc52xx_psc __iomem *psc,
u16 prescaler, unsigned int divisor)
{
/* select prescaler */
out_be16(&psc->mpc52xx_psc_clock_select, prescaler);
out_8(&psc->ctur, divisor >> 8);
out_8(&psc->ctlr, divisor & 0xff);
}
#ifdef CONFIG_PPC_MPC52xx
#define FIFO_52xx(port) ((struct mpc52xx_psc_fifo __iomem *)(PSC(port)+1))
static void mpc52xx_psc_fifo_init(struct uart_port *port)
{
struct mpc52xx_psc __iomem *psc = PSC(port);
struct mpc52xx_psc_fifo __iomem *fifo = FIFO_52xx(port);
out_8(&fifo->rfcntl, 0x00);
out_be16(&fifo->rfalarm, 0x1ff);
out_8(&fifo->tfcntl, 0x07);
out_be16(&fifo->tfalarm, 0x80);
port->read_status_mask |= MPC52xx_PSC_IMR_RXRDY | MPC52xx_PSC_IMR_TXRDY;
out_be16(&psc->mpc52xx_psc_imr, port->read_status_mask);
}
static int mpc52xx_psc_raw_rx_rdy(struct uart_port *port)
{
return in_be16(&PSC(port)->mpc52xx_psc_status)
& MPC52xx_PSC_SR_RXRDY;
}
static int mpc52xx_psc_raw_tx_rdy(struct uart_port *port)
{
return in_be16(&PSC(port)->mpc52xx_psc_status)
& MPC52xx_PSC_SR_TXRDY;
}
static int mpc52xx_psc_rx_rdy(struct uart_port *port)
{
return in_be16(&PSC(port)->mpc52xx_psc_isr)
& port->read_status_mask
& MPC52xx_PSC_IMR_RXRDY;
}
static int mpc52xx_psc_tx_rdy(struct uart_port *port)
{
return in_be16(&PSC(port)->mpc52xx_psc_isr)
& port->read_status_mask
& MPC52xx_PSC_IMR_TXRDY;
}
static int mpc52xx_psc_tx_empty(struct uart_port *port)
{
return in_be16(&PSC(port)->mpc52xx_psc_status)
& MPC52xx_PSC_SR_TXEMP;
}
static void mpc52xx_psc_start_tx(struct uart_port *port)
{
port->read_status_mask |= MPC52xx_PSC_IMR_TXRDY;
out_be16(&PSC(port)->mpc52xx_psc_imr, port->read_status_mask);
}
static void mpc52xx_psc_stop_tx(struct uart_port *port)
{
port->read_status_mask &= ~MPC52xx_PSC_IMR_TXRDY;
out_be16(&PSC(port)->mpc52xx_psc_imr, port->read_status_mask);
}
static void mpc52xx_psc_stop_rx(struct uart_port *port)
{
port->read_status_mask &= ~MPC52xx_PSC_IMR_RXRDY;
out_be16(&PSC(port)->mpc52xx_psc_imr, port->read_status_mask);
}
static void mpc52xx_psc_rx_clr_irq(struct uart_port *port)
{
}
static void mpc52xx_psc_tx_clr_irq(struct uart_port *port)
{
}
static void mpc52xx_psc_write_char(struct uart_port *port, unsigned char c)
{
out_8(&PSC(port)->mpc52xx_psc_buffer_8, c);
}
static unsigned char mpc52xx_psc_read_char(struct uart_port *port)
{
return in_8(&PSC(port)->mpc52xx_psc_buffer_8);
}
static void mpc52xx_psc_cw_disable_ints(struct uart_port *port)
{
out_be16(&PSC(port)->mpc52xx_psc_imr, 0);
}
static void mpc52xx_psc_cw_restore_ints(struct uart_port *port)
{
out_be16(&PSC(port)->mpc52xx_psc_imr, port->read_status_mask);
}
static unsigned int mpc5200_psc_set_baudrate(struct uart_port *port,
struct ktermios *new,
struct ktermios *old)
{
unsigned int baud;
unsigned int divisor;
/* The 5200 has a fixed /32 prescaler, uartclk contains the ipb freq */
|