Commit 221bfb31 authored by David Woodhouse's avatar David Woodhouse

UART driver for Motorola CPM/CPM2 I/O core on 8xx/8xxx chips.

parent b0e99957
......@@ -616,5 +616,65 @@ config SERIAL_PMACZILOG_CONSOLE
on your PowerMac as the console, you can do so by answering
Y to this option.
config SERIAL_CPM
tristate "CPM2 SCC/SMC serial port support"
depends on CPM2 || 8xx
select SERIAL_CORE
help
This driver supports the SCC and SMC serial ports on Motorola
embedded PowerPC that contain a CPM2 (8xxx) or a CPM1 (8xx)
config SERIAL_CPM_CONSOLE
bool "Support for console on CPM2 SCC/SMC serial port"
depends on SERIAL_CPM=y
select SERIAL_CORE_CONSOLE
help
Say Y here if you wish to use a SCC or SMC CPM UART as the system
console (the system console is the device which receives all kernel
messages and warnings and which allows logins in single user mode).
Even if you say Y here, the currently visible framebuffer console
(/dev/tty0) will still be used as the system console by default, but
you can alter that using a kernel command line option such as
"console=ttyCPM0". (Try "man bootparam" or see the documentation of
your boot loader (lilo or loadlin) about how to pass options to the
kernel at boot time.)
config SERIAL_CPM_SCC1
bool "Support for SCC1 serial port"
depends on SERIAL_CPM=y
help
Select the is option to use SCC1 as a serial port
config SERIAL_CPM_SCC2
bool "Support for SCC2 serial port"
depends on SERIAL_CPM=y
help
Select the is option to use SCC2 as a serial port
config SERIAL_CPM_SCC3
bool "Support for SCC3 serial port"
depends on SERIAL_CPM=y
help
Select the is option to use SCC3 as a serial port
config SERIAL_CPM_SCC4
bool "Support for SCC4 serial port"
depends on SERIAL_CPM=y
help
Select the is option to use SCC4 as a serial port
config SERIAL_CPM_SMC1
bool "Support for SMC1 serial port"
depends on SERIAL_CPM=y
help
Select the is option to use SMC1 as a serial port
config SERIAL_CPM_SMC2
bool "Support for SMC2 serial port"
depends on SERIAL_CPM=y
help
Select the is option to use SMC2 as a serial port
endmenu
......@@ -39,3 +39,4 @@ obj-$(CONFIG_SERIAL_AU1X00) += au1x00_uart.o
obj-$(CONFIG_SERIAL_DZ) += dz.o
obj-$(CONFIG_SERIAL_SH_SCI) += sh-sci.o
obj-$(CONFIG_SERIAL_BAST_SIO) += bast_sio.o
obj-$(CONFIG_SERIAL_CPM) += cpm_uart/
#
# Makefile for the Motorola 8xx FEC ethernet controller
#
obj-$(CONFIG_SERIAL_CPM) += cpm_uart.o
# Select the correct platform objects.
cpm_uart-objs-$(CONFIG_CPM2) += cpm_uart_cpm2.o
cpm_uart-objs-$(CONFIG_8xx) += cpm_uart_cpm1.o
cpm_uart-objs := cpm_uart_core.o $(cpm_uart-objs-y)
/*
* linux/drivers/serial/cpm_uart.h
*
* Driver for CPM (SCC/SMC) serial ports
*
* Copyright (C) 2004 Motorola, Inc
*
*/
#ifndef CPM_UART_H
#define CPM_UART_H
#include <linux/config.h>
#if defined(CONFIG_CPM2)
#include "cpm_uart_cpm2.h"
#elif defined(CONFIG_8xx)
#include "cpm_uart_cpm1.h"
#endif
#ifndef CONFIG_SERIAL_8250
#define SERIAL_CPM_MAJOR TTY_MAJOR
#define SERIAL_CPM_MINOR 64
#else
#define SERIAL_CPM_MAJOR 204
#define SERIAL_CPM_MINOR 42
#endif
#define IS_SMC(pinfo) (pinfo->flags & FLAG_SMC)
#define FLAG_SMC 0x00000002
#define FLAG_CONSOLE 0x00000001
#define UART_SMC1 0
#define UART_SMC2 1
#define UART_SCC1 2
#define UART_SCC2 3
#define UART_SCC3 4
#define UART_SCC4 5
#define UART_NR 6
#define RX_NUM_FIFO 4
#define RX_BUF_SIZE 32
#define TX_NUM_FIFO 4
#define TX_BUF_SIZE 32
struct uart_cpm_port {
struct uart_port port;
u16 rx_nrfifos;
u16 rx_fifosize;
u16 tx_nrfifos;
u16 tx_fifosize;
smc_t *smcp;
volatile smc_uart_t *smcup;
scc_t *sccp;
scc_uart_t *sccup;
volatile cbd_t *rx_bd_base;
volatile cbd_t *rx_cur;
volatile cbd_t *tx_bd_base;
volatile cbd_t *tx_cur;
unsigned char *tx_buf;
unsigned char *rx_buf;
u32 flags;
void (*set_lineif) (struct uart_cpm_port *);
u8 brg;
uint dp_addr;
void *mem_addr;
dma_addr_t dma_addr;
int baud;
int bits;
};
extern int cpm_uart_port_map[UART_NR];
extern int cpm_uart_nr;
extern struct uart_cpm_port cpm_uart_ports[UART_NR];
/* these are located in their respective files */
extern void cpm_line_cr_cmd(int line, int cmd);
extern int cpm_uart_init_portdesc(void);
extern int cpm_uart_allocbuf(struct uart_cpm_port *pinfo, unsigned int is_con);
extern void cpm_uart_freebuf(struct uart_cpm_port *pinfo);
extern void smc1_lineif(struct uart_cpm_port *pinfo);
extern void smc2_lineif(struct uart_cpm_port *pinfo);
extern void scc1_lineif(struct uart_cpm_port *pinfo);
extern void scc2_lineif(struct uart_cpm_port *pinfo);
extern void scc3_lineif(struct uart_cpm_port *pinfo);
extern void scc4_lineif(struct uart_cpm_port *pinfo);
#endif /* CPM_UART_H */
/*
* linux/drivers/serial/cpm_uart.c
*
* Driver for CPM (SCC/SMC) serial ports; core driver
*
* Based on arch/ppc/cpm2_io/uart.c by Dan Malek
* Based on ppc8xx.c by Thomas Gleixner
* Based on drivers/serial/amba.c by Russell King
*
* Maintainer: Kumar Gala (kumar.gala@motorola.com) (CPM2)
* Pantelis Antoniou (panto@intracom.gr) (CPM1)
*
* Copyright (C) 2004 Motorola, Inc
* (C) 2004 Intracom, S.A.
*
* 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.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/tty.h>
#include <linux/ioport.h>
#include <linux/init.h>
#include <linux/serial.h>
#include <linux/console.h>
#include <linux/sysrq.h>
#include <linux/device.h>
#include <linux/bootmem.h>
#include <linux/dma-mapping.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/delay.h>
#if defined(CONFIG_SERIAL_CPM_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
#define SUPPORT_SYSRQ
#endif
#include <linux/serial_core.h>
#include "cpm_uart.h"
/***********************************************************************/
/* Track which ports are configured as uarts */
int cpm_uart_port_map[UART_NR];
/* How many ports did we config as uarts */
int cpm_uart_nr;
/*
* Check, if transmit buffers are processed
*/
static unsigned int cpm_uart_tx_empty(struct uart_port *port)
{
struct uart_cpm_port *pinfo = (struct uart_cpm_port *)port;
volatile cbd_t *bdp = pinfo->tx_bd_base;
int ret = 0;
while (1) {
if (bdp->cbd_sc & BD_SC_READY)
break;
if (bdp->cbd_sc & BD_SC_WRAP) {
ret = TIOCSER_TEMT;
break;
}
bdp++;
}
pr_debug("CPM uart[%d]:tx_empty: %d\n", port->line, ret);
return ret;
}
static void cpm_uart_set_mctrl(struct uart_port *port, unsigned int mctrl)
{
/* Whee. Do nothing. */
}
static unsigned int cpm_uart_get_mctrl(struct uart_port *port)
{
/* Whee. Do nothing. */
return TIOCM_CAR | TIOCM_DSR | TIOCM_CTS;
}
/*
* Stop transmitter
*/
static void cpm_uart_stop_tx(struct uart_port *port, unsigned int tty_stop)
{
struct uart_cpm_port *pinfo = (struct uart_cpm_port *)port;
volatile smc_t *smcp = pinfo->smcp;
volatile scc_t *sccp = pinfo->sccp;
pr_debug("CPM uart[%d]:stop tx\n", port->line);
if (IS_SMC(pinfo))
smcp->smc_smcm &= ~SMCM_TX;
else
sccp->scc_sccm &= ~UART_SCCM_TX;
}
/*
* Transmit characters, refill buffer descriptor, if possible
*/
static int cpm_uart_tx_pump(struct uart_port *port)
{
volatile cbd_t *bdp;
unsigned char *p;
int count;
struct uart_cpm_port *pinfo = (struct uart_cpm_port *)port;
struct circ_buf *xmit = &port->info->xmit;
/* Handle xon/xoff */
if (port->x_char) {
/* Pick next descriptor and fill from buffer */
bdp = pinfo->tx_cur;
p = bus_to_virt(bdp->cbd_bufaddr);
*p++ = xmit->buf[xmit->tail];
bdp->cbd_datlen = 1;
bdp->cbd_sc |= BD_SC_READY;
/* Get next BD. */
if (bdp->cbd_sc & BD_SC_WRAP)
bdp = pinfo->tx_bd_base;
else
bdp++;
pinfo->tx_cur = bdp;
port->icount.tx++;
port->x_char = 0;
return 1;
}
if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
cpm_uart_stop_tx(port, 0);
return 0;
}
/* Pick next descriptor and fill from buffer */
bdp = pinfo->tx_cur;
while (!(bdp->cbd_sc & BD_SC_READY) && (xmit->tail != xmit->head)) {
count = 0;
p = bus_to_virt(bdp->cbd_bufaddr);
while (count < pinfo->tx_fifosize) {
*p++ = xmit->buf[xmit->tail];
xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
port->icount.tx++;
count++;
if (xmit->head == xmit->tail)
break;
}
bdp->cbd_datlen = count;
bdp->cbd_sc |= BD_SC_READY;
/* Get next BD. */
if (bdp->cbd_sc & BD_SC_WRAP)
bdp = pinfo->tx_bd_base;
else
bdp++;
}
pinfo->tx_cur = bdp;
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
uart_write_wakeup(port);
if (uart_circ_empty(xmit)) {
cpm_uart_stop_tx(port, 0);
return 0;
}
return 1;
}
/*
* Start transmitter
*/
static void cpm_uart_start_tx(struct uart_port *port, unsigned int tty_start)
{
struct uart_cpm_port *pinfo = (struct uart_cpm_port *)port;
volatile smc_t *smcp = pinfo->smcp;
volatile scc_t *sccp = pinfo->sccp;
pr_debug("CPM uart[%d]:start tx\n", port->line);
if (IS_SMC(pinfo)) {
if (smcp->smc_smcm & SMCM_TX)
return;
} else {
if (sccp->scc_sccm & UART_SCCM_TX)
return;
}
if (cpm_uart_tx_pump(port) != 0) {
if (IS_SMC(pinfo))
smcp->smc_smcm |= SMCM_TX;
else
sccp->scc_sccm |= UART_SCCM_TX;
}
}
/*
* Stop receiver
*/
static void cpm_uart_stop_rx(struct uart_port *port)
{
struct uart_cpm_port *pinfo = (struct uart_cpm_port *)port;
volatile smc_t *smcp = pinfo->smcp;
volatile scc_t *sccp = pinfo->sccp;
pr_debug("CPM uart[%d]:stop rx\n", port->line);
if (IS_SMC(pinfo))
smcp->smc_smcm &= ~SMCM_RX;
else
sccp->scc_sccm &= ~UART_SCCM_RX;
}
/*
* Enable Modem status interrupts
*/
static void cpm_uart_enable_ms(struct uart_port *port)
{
pr_debug("CPM uart[%d]:enable ms\n", port->line);
}
/*
* Generate a break.
*/
static void cpm_uart_break_ctl(struct uart_port *port, int break_state)
{
struct uart_cpm_port *pinfo = (struct uart_cpm_port *)port;
pr_debug("CPM uart[%d]:break ctrl, break_state: %d\n", port->line,
break_state);
if (break_state)
cpm_line_cr_cmd(pinfo->port.line, CPM_CR_STOP_TX);
else
cpm_line_cr_cmd(pinfo->port.line, CPM_CR_RESTART_TX);
}
/*
* Transmit characters, refill buffer descriptor, if possible
*/
static void cpm_uart_int_tx(struct uart_port *port, struct pt_regs *regs)
{
pr_debug("CPM uart[%d]:TX INT\n", port->line);
cpm_uart_tx_pump(port);
}
/*
* Receive characters
*/
static void cpm_uart_int_rx(struct uart_port *port, struct pt_regs *regs)
{
int i;
unsigned char ch, *cp;
struct tty_struct *tty = port->info->tty;
struct uart_cpm_port *pinfo = (struct uart_cpm_port *)port;
volatile cbd_t *bdp;
u16 status;
unsigned int flg;
pr_debug("CPM uart[%d]:RX INT\n", port->line);
/* Just loop through the closed BDs and copy the characters into
* the buffer.
*/
bdp = pinfo->rx_cur;
for (;;) {
/* get status */
status = bdp->cbd_sc;
/* If this one is empty, return happy */
if (status & BD_SC_EMPTY)
break;
/* get number of characters, and check spce in flip-buffer */
i = bdp->cbd_datlen;
/* If we have not enough room in tty flip buffer, then we try
* later, which will be the next rx-interrupt or a timeout
*/
if ((tty->flip.count + i) >= TTY_FLIPBUF_SIZE) {
tty->flip.work.func((void *)tty);
if ((tty->flip.count + i) >= TTY_FLIPBUF_SIZE) {
printk(KERN_WARNING "TTY_DONT_FLIP set\n");
return;
}
}
/* get pointer */
cp = (unsigned char *)bus_to_virt(bdp->cbd_bufaddr);
/* loop through the buffer */
while (i-- > 0) {
ch = *cp++;
port->icount.rx++;
flg = TTY_NORMAL;
if (status &
(BD_SC_BR | BD_SC_FR | BD_SC_PR | BD_SC_OV))
goto handle_error;
if (uart_handle_sysrq_char(port, ch, regs))
continue;
error_return:
*tty->flip.char_buf_ptr++ = ch;
*tty->flip.flag_buf_ptr++ = flg;
tty->flip.count++;
} /* End while (i--) */
/* This BD is ready to be used again. Clear status. get next */
bdp->cbd_sc &= ~(BD_SC_BR | BD_SC_FR | BD_SC_PR | BD_SC_OV);
bdp->cbd_sc |= BD_SC_EMPTY;
if (bdp->cbd_sc & BD_SC_WRAP)
bdp = pinfo->rx_bd_base;
else
bdp++;
} /* End for (;;) */
/* Write back buffer pointer */
pinfo->rx_cur = (volatile cbd_t *) bdp;
/* activate BH processing */
tty_flip_buffer_push(tty);
return;
/* Error processing */
handle_error:
/* Statistics */
if (status & BD_SC_BR)
port->icount.brk++;
if (status & BD_SC_PR)
port->icount.parity++;
if (status & BD_SC_FR)
port->icount.frame++;
if (status & BD_SC_OV)
port->icount.overrun++;
/* Mask out ignored conditions */
status &= port->read_status_mask;
/* Handle the remaining ones */
if (status & BD_SC_BR)
flg = TTY_BREAK;
else if (status & BD_SC_PR)
flg = TTY_PARITY;
else if (status & BD_SC_FR)
flg = TTY_FRAME;
/* overrun does not affect the current character ! */
if (status & BD_SC_OV) {
ch = 0;
flg = TTY_OVERRUN;
/* We skip this buffer */
/* CHECK: Is really nothing senseful there */
/* ASSUMPTION: it contains nothing valid */
i = 0;
}
#ifdef SUPPORT_SYSRQ
port->sysrq = 0;
#endif
goto error_return;
}
/*
* Asynchron mode interrupt handler
*/
static irqreturn_t cpm_uart_int(int irq, void *data, struct pt_regs *regs)
{
u8 events;
struct uart_port *port = (struct uart_port *)data;
struct uart_cpm_port *pinfo = (struct uart_cpm_port *)port;
volatile smc_t *smcp = pinfo->smcp;
volatile scc_t *sccp = pinfo->sccp;
pr_debug("CPM uart[%d]:IRQ\n", port->line);
if (IS_SMC(pinfo)) {
events = smcp->smc_smce;
if (events & SMCM_BRKE)
uart_handle_break(port);
if (events & SMCM_RX)
cpm_uart_int_rx(port, regs);
if (events & SMCM_TX)
cpm_uart_int_tx(port, regs);
smcp->smc_smce = events;
} else {
events = sccp->scc_scce;
if (events & UART_SCCM_BRKE)
uart_handle_break(port);
if (events & UART_SCCM_RX)
cpm_uart_int_rx(port, regs);
if (events & UART_SCCM_TX)
cpm_uart_int_tx(port, regs);
sccp->scc_scce = events;
}
return (events) ? IRQ_HANDLED : IRQ_NONE;
}
static int cpm_uart_startup(struct uart_port *port)
{
struct uart_cpm_port *pinfo = (struct uart_cpm_port *)port;
int retval;
pr_debug("CPM uart[%d]:startup\n", port->line);
/* Install interrupt handler. */
retval = request_irq(port->irq, cpm_uart_int, 0, "cpm_uart", port);
if (retval)
return retval;
/* Startup rx-int */
if (IS_SMC(pinfo)) {
pinfo->smcp->smc_smcm |= SMCM_RX;
pinfo->smcp->smc_smcmr |= SMCMR_REN;
} else {
pinfo->sccp->scc_sccm |= UART_SCCM_RX;
}
return 0;
}
/*
* Reinit buffer descriptors
*/
static void cpm_uart_initbd(struct uart_cpm_port *pinfo)
{
int i;
volatile cbd_t *bdp;
pr_debug("CPM uart[%d]:initbd\n", pinfo->port.line);
for (bdp = pinfo->rx_cur = pinfo->rx_bd_base, i = 0; i < pinfo->rx_nrfifos; i++, bdp++)
bdp->cbd_sc = BD_SC_EMPTY | BD_SC_INTRPT | (i < (pinfo->rx_nrfifos - 1) ? 0 : BD_SC_WRAP);
for (bdp = pinfo->tx_cur = pinfo->tx_bd_base, i = 0; i < pinfo->tx_nrfifos; i++, bdp++)
bdp->cbd_sc = BD_SC_INTRPT | (i < (pinfo->rx_nrfifos - 1) ? 0 : BD_SC_WRAP);
}
/*
* Shutdown the uart
*/
static void cpm_uart_shutdown(struct uart_port *port)
{
struct uart_cpm_port *pinfo = (struct uart_cpm_port *)port;
pr_debug("CPM uart[%d]:shutdown\n", port->line);
/* free interrupt handler */
free_irq(port->irq, port);
/* If the port is not the console, disable Rx and Tx. */
if (!(pinfo->flags & FLAG_CONSOLE)) {
/* Stop uarts */
if (IS_SMC(pinfo)) {
volatile smc_t *smcp = pinfo->smcp;
smcp->smc_smcmr &= ~(SMCMR_REN | SMCMR_TEN);
smcp->smc_smcm &= ~(SMCM_RX | SMCM_TX);
} else {
volatile scc_t *sccp = pinfo->sccp;
sccp->scc_gsmrl &= ~(SCC_GSMRL_ENR | SCC_GSMRL_ENT);
sccp->scc_sccm &= ~(UART_SCCM_TX | UART_SCCM_RX);
}
/* Shut them really down and reinit buffer descriptors */
cpm_line_cr_cmd(pinfo->port.line, CPM_CR_STOP_TX);
cpm_uart_initbd(pinfo);
}
}
static void cpm_uart_init_scc(struct uart_cpm_port *pinfo, int bits, u16 scval)
{
volatile scc_t *scp;
volatile scc_uart_t *sup;
u8 *mem_addr;
volatile cbd_t *bdp;
int i;
pr_debug("CPM uart[%d]:init_scc\n", pinfo->port.line);
scp = pinfo->sccp;
sup = pinfo->sccup;
/* Set the physical address of the host memory
* buffers in the buffer descriptors, and the
* virtual address for us to work with.
*/
pinfo->rx_cur = pinfo->rx_bd_base;
mem_addr = pinfo->mem_addr;
for (bdp = pinfo->rx_bd_base, i = 0; i < pinfo->rx_nrfifos; i++, bdp++) {
bdp->cbd_bufaddr = virt_to_bus(mem_addr);
bdp->cbd_sc = BD_SC_EMPTY | BD_SC_INTRPT | (i < (pinfo->rx_nrfifos - 1) ? 0 : BD_SC_WRAP);
mem_addr += pinfo->rx_fifosize;
}
/* Set the physical address of the host memory
* buffers in the buffer descriptors, and the
* virtual address for us to work with.
*/
mem_addr = pinfo->mem_addr + L1_CACHE_ALIGN(pinfo->rx_nrfifos * pinfo->rx_fifosize);
pinfo->tx_cur = pinfo->tx_bd_base;
for (bdp = pinfo->tx_bd_base, i = 0; i < pinfo->tx_nrfifos; i++, bdp++) {
bdp->cbd_bufaddr = virt_to_bus(mem_addr);
bdp->cbd_sc = BD_SC_INTRPT | (i < (pinfo->tx_nrfifos - 1) ? 0 : BD_SC_WRAP);
mem_addr += pinfo->tx_fifosize;
bdp++;
}
/* Store address */
pinfo->sccup->scc_genscc.scc_rbase = (unsigned char *)pinfo->rx_bd_base - DPRAM_BASE;
pinfo->sccup->scc_genscc.scc_tbase = (unsigned char *)pinfo->tx_bd_base - DPRAM_BASE;
/* Set up the uart parameters in the
* parameter ram.
*/
cpm_set_scc_fcr(sup);
sup->scc_genscc.scc_mrblr = pinfo->rx_fifosize;
sup->scc_maxidl = pinfo->rx_fifosize;
sup->scc_brkcr = 1;
sup->scc_parec = 0;
sup->scc_frmec = 0;
sup->scc_nosec = 0;
sup->scc_brkec = 0;
sup->scc_uaddr1 = 0;
sup->scc_uaddr2 = 0;
sup->scc_toseq = 0;
sup->scc_char1 = 0x8000;
sup->scc_char2 = 0x8000;
sup->scc_char3 = 0x8000;
sup->scc_char4 = 0x8000;
sup->scc_char5 = 0x8000;
sup->scc_char6 = 0x8000;
sup->scc_char7 = 0x8000;
sup->scc_char8 = 0x8000;
sup->scc_rccm = 0xc0ff;
/* Send the CPM an initialize command.
*/
cpm_line_cr_cmd(pinfo->port.line, CPM_CR_INIT_TRX);
/* Set UART mode, 8 bit, no parity, one stop.
* Enable receive and transmit.
*/
scp->scc_gsmrh = 0;
scp->scc_gsmrl =
(SCC_GSMRL_MODE_UART | SCC_GSMRL_TDCR_16 | SCC_GSMRL_RDCR_16);
/* Enable rx interrupts and clear all pending events. */
scp->scc_sccm = UART_SCCM_RX;
scp->scc_scce = 0xffff;
scp->scc_dsr = 0x7e7e;
scp->scc_psmr = (bits << 12) | scval;
scp->scc_gsmrl |= (SCC_GSMRL_ENR | SCC_GSMRL_ENT);
}
static void cpm_uart_init_smc(struct uart_cpm_port *pinfo, int bits, u16 cval)
{
volatile smc_t *sp;
volatile smc_uart_t *up;
volatile u8 *mem_addr;
volatile cbd_t *bdp;
int i;
pr_debug("CPM uart[%d]:init_smc\n", pinfo->port.line);
sp = pinfo->smcp;
up = pinfo->smcup;
/* Set the physical address of the host memory
* buffers in the buffer descriptors, and the
* virtual address for us to work with.
*/
mem_addr = pinfo->mem_addr;
pinfo->rx_cur = pinfo->rx_bd_base;
for (bdp = pinfo->rx_bd_base, i = 0; i < pinfo->rx_nrfifos; i++, bdp++) {
bdp->cbd_bufaddr = virt_to_bus(mem_addr);
bdp->cbd_sc = BD_SC_EMPTY | BD_SC_INTRPT | (i < (pinfo->rx_nrfifos - 1) ? 0 : BD_SC_WRAP);
mem_addr += pinfo->rx_fifosize;
}
/* Set the physical address of the host memory
* buffers in the buffer descriptors, and the
* virtual address for us to work with.
*/
mem_addr = pinfo->mem_addr + L1_CACHE_ALIGN(pinfo->rx_nrfifos * pinfo->rx_fifosize);
pinfo->tx_cur = pinfo->tx_bd_base;
for (bdp = pinfo->tx_bd_base, i = 0; i < pinfo->tx_nrfifos; i++, bdp++) {
bdp->cbd_bufaddr = virt_to_bus(mem_addr);
bdp->cbd_sc = BD_SC_INTRPT | (i < (pinfo->tx_nrfifos - 1) ? 0 : BD_SC_WRAP);
mem_addr += pinfo->tx_fifosize;
}
/* Store address */
pinfo->smcup->smc_rbase = (u_char *)pinfo->rx_bd_base - DPRAM_BASE;
pinfo->smcup->smc_tbase = (u_char *)pinfo->tx_bd_base - DPRAM_BASE;
/* Set up the uart parameters in the
* parameter ram.
*/
cpm_set_smc_fcr(up);
/* Using idle charater time requires some additional tuning. */
up->smc_mrblr = pinfo->rx_fifosize;
up->smc_maxidl = pinfo->rx_fifosize;
up->smc_brkcr = 1;
cpm_line_cr_cmd(pinfo->port.line, CPM_CR_INIT_TRX);
/* Set UART mode, according to the parameters */
sp->smc_smcmr = smcr_mk_clen(bits) | cval | SMCMR_SM_UART;
/* Enable only rx interrupts clear all pending events. */
sp->smc_smcm = SMCM_RX;
sp->smc_smce = 0xff;
sp->smc_smcmr |= (SMCMR_REN | SMCMR_TEN);
}
static void cpm_uart_set_termios(struct uart_port *port,
struct termios *termios, struct termios *old)
{
int baud;
unsigned long flags;
u16 cval, scval;
int bits, sbits;
struct uart_cpm_port *pinfo = (struct uart_cpm_port *)port;
pr_debug("CPM uart[%d]:set_termios\n", port->line);
spin_lock_irqsave(&port->lock, flags);
/* Disable UART interrupts */
if (IS_SMC(pinfo))
pinfo->smcp->smc_smcm &= ~(SMCM_RX | SMCM_TX);
else
pinfo->sccp->scc_sccm &= ~(UART_SCCM_TX | UART_SCCM_RX);
spin_unlock_irqrestore(&port->lock, flags);
/*
* If a previous configuration exists wait 2+1 character times
* for tx to finish
*/
if (pinfo->baud != 0 && pinfo->bits != 0)
udelay((3 * 1000000 * pinfo->bits) / pinfo->baud);
spin_lock_irqsave(&port->lock, flags);
/* Send the CPM an initialize command. */
cpm_line_cr_cmd(pinfo->port.line, CPM_CR_STOP_TX);
/* Stop uart */
if (IS_SMC(pinfo))
pinfo->smcp->smc_smcmr &= ~(SMCMR_REN | SMCMR_TEN);
else
pinfo->sccp->scc_gsmrl &= ~(SCC_GSMRL_ENR | SCC_GSMRL_ENT);
/* Send the CPM an initialize command. */
cpm_line_cr_cmd(pinfo->port.line, CPM_CR_INIT_TRX);
/* init bds */
cpm_uart_initbd(pinfo);
spin_unlock_irqrestore(&port->lock, flags);
baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk / 16);
/* Character length programmed into the mode register is the
* sum of: 1 start bit, number of data bits, 0 or 1 parity bit,
* 1 or 2 stop bits, minus 1.
* The value 'bits' counts this for us.
*/
cval = 0;
scval = 0;
/* byte size */
switch (termios->c_cflag & CSIZE) {
case CS5:
bits = 5;
break;
case CS6:
bits = 6;
break;
case CS7:
bits = 7;
break;
case CS8:
bits = 8;
break;
/* Never happens, but GCC is too dumb to figure it out */
default:
bits = 8;
break;
}
sbits = bits - 5;
if (termios->c_cflag & CSTOPB) {
cval |= SMCMR_SL; /* Two stops */
scval |= SCU_PSMR_SL;
bits++;
}
if (termios->c_cflag & PARENB) {
cval |= SMCMR_PEN;
scval |= SCU_PSMR_PEN;
bits++;
if (!(termios->c_cflag & PARODD)) {
cval |= SMCMR_PM_EVEN;
scval |= (SCU_PSMR_REVP | SCU_PSMR_TEVP);
}
}
/*
* Set up parity check flag
*/
#define RELEVANT_IFLAG(iflag) (iflag & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK))
port->read_status_mask = (BD_SC_EMPTY | BD_SC_OV);
if (termios->c_iflag & INPCK)
port->read_status_mask |= BD_SC_FR | BD_SC_PR;
if ((termios->c_iflag & BRKINT) || (termios->c_iflag & PARMRK))
port->read_status_mask |= BD_SC_BR;
/*
* Characters to ignore
*/
port->ignore_status_mask = 0;
if (termios->c_iflag & IGNPAR)
port->ignore_status_mask |= BD_SC_PR | BD_SC_FR;
if (termios->c_iflag & IGNBRK) {
port->ignore_status_mask |= BD_SC_BR;
/*
* If we're ignore parity and break indicators, ignore
* overruns too. (For real raw support).
*/
if (termios->c_iflag & IGNPAR)
port->ignore_status_mask |= BD_SC_OV;
}
/*
* !!! ignore all characters if CREAD is not set
*/
if ((termios->c_cflag & CREAD) == 0)
port->read_status_mask &= ~BD_SC_EMPTY;
spin_lock_irqsave(&port->lock, flags);
cpm_set_brg(pinfo->brg - 1, baud);
/* Start bit has not been added (so don't, because we would just
* subtract it later), and we need to add one for the number of
* stops bits (there is always at least one).
*/
bits++;
/* re-init */
if (IS_SMC(pinfo))
cpm_uart_init_smc(pinfo, bits, cval);
else
cpm_uart_init_scc(pinfo, sbits, scval);
pinfo->baud = baud;
pinfo->bits = bits;
spin_unlock_irqrestore(&port->lock, flags);
}
static const char *cpm_uart_type(struct uart_port *port)
{
pr_debug("CPM uart[%d]:uart_type\n", port->line);
return port->type == PORT_CPM ? "CPM UART" : NULL;
}
/*
* verify the new serial_struct (for TIOCSSERIAL).
*/
static int cpm_uart_verify_port(struct uart_port *port,
struct serial_struct *ser)
{
int ret = 0;
pr_debug("CPM uart[%d]:verify_port\n", port->line);
if (ser->type != PORT_UNKNOWN && ser->type != PORT_CPM)
ret = -EINVAL;
if (ser->irq < 0 || ser->irq >= NR_IRQS)
ret = -EINVAL;
if (ser->baud_base < 9600)
ret = -EINVAL;
return ret;
}
/*
* Initialize port. This is called from early_console stuff
* so we have to be careful here !
*/
static int cpm_uart_request_port(struct uart_port *port)
{
struct uart_cpm_port *pinfo = (struct uart_cpm_port *)port;
int ret;
pr_debug("CPM uart[%d]:request port\n", port->line);
if (pinfo->flags & FLAG_CONSOLE)
return 0;
/*
* Setup any port IO, connect any baud rate generators,
* etc. This is expected to be handled by board
* dependant code
*/
if (pinfo->set_lineif)
pinfo->set_lineif(pinfo);
ret = cpm_uart_allocbuf(pinfo, 0);
if (ret)
return ret;
return 0;
}
static void cpm_uart_release_port(struct uart_port *port)
{
struct uart_cpm_port *pinfo = (struct uart_cpm_port *)port;
if (!(pinfo->flags & FLAG_CONSOLE))
cpm_uart_freebuf(pinfo);
}
/*
* Configure/autoconfigure the port.
*/
static void cpm_uart_config_port(struct uart_port *port, int flags)
{
pr_debug("CPM uart[%d]:config_port\n", port->line);
if (flags & UART_CONFIG_TYPE) {
port->type = PORT_CPM;
cpm_uart_request_port(port);
}
}
static struct uart_ops cpm_uart_pops = {
.tx_empty = cpm_uart_tx_empty,
.set_mctrl = cpm_uart_set_mctrl,
.get_mctrl = cpm_uart_get_mctrl,
.stop_tx = cpm_uart_stop_tx,
.start_tx = cpm_uart_start_tx,
.stop_rx = cpm_uart_stop_rx,
.enable_ms = cpm_uart_enable_ms,
.break_ctl = cpm_uart_break_ctl,
.startup = cpm_uart_startup,
.shutdown = cpm_uart_shutdown,
.set_termios = cpm_uart_set_termios,
.type = cpm_uart_type,
.release_port = cpm_uart_release_port,
.request_port = cpm_uart_request_port,
.config_port = cpm_uart_config_port,
.verify_port = cpm_uart_verify_port,
};
struct uart_cpm_port cpm_uart_ports[UART_NR] = {
[UART_SMC1] = {
.port = {
.irq = SMC1_IRQ,
.ops = &cpm_uart_pops,
.iotype = SERIAL_IO_MEM,
.line = UART_SMC1,
},
.flags = FLAG_SMC,
.tx_nrfifos = TX_NUM_FIFO,
.tx_fifosize = TX_BUF_SIZE,
.rx_nrfifos = RX_NUM_FIFO,
.rx_fifosize = RX_BUF_SIZE,
.set_lineif = smc1_lineif,
},
[UART_SMC2] = {
.port = {
.irq = SMC2_IRQ,
.ops = &cpm_uart_pops,
.iotype = SERIAL_IO_MEM,
},
.flags = FLAG_SMC,
.tx_nrfifos = TX_NUM_FIFO,
.tx_fifosize = TX_BUF_SIZE,
.rx_nrfifos = RX_NUM_FIFO,
.rx_fifosize = RX_BUF_SIZE,
.set_lineif = smc2_lineif,
},
[UART_SCC1] = {
.port = {
.irq = SCC1_IRQ,
.ops = &cpm_uart_pops,
.iotype = SERIAL_IO_MEM,
.line = UART_SCC1,
},
.tx_nrfifos = TX_NUM_FIFO,
.tx_fifosize = TX_BUF_SIZE,
.rx_nrfifos = RX_NUM_FIFO,
.rx_fifosize = RX_BUF_SIZE,
.set_lineif = scc1_lineif,
},
[UART_SCC2] = {
.port = {
.irq = SCC2_IRQ,
.ops = &cpm_uart_pops,
.iotype = SERIAL_IO_MEM,
},
.tx_nrfifos = TX_NUM_FIFO,
.tx_fifosize = TX_BUF_SIZE,
.rx_nrfifos = RX_NUM_FIFO,
.rx_fifosize = RX_BUF_SIZE,
.set_lineif = scc2_lineif,
},
[UART_SCC3] = {
.port = {
.irq = SCC3_IRQ,
.ops = &cpm_uart_pops,
.iotype = SERIAL_IO_MEM,
},
.tx_nrfifos = TX_NUM_FIFO,
.tx_fifosize = TX_BUF_SIZE,
.rx_nrfifos = RX_NUM_FIFO,
.rx_fifosize = RX_BUF_SIZE,
.set_lineif = scc3_lineif,
},
[UART_SCC4] = {
.port = {
.irq = SCC4_IRQ,
.ops = &cpm_uart_pops,
.iotype = SERIAL_IO_MEM,
.line = UART_SCC4,
},
.tx_nrfifos = TX_NUM_FIFO,
.tx_fifosize = TX_BUF_SIZE,
.rx_nrfifos = RX_NUM_FIFO,
.rx_fifosize = RX_BUF_SIZE,
.set_lineif = scc4_lineif,
},
};
#ifdef CONFIG_SERIAL_CPM_CONSOLE
/*
* Print a string to the serial port trying not to disturb
* any possible real use of the port...
*
* Note that this is called with interrupts already disabled
*/
static void cpm_uart_console_write(struct console *co, const char *s,
u_int count)
{
struct uart_cpm_port *pinfo =
&cpm_uart_ports[cpm_uart_port_map[co->index]];
unsigned int i;
volatile cbd_t *bdp, *bdbase;
volatile unsigned char *cp;
/* Get the address of the host memory buffer.
*/
bdp = pinfo->tx_cur;
bdbase = pinfo->tx_bd_base;
/*
* Now, do each character. This is not as bad as it looks
* since this is a holding FIFO and not a transmitting FIFO.
* We could add the complexity of filling the entire transmit
* buffer, but we would just wait longer between accesses......
*/
for (i = 0; i < count; i++, s++) {
/* Wait for transmitter fifo to empty.
* Ready indicates output is ready, and xmt is doing
* that, not that it is ready for us to send.
*/
while ((bdp->cbd_sc & BD_SC_READY) != 0)
;
/* Send the character out.
* If the buffer address is in the CPM DPRAM, don't
* convert it.
*/
if ((uint) (bdp->cbd_bufaddr) > (uint) CPM_ADDR)
cp = (unsigned char *) (bdp->cbd_bufaddr);
else
cp = bus_to_virt(bdp->cbd_bufaddr);
*cp = *s;
bdp->cbd_datlen = 1;
bdp->cbd_sc |= BD_SC_READY;
if (bdp->cbd_sc & BD_SC_WRAP)
bdp = bdbase;
else
bdp++;
/* if a LF, also do CR... */
if (*s == 10) {
while ((bdp->cbd_sc & BD_SC_READY) != 0)
;
if ((uint) (bdp->cbd_bufaddr) > (uint) CPM_ADDR)
cp = (unsigned char *) (bdp->cbd_bufaddr);
else
cp = bus_to_virt(bdp->cbd_bufaddr);
*cp = 13;
bdp->cbd_datlen = 1;
bdp->cbd_sc |= BD_SC_READY;
if (bdp->cbd_sc & BD_SC_WRAP)
bdp = bdbase;
else
bdp++;
}
}
/*
* Finally, Wait for transmitter & holding register to empty
* and restore the IER
*/
while ((bdp->cbd_sc & BD_SC_READY) != 0)
;
pinfo->tx_cur = (volatile cbd_t *) bdp;
}
/*
* Setup console. Be careful is called early !
*/
static int __init cpm_uart_console_setup(struct console *co, char *options)
{
struct uart_port *port;
struct uart_cpm_port *pinfo;
int baud = 38400;
int bits = 8;
int parity = 'n';
int flow = 'n';
int ret;
port =
(struct uart_port *)&cpm_uart_ports[cpm_uart_port_map[co->index]];
pinfo = (struct uart_cpm_port *)port;
pinfo->flags |= FLAG_CONSOLE;
if (options) {
uart_parse_options(options, &baud, &parity, &bits, &flow);
} else {
bd_t *bd = (bd_t *) __res;
if (bd->bi_baudrate)
baud = bd->bi_baudrate;
else
baud = 9600;
}
/*
* Setup any port IO, connect any baud rate generators,
* etc. This is expected to be handled by board
* dependant code
*/
if (pinfo->set_lineif)
pinfo->set_lineif(pinfo);
ret = cpm_uart_allocbuf(pinfo, 1);
if (ret)
return ret;
uart_set_options(port, co, baud, parity, bits, flow);
return 0;
}
extern struct uart_driver cpm_reg;
static struct console cpm_scc_uart_console = {
.name "ttyCPM",
.write cpm_uart_console_write,
.device uart_console_device,
.setup cpm_uart_console_setup,
.flags CON_PRINTBUFFER,
.index -1,
.data = &cpm_reg,
};
int __init cpm_uart_console_init(void)
{
int ret = cpm_uart_init_portdesc();
if (!ret)
register_console(&cpm_scc_uart_console);
return ret;
}
console_initcall(cpm_uart_console_init);
#define CPM_UART_CONSOLE &cpm_scc_uart_console
#else
#define CPM_UART_CONSOLE NULL
#endif
static struct uart_driver cpm_reg = {
.owner = THIS_MODULE,
.driver_name = "ttyCPM",
.dev_name = "ttyCPM",
.major = SERIAL_CPM_MAJOR,
.minor = SERIAL_CPM_MINOR,
.cons = CPM_UART_CONSOLE,
};
static int __init cpm_uart_init(void)
{
int ret, i;
printk(KERN_INFO "Serial: CPM driver $Revision: 0.01 $\n");
#ifndef CONFIG_SERIAL_CPM_CONSOLE
ret = cpm_uart_init_portdesc();
if (ret)
return ret;
#endif
cpm_reg.nr = cpm_uart_nr;
ret = uart_register_driver(&cpm_reg);
if (ret)
return ret;
for (i = 0; i < cpm_uart_nr; i++) {
int con = cpm_uart_port_map[i];
cpm_uart_ports[con].port.line = i;
cpm_uart_ports[con].port.flags = UPF_BOOT_AUTOCONF;
uart_add_one_port(&cpm_reg, &cpm_uart_ports[con].port);
}
return ret;
}
static void __exit cpm_uart_exit(void)
{
int i;
for (i = 0; i < cpm_uart_nr; i++) {
int con = cpm_uart_port_map[i];
uart_remove_one_port(&cpm_reg, &cpm_uart_ports[con].port);
}
uart_unregister_driver(&cpm_reg);
}
module_init(cpm_uart_init);
module_exit(cpm_uart_exit);
MODULE_AUTHOR("Kumar Gala/Antoniou Pantelis");
MODULE_DESCRIPTION("CPM SCC/SMC port driver $Revision: 0.01 $");
MODULE_LICENSE("GPL");
MODULE_ALIAS_CHARDEV(SERIAL_CPM_MAJOR, SERIAL_CPM_MINOR);
/*
* linux/drivers/serial/cpm_uart.c
*
* Driver for CPM (SCC/SMC) serial ports; CPM1 definitions
*
* Maintainer: Kumar Gala (kumar.gala@motorola.com) (CPM2)
* Pantelis Antoniou (panto@intracom.gr) (CPM1)
*
* Copyright (C) 2004 Motorola, Inc
* (C) 2004 Intracom, S.A.
*
* 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.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/tty.h>
#include <linux/ioport.h>
#include <linux/init.h>
#include <linux/serial.h>
#include <linux/console.h>
#include <linux/sysrq.h>
#include <linux/device.h>
#include <linux/bootmem.h>
#include <linux/dma-mapping.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <linux/serial_core.h>
#include <linux/kernel.h>
#include "cpm_uart.h"
/**************************************************************/
void cpm_line_cr_cmd(int line, int cmd)
{
ushort val;
volatile cpm8xx_t *cp = cpmp;
switch (line) {
case UART_SMC1:
val = mk_cr_cmd(CPM_CR_CH_SMC1, cmd) | CPM_CR_FLG;
break;
case UART_SMC2:
val = mk_cr_cmd(CPM_CR_CH_SMC2, cmd) | CPM_CR_FLG;
break;
case UART_SCC1:
val = mk_cr_cmd(CPM_CR_CH_SCC1, cmd) | CPM_CR_FLG;
break;
case UART_SCC2:
val = mk_cr_cmd(CPM_CR_CH_SCC2, cmd) | CPM_CR_FLG;
break;
case UART_SCC3:
val = mk_cr_cmd(CPM_CR_CH_SCC3, cmd) | CPM_CR_FLG;
break;
case UART_SCC4:
val = mk_cr_cmd(CPM_CR_CH_SCC4, cmd) | CPM_CR_FLG;
break;
default:
return;
}
cp->cp_cpcr = val;
while (cp->cp_cpcr & CPM_CR_FLG) ;
}
void smc1_lineif(struct uart_cpm_port *pinfo)
{
volatile cpm8xx_t *cp = cpmp;
cp->cp_pbpar |= 0x000000c0;
cp->cp_pbdir &= ~0x000000c0;
cp->cp_pbodr &= ~0x000000c0;
pinfo->brg = 1;
}
void smc2_lineif(struct uart_cpm_port *pinfo)
{
/* XXX SMC2: insert port configuration here */
pinfo->brg = 2;
}
void scc1_lineif(struct uart_cpm_port *pinfo)
{
/* XXX SCC1: insert port configuration here */
pinfo->brg = 1;
}
void scc2_lineif(struct uart_cpm_port *pinfo)
{
/* XXX SCC2: insert port configuration here */
pinfo->brg = 2;
}
void scc3_lineif(struct uart_cpm_port *pinfo)
{
/* XXX SCC3: insert port configuration here */
pinfo->brg = 3;
}
void scc4_lineif(struct uart_cpm_port *pinfo)
{
/* XXX SCC4: insert port configuration here */
pinfo->brg = 4;
}
/*
* Allocate DP-Ram and memory buffers. We need to allocate a transmit and
* receive buffer descriptors from dual port ram, and a character
* buffer area from host mem. If we are allocating for the console we need
* to do it from bootmem
*/
int cpm_uart_allocbuf(struct uart_cpm_port *pinfo, unsigned int is_con)
{
int dpmemsz, memsz;
uint dp_addr;
u8 *mem_addr;
dma_addr_t dma_addr;
pr_debug("CPM uart[%d]:allocbuf\n", pinfo->port.line);
dpmemsz = sizeof(cbd_t) * (pinfo->rx_nrfifos + pinfo->tx_nrfifos);
dp_addr = m8xx_cpm_dpalloc(dpmemsz);
if (dp_addr == CPM_DP_NOSPACE) {
printk(KERN_ERR
"cpm_uart_cpm1.c: could not allocate buffer descriptors\n");
return -ENOMEM;
}
memsz = L1_CACHE_ALIGN(pinfo->rx_nrfifos * pinfo->rx_fifosize) +
L1_CACHE_ALIGN(pinfo->tx_nrfifos * pinfo->tx_fifosize);
if (is_con) {
mem_addr = (u8 *) m8xx_cpm_hostalloc(memsz);
dma_addr = 0;
} else
mem_addr = dma_alloc_coherent(NULL, memsz, &dma_addr,
GFP_KERNEL);
/* We cant really from memory allocated via cpm2_dpalloc,
* fix this if in the future we can */
if (mem_addr == NULL) {
/* XXX cpm_dpalloc does not yet free */
printk(KERN_ERR
"cpm_uart_cpm1.c: could not allocate coherent memory\n");
return -ENOMEM;
}
pinfo->dp_addr = dp_addr;
pinfo->mem_addr = mem_addr;
pinfo->dma_addr = dma_addr;
pinfo->rx_buf = mem_addr;
pinfo->tx_buf = pinfo->rx_buf + L1_CACHE_ALIGN(pinfo->rx_nrfifos
* pinfo->rx_fifosize);
pinfo->rx_bd_base = (volatile cbd_t *)(DPRAM_BASE + dp_addr);
pinfo->tx_bd_base = pinfo->rx_bd_base + pinfo->rx_nrfifos;
return 0;
}
void cpm_uart_freebuf(struct uart_cpm_port *pinfo)
{
dma_free_coherent(NULL, L1_CACHE_ALIGN(pinfo->rx_nrfifos *
pinfo->rx_fifosize) +
L1_CACHE_ALIGN(pinfo->tx_nrfifos *
pinfo->tx_fifosize), pinfo->mem_addr,
pinfo->dma_addr);
/* XXX cannot free dpmem yet */
}
/* Setup any dynamic params in the uart desc */
int cpm_uart_init_portdesc(void)
{
pr_debug("CPM uart[-]:init portdesc\n");
cpm_uart_nr = 0;
#ifdef CONFIG_SERIAL_CPM_SMC1
cpm_uart_ports[UART_SMC1].smcp = &cpmp->cp_smc[0];
cpm_uart_ports[UART_SMC1].smcup =
(smc_uart_t *) & cpmp->cp_dparam[PROFF_SMC1];
cpm_uart_ports[UART_SMC1].port.mapbase =
(unsigned long)&cpmp->cp_smc[0];
cpm_uart_ports[UART_SMC1].smcp->smc_smcm |= (SMCM_RX | SMCM_TX);
cpm_uart_ports[UART_SMC1].smcp->smc_smcmr &= ~(SMCMR_REN | SMCMR_TEN);
cpm_uart_ports[UART_SMC1].port.uartclk = (((bd_t *) __res)->bi_intfreq);
cpm_uart_port_map[cpm_uart_nr++] = UART_SMC1;
#endif
#ifdef CONFIG_SERIAL_CPM_SMC2
cpm_uart_ports[UART_SMC2].smcp = &cpmp->cp_smc[1];
cpm_uart_ports[UART_SMC2].smcup =
(smc_uart_t *) & cpmp->cp_dparam[PROFF_SMC2];
cpm_uart_ports[UART_SMC2].port.mapbase =
(unsigned long)&cpmp->cp_smc[1];
cpm_uart_ports[UART_SMC2].smcp->smc_smcm |= (SMCM_RX | SMCM_TX);
cpm_uart_ports[UART_SMC2].smcp->smc_smcmr &= ~(SMCMR_REN | SMCMR_TEN);
cpm_uart_ports[UART_SMC2].port.uartclk = (((bd_t *) __res)->bi_intfreq);
cpm_uart_port_map[cpm_uart_nr++] = UART_SMC2;
#endif
#ifdef CONFIG_SERIAL_CPM_SCC1
cpm_uart_ports[UART_SCC1].sccp = &cpmp->cp_scc[0];
cpm_uart_ports[UART_SCC1].sccup =
(scc_uart_t *) & cpmp->cp_dparam[PROFF_SCC1];
cpm_uart_ports[UART_SCC1].port.mapbase =
(unsigned long)&cpmp->cp_scc[0];
cpm_uart_ports[UART_SCC1].sccp->scc_sccm &=
~(UART_SCCM_TX | UART_SCCM_RX);
cpm_uart_ports[UART_SCC1].sccp->scc_gsmrl &=
~(SCC_GSMRL_ENR | SCC_GSMRL_ENT);
cpm_uart_ports[UART_SCC1].port.uartclk = (((bd_t *) __res)->bi_intfreq);
cpm_uart_port_map[cpm_uart_nr++] = UART_SCC1;
#endif
#ifdef CONFIG_SERIAL_CPM_SCC2
cpm_uart_ports[UART_SCC2].sccp = &cpmp->cp_scc[1];
cpm_uart_ports[UART_SCC2].sccup =
(scc_uart_t *) & cpmp->cp_dparam[PROFF_SCC2];
cpm_uart_ports[UART_SCC2].port.mapbase =
(unsigned long)&cpmp->cp_scc[1];
cpm_uart_ports[UART_SCC2].sccp->scc_sccm &=
~(UART_SCCM_TX | UART_SCCM_RX);
cpm_uart_ports[UART_SCC2].sccp->scc_gsmrl &=
~(SCC_GSMRL_ENR | SCC_GSMRL_ENT);
cpm_uart_ports[UART_SCC2].port.uartclk = (((bd_t *) __res)->bi_intfreq);
cpm_uart_port_map[cpm_uart_nr++] = UART_SCC2;
#endif
#ifdef CONFIG_SERIAL_CPM_SCC3
cpm_uart_ports[UART_SCC3].sccp = &cpmp->cp_scc[2];
cpm_uart_ports[UART_SCC3].sccup =
(scc_uart_t *) & cpmp->cp_dparam[PROFF_SCC3];
cpm_uart_ports[UART_SCC3].port.mapbase =
(unsigned long)&cpmp->cp_scc[2];
cpm_uart_ports[UART_SCC3].sccp->scc_sccm &=
~(UART_SCCM_TX | UART_SCCM_RX);
cpm_uart_ports[UART_SCC3].sccp->scc_gsmrl &=
~(SCC_GSMRL_ENR | SCC_GSMRL_ENT);
cpm_uart_ports[UART_SCC3].port.uartclk = (((bd_t *) __res)->bi_intfreq);
cpm_uart_port_map[cpm_uart_nr++] = UART_SCC3;
#endif
#ifdef CONFIG_SERIAL_CPM_SCC4
cpm_uart_ports[UART_SCC4].sccp = &cpmp->cp_scc[3];
cpm_uart_ports[UART_SCC4].sccup =
(scc_uart_t *) & cpmp->cp_dparam[PROFF_SCC4];
cpm_uart_ports[UART_SCC4].port.mapbase =
(unsigned long)&cpmp->cp_scc[3];
cpm_uart_ports[UART_SCC4].sccp->scc_sccm &=
~(UART_SCCM_TX | UART_SCCM_RX);
cpm_uart_ports[UART_SCC4].sccp->scc_gsmrl &=
~(SCC_GSMRL_ENR | SCC_GSMRL_ENT);
cpm_uart_ports[UART_SCC4].port.uartclk = (((bd_t *) __res)->bi_intfreq);
cpm_uart_port_map[cpm_uart_nr++] = UART_SCC4;
#endif
return 0;
}
/*
* linux/drivers/serial/cpm_uart_cpm1.h
*
* Driver for CPM (SCC/SMC) serial ports
*
* definitions for cpm1
*
*/
#ifndef CPM_UART_CPM1_H
#define CPM_UART_CPM1_H
#include <asm/commproc.h>
/* defines for IRQs */
#define SMC1_IRQ (CPM_IRQ_OFFSET + CPMVEC_SMC1)
#define SMC2_IRQ (CPM_IRQ_OFFSET + CPMVEC_SMC2)
#define SCC1_IRQ (CPM_IRQ_OFFSET + CPMVEC_SCC1)
#define SCC2_IRQ (CPM_IRQ_OFFSET + CPMVEC_SCC2)
#define SCC3_IRQ (CPM_IRQ_OFFSET + CPMVEC_SCC3)
#define SCC4_IRQ (CPM_IRQ_OFFSET + CPMVEC_SCC4)
/* the CPM address */
#define CPM_ADDR IMAP_ADDR
static inline void cpm_set_brg(int brg, int baud)
{
m8xx_cpm_setbrg(brg, baud);
}
static inline void cpm_set_scc_fcr(volatile scc_uart_t * sup)
{
sup->scc_genscc.scc_rfcr = SMC_EB;
sup->scc_genscc.scc_tfcr = SMC_EB;
}
static inline void cpm_set_smc_fcr(volatile smc_uart_t * up)
{
up->smc_rfcr = SMC_EB;
up->smc_tfcr = SMC_EB;
}
#define DPRAM_BASE ((unsigned char *)&cpmp->cp_dpmem[0])
#endif
/*
* linux/drivers/serial/cpm_uart_cpm2.c
*
* Driver for CPM (SCC/SMC) serial ports; CPM2 definitions
*
* Maintainer: Kumar Gala (kumar.gala@motorola.com) (CPM2)
* Pantelis Antoniou (panto@intracom.gr) (CPM1)
*
* Copyright (C) 2004 Motorola, Inc
* (C) 2004 Intracom, S.A.
*
* 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.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/tty.h>
#include <linux/ioport.h>
#include <linux/init.h>
#include <linux/serial.h>
#include <linux/console.h>
#include <linux/sysrq.h>
#include <linux/device.h>
#include <linux/bootmem.h>
#include <linux/dma-mapping.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <linux/serial_core.h>
#include <linux/kernel.h>
#include "cpm_uart.h"
/**************************************************************/
void cpm_line_cr_cmd(int line, int cmd)
{
volatile cpm_cpm2_t *cp = cpmp;
ulong val;
switch (line) {
case UART_SMC1:
val = mk_cr_cmd(CPM_CR_SMC1_PAGE, CPM_CR_SMC1_SBLOCK, 0,
cmd) | CPM_CR_FLG;
break;
case UART_SMC2:
val = mk_cr_cmd(CPM_CR_SMC2_PAGE, CPM_CR_SMC2_SBLOCK, 0,
cmd) | CPM_CR_FLG;
break;
case UART_SCC1:
val = mk_cr_cmd(CPM_CR_SCC1_PAGE, CPM_CR_SCC1_SBLOCK, 0,
cmd) | CPM_CR_FLG;
break;
case UART_SCC2:
val = mk_cr_cmd(CPM_CR_SCC2_PAGE, CPM_CR_SCC2_SBLOCK, 0,
cmd) | CPM_CR_FLG;
break;
case UART_SCC3:
val = mk_cr_cmd(CPM_CR_SCC3_PAGE, CPM_CR_SCC3_SBLOCK, 0,
cmd) | CPM_CR_FLG;
break;
case UART_SCC4:
val = mk_cr_cmd(CPM_CR_SCC4_PAGE, CPM_CR_SCC4_SBLOCK, 0,
cmd) | CPM_CR_FLG;
break;
default:
return;
}
cp->cp_cpcr = val;
while (cp->cp_cpcr & CPM_CR_FLG) ;
}
void smc1_lineif(struct uart_cpm_port *pinfo)
{
volatile iop_cpm2_t *io = &cpm2_immr->im_ioport;
/* SMC1 is only on port D */
io->iop_ppard |= 0x00c00000;
io->iop_pdird |= 0x00400000;
io->iop_pdird &= ~0x00800000;
io->iop_psord &= ~0x00c00000;
/* Wire BRG1 to SMC1 */
cpm2_immr->im_cpmux.cmx_smr &= 0x0f;
pinfo->brg = 1;
}
void smc2_lineif(struct uart_cpm_port *pinfo)
{
volatile iop_cpm2_t *io = &cpm2_immr->im_ioport;
/* SMC2 is only on port A */
io->iop_ppara |= 0x00c00000;
io->iop_pdira |= 0x00400000;
io->iop_pdira &= ~0x00800000;
io->iop_psora &= ~0x00c00000;
/* Wire BRG2 to SMC2 */
cpm2_immr->im_cpmux.cmx_smr &= 0xf0;
pinfo->brg = 2;
}
void scc1_lineif(struct uart_cpm_port *pinfo)
{
volatile iop_cpm2_t *io = &cpm2_immr->im_ioport;
/* Use Port D for SCC1 instead of other functions. */
io->iop_ppard |= 0x00000003;
io->iop_psord &= ~0x00000001; /* Rx */
io->iop_psord |= 0x00000002; /* Tx */
io->iop_pdird &= ~0x00000001; /* Rx */
io->iop_pdird |= 0x00000002; /* Tx */
/* Wire BRG1 to SCC1 */
cpm2_immr->im_cpmux.cmx_scr &= ~0x00ffffff;
cpm2_immr->im_cpmux.cmx_scr |= 0x00000000;
pinfo->brg = 1;
}
void scc2_lineif(struct uart_cpm_port *pinfo)
{
volatile iop_cpm2_t *io = &cpm2_immr->im_ioport;
io->iop_pparb |= 0x008b0000;
io->iop_pdirb |= 0x00880000;
io->iop_psorb |= 0x00880000;
io->iop_pdirb &= ~0x00030000;
io->iop_psorb &= ~0x00030000;
cpm2_immr->im_cpmux.cmx_scr &= ~0xff00ffff;
cpm2_immr->im_cpmux.cmx_scr |= 0x00090000;
pinfo->brg = 2;
}
void scc3_lineif(struct uart_cpm_port *pinfo)
{
volatile iop_cpm2_t *io = &cpm2_immr->im_ioport;
io->iop_pparb |= 0x008b0000;
io->iop_pdirb |= 0x00880000;
io->iop_psorb |= 0x00880000;
io->iop_pdirb &= ~0x00030000;
io->iop_psorb &= ~0x00030000;
cpm2_immr->im_cpmux.cmx_scr &= ~0xffff00ff;
cpm2_immr->im_cpmux.cmx_scr |= 0x00001200;
pinfo->brg = 3;
}
void scc4_lineif(struct uart_cpm_port *pinfo)
{
volatile iop_cpm2_t *io = &cpm2_immr->im_ioport;
io->iop_ppard |= 0x00000600;
io->iop_psord &= ~0x00000600; /* Tx/Rx */
io->iop_pdird &= ~0x00000200; /* Rx */
io->iop_pdird |= 0x00000400; /* Tx */
cpm2_immr->im_cpmux.cmx_scr &= ~0xffffff00;
cpm2_immr->im_cpmux.cmx_scr |= 0x0000001b;
pinfo->brg = 4;
}
/*
* Allocate DP-Ram and memory buffers. We need to allocate a transmit and
* receive buffer descriptors from dual port ram, and a character
* buffer area from host mem. If we are allocating for the console we need
* to do it from bootmem
*/
int cpm_uart_allocbuf(struct uart_cpm_port *pinfo, unsigned int is_con)
{
int dpmemsz, memsz;
uint dp_addr;
u8 *mem_addr;
dma_addr_t dma_addr = 0;
pr_debug("CPM uart[%d]:allocbuf\n", pinfo->port.line);
dpmemsz = sizeof(cbd_t) * (pinfo->rx_nrfifos + pinfo->tx_nrfifos);
dp_addr = cpm2_dpalloc(dpmemsz, 8);
if (dp_addr == CPM_DP_NOSPACE) {
printk(KERN_ERR
"cpm_uart_cpm1.c: could not allocate buffer descriptors\n");
return -ENOMEM;
}
memsz = L1_CACHE_ALIGN(pinfo->rx_nrfifos * pinfo->rx_fifosize) +
L1_CACHE_ALIGN(pinfo->tx_nrfifos * pinfo->tx_fifosize);
if (is_con)
mem_addr = alloc_bootmem(memsz);
else
mem_addr = dma_alloc_coherent(NULL, memsz, &dma_addr,
GFP_KERNEL);
/* We cant really from memory allocated via cpm2_dpalloc,
* fix this if in the future we can */
if (mem_addr == NULL) {
/* XXX cpm_dpalloc does not yet free */
printk(KERN_ERR
"cpm_uart_cpm1.c: could not allocate coherent memory\n");
return -ENOMEM;
}
pinfo->dp_addr = dp_addr;
pinfo->mem_addr = mem_addr;
pinfo->dma_addr = dma_addr;
pinfo->rx_buf = mem_addr;
pinfo->tx_buf = pinfo->rx_buf + L1_CACHE_ALIGN(pinfo->rx_nrfifos
* pinfo->rx_fifosize);
pinfo->rx_bd_base = (volatile cbd_t *)(DPRAM_BASE + dp_addr);
pinfo->tx_bd_base = pinfo->rx_bd_base + pinfo->rx_nrfifos;
return 0;
}
void cpm_uart_freebuf(struct uart_cpm_port *pinfo)
{
dma_free_coherent(NULL, L1_CACHE_ALIGN(pinfo->rx_nrfifos *
pinfo->rx_fifosize) +
L1_CACHE_ALIGN(pinfo->tx_nrfifos *
pinfo->tx_fifosize), pinfo->mem_addr,
pinfo->dma_addr);
/* XXX cannot free dpmem yet */
}
/* Setup any dynamic params in the uart desc */
int cpm_uart_init_portdesc(void)
{
pr_debug("CPM uart[-]:init portdesc\n");
cpm_uart_nr = 0;
#ifdef CONFIG_SERIAL_CPM_SMC1
cpm_uart_ports[UART_SMC1].smcp = (smc_t *) & cpm2_immr->im_smc[0];
cpm_uart_ports[UART_SMC1].smcup =
(smc_uart_t *) & cpm2_immr->im_dprambase[PROFF_SMC1];
cpm_uart_ports[UART_SMC1].port.mapbase =
(unsigned long)&cpm2_immr->im_smc[0];
cpm_uart_ports[UART_SMC1].smcp->smc_smcm |= (SMCM_RX | SMCM_TX);
cpm_uart_ports[UART_SMC1].smcp->smc_smcmr &= ~(SMCMR_REN | SMCMR_TEN);
cpm_uart_ports[UART_SMC1].port.uartclk = (((bd_t *) __res)->bi_intfreq);
cpm_uart_port_map[cpm_uart_nr++] = UART_SMC1;
#endif
#ifdef CONFIG_SERIAL_CPM_SMC2
cpm_uart_ports[UART_SMC2].smcp = (smc_t *) & cpm2_immr->im_smc[1];
cpm_uart_ports[UART_SMC2].smcup =
(smc_uart_t *) & cpm2_immr->im_dprambase[PROFF_SMC2];
cpm_uart_ports[UART_SMC2].port.mapbase =
(unsigned long)&cpm2_immr->im_smc[1];
cpm_uart_ports[UART_SMC2].smcp->smc_smcm |= (SMCM_RX | SMCM_TX);
cpm_uart_ports[UART_SMC2].smcp->smc_smcmr &= ~(SMCMR_REN | SMCMR_TEN);
cpm_uart_ports[UART_SMC2].port.uartclk = (((bd_t *) __res)->bi_intfreq);
cpm_uart_port_map[cpm_uart_nr++] = UART_SMC2;
#endif
#ifdef CONFIG_SERIAL_CPM_SCC1
cpm_uart_ports[UART_SCC1].sccp = (scc_t *) & cpm2_immr->im_scc[0];
cpm_uart_ports[UART_SCC1].sccup =
(scc_uart_t *) & cpm2_immr->im_dprambase[PROFF_SCC1];
cpm_uart_ports[UART_SCC1].port.mapbase =
(unsigned long)&cpm2_immr->im_scc[0];
cpm_uart_ports[UART_SCC1].sccp->scc_sccm &=
~(UART_SCCM_TX | UART_SCCM_RX);
cpm_uart_ports[UART_SCC1].sccp->scc_gsmrl &=
~(SCC_GSMRL_ENR | SCC_GSMRL_ENT);
cpm_uart_ports[UART_SCC1].port.uartclk = (((bd_t *) __res)->bi_intfreq);
cpm_uart_port_map[cpm_uart_nr++] = UART_SCC1;
#endif
#ifdef CONFIG_SERIAL_CPM_SCC2
cpm_uart_ports[UART_SCC2].sccp = (scc_t *) & cpm2_immr->im_scc[1];
cpm_uart_ports[UART_SCC2].sccup =
(scc_uart_t *) & cpm2_immr->im_dprambase[PROFF_SCC2];
cpm_uart_ports[UART_SCC2].port.mapbase =
(unsigned long)&cpm2_immr->im_scc[1];
cpm_uart_ports[UART_SCC2].sccp->scc_sccm &=
~(UART_SCCM_TX | UART_SCCM_RX);
cpm_uart_ports[UART_SCC2].sccp->scc_gsmrl &=
~(SCC_GSMRL_ENR | SCC_GSMRL_ENT);
cpm_uart_ports[UART_SCC2].port.uartclk = (((bd_t *) __res)->bi_intfreq);
cpm_uart_port_map[cpm_uart_nr++] = UART_SCC2;
#endif
#ifdef CONFIG_SERIAL_CPM_SCC3
cpm_uart_ports[UART_SCC3].sccp = (scc_t *) & cpm2_immr->im_scc[2];
cpm_uart_ports[UART_SCC3].sccup =
(scc_uart_t *) & cpm2_immr->im_dprambase[PROFF_SCC3];
cpm_uart_ports[UART_SCC3].port.mapbase =
(unsigned long)&cpm2_immr->im_scc[2];
cpm_uart_ports[UART_SCC3].sccp->scc_sccm &=
~(UART_SCCM_TX | UART_SCCM_RX);
cpm_uart_ports[UART_SCC3].sccp->scc_gsmrl &=
~(SCC_GSMRL_ENR | SCC_GSMRL_ENT);
cpm_uart_ports[UART_SCC3].port.uartclk = (((bd_t *) __res)->bi_intfreq);
cpm_uart_port_map[cpm_uart_nr++] = UART_SCC3;
#endif
#ifdef CONFIG_SERIAL_CPM_SCC4
cpm_uart_ports[UART_SCC4].sccp = (scc_t *) & cpm2_immr->im_scc[3];
cpm_uart_ports[UART_SCC4].sccup =
(scc_uart_t *) & cpm2_immr->im_dprambase[PROFF_SCC4];
cpm_uart_ports[UART_SCC4].port.mapbase =
(unsigned long)&cpm2_immr->im_scc[3];
cpm_uart_ports[UART_SCC4].sccp->scc_sccm &=
~(UART_SCCM_TX | UART_SCCM_RX);
cpm_uart_ports[UART_SCC4].sccp->scc_gsmrl &=
~(SCC_GSMRL_ENR | SCC_GSMRL_ENT);
cpm_uart_ports[UART_SCC4].port.uartclk = (((bd_t *) __res)->bi_intfreq);
cpm_uart_port_map[cpm_uart_nr++] = UART_SCC4;
#endif
return 0;
}
/*
* linux/drivers/serial/cpm_uart_cpm2.h
*
* Driver for CPM (SCC/SMC) serial ports
*
* definitions for cpm2
*
*/
#ifndef CPM_UART_CPM2_H
#define CPM_UART_CPM2_H
#include <asm/cpm2.h>
/* defines for IRQs */
#define SMC1_IRQ SIU_INT_SMC1
#define SMC2_IRQ SIU_INT_SMC2
#define SCC1_IRQ SIU_INT_SCC1
#define SCC2_IRQ SIU_INT_SCC2
#define SCC3_IRQ SIU_INT_SCC3
#define SCC4_IRQ SIU_INT_SCC4
/* the CPM address */
#define CPM_ADDR CPM_MAP_ADDR
static inline void cpm_set_brg(int brg, int baud)
{
cpm2_setbrg(brg, baud);
}
static inline void cpm_set_scc_fcr(volatile scc_uart_t * sup)
{
sup->scc_genscc.scc_rfcr = CPMFCR_GBL | CPMFCR_EB;
sup->scc_genscc.scc_tfcr = CPMFCR_GBL | CPMFCR_EB;
}
static inline void cpm_set_smc_fcr(volatile smc_uart_t * up)
{
up->smc_rfcr = CPMFCR_GBL | CPMFCR_EB;
up->smc_tfcr = CPMFCR_GBL | CPMFCR_EB;
}
#define DPRAM_BASE ((unsigned char *)&cpm2_immr->im_dprambase[0])
#endif
......@@ -88,6 +88,9 @@
/* SGI IP22 aka Indy / Challenge S / Indigo 2 */
#define PORT_IP22ZILOG 56
/* PPC CPM type number */
#define PORT_CPM 57
#ifdef __KERNEL__
#include <linux/config.h>
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment