diff options
Diffstat (limited to 'drivers/serial/cpm_uart')
-rw-r--r-- | drivers/serial/cpm_uart/Makefile | 11 | ||||
-rw-r--r-- | drivers/serial/cpm_uart/cpm_uart.h | 89 | ||||
-rw-r--r-- | drivers/serial/cpm_uart/cpm_uart_core.c | 1177 | ||||
-rw-r--r-- | drivers/serial/cpm_uart/cpm_uart_cpm1.c | 290 | ||||
-rw-r--r-- | drivers/serial/cpm_uart/cpm_uart_cpm1.h | 45 | ||||
-rw-r--r-- | drivers/serial/cpm_uart/cpm_uart_cpm2.c | 328 | ||||
-rw-r--r-- | drivers/serial/cpm_uart/cpm_uart_cpm2.h | 45 |
7 files changed, 1985 insertions, 0 deletions
diff --git a/drivers/serial/cpm_uart/Makefile b/drivers/serial/cpm_uart/Makefile new file mode 100644 index 00000000000..e072724ea75 --- /dev/null +++ b/drivers/serial/cpm_uart/Makefile @@ -0,0 +1,11 @@ +# +# 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) diff --git a/drivers/serial/cpm_uart/cpm_uart.h b/drivers/serial/cpm_uart/cpm_uart.h new file mode 100644 index 00000000000..5f6187baad8 --- /dev/null +++ b/drivers/serial/cpm_uart/cpm_uart.h @@ -0,0 +1,89 @@ +/* + * linux/drivers/serial/cpm_uart.h + * + * Driver for CPM (SCC/SMC) serial ports + * + * Copyright (C) 2004 Freescale Semiconductor, 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 + +#define SERIAL_CPM_MAJOR 204 +#define SERIAL_CPM_MINOR 46 + +#define IS_SMC(pinfo) (pinfo->flags & FLAG_SMC) +#define IS_DISCARDING(pinfo) (pinfo->flags & FLAG_DISCARDING) +#define FLAG_DISCARDING 0x00000004 /* when set, don't discard */ +#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; + 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; + /* helpers */ + int baud; + int bits; + /* Keep track of 'odd' SMC2 wirings */ + int is_portb; +}; + +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 */ +void cpm_line_cr_cmd(int line, int cmd); +int cpm_uart_init_portdesc(void); +int cpm_uart_allocbuf(struct uart_cpm_port *pinfo, unsigned int is_con); +void cpm_uart_freebuf(struct uart_cpm_port *pinfo); + +void smc1_lineif(struct uart_cpm_port *pinfo); +void smc2_lineif(struct uart_cpm_port *pinfo); +void scc1_lineif(struct uart_cpm_port *pinfo); +void scc2_lineif(struct uart_cpm_port *pinfo); +void scc3_lineif(struct uart_cpm_port *pinfo); +void scc4_lineif(struct uart_cpm_port *pinfo); + +#endif /* CPM_UART_H */ diff --git a/drivers/serial/cpm_uart/cpm_uart_core.c b/drivers/serial/cpm_uart/cpm_uart_core.c new file mode 100644 index 00000000000..29db677d428 --- /dev/null +++ b/drivers/serial/cpm_uart/cpm_uart_core.c @@ -0,0 +1,1177 @@ +/* + * 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@freescale.com) (CPM2) + * Pantelis Antoniou (panto@intracom.gr) (CPM1) + * + * Copyright (C) 2004 Freescale Semiconductor, 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 <asm/delay.h> + +#if defined(CONFIG_SERIAL_CPM_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) +#define SUPPORT_SYSRQ +#endif + +#include <linux/serial_core.h> +#include <linux/kernel.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; + +/**************************************************************/ + +static int cpm_uart_tx_pump(struct uart_port *port); +static void cpm_uart_init_smc(struct uart_cpm_port *pinfo); +static void cpm_uart_init_scc(struct uart_cpm_port *pinfo); +static void cpm_uart_initbd(struct uart_cpm_port *pinfo); + +/**************************************************************/ + +/* + * 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; +} + +/* + * 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; + int line = pinfo - cpm_uart_ports; + + pr_debug("CPM uart[%d]:break ctrl, break_state: %d\n", port->line, + break_state); + + if (break_state) + cpm_line_cr_cmd(line, CPM_CR_STOP_TX); + else + cpm_line_cr_cmd(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) +{ + int retval; + struct uart_cpm_port *pinfo = (struct uart_cpm_port *)port; + + 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; +} + +/* + * Shutdown the uart + */ +static void cpm_uart_shutdown(struct uart_port *port) +{ + struct uart_cpm_port *pinfo = (struct uart_cpm_port *)port; + int line = pinfo - cpm_uart_ports; + + 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(line, CPM_CR_STOP_TX); + cpm_uart_initbd(pinfo); + } +} + +static void cpm_uart_set_termios(struct uart_port *port, + struct termios *termios, struct termios *old) +{ + int baud; + unsigned long flags; + u16 cval, scval, prev_mode; + int bits, sbits; + 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]:set_termios\n", port->line); + + 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); + + /* 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++; + if (IS_SMC(pinfo)) { + /* Set the mode register. We want to keep a copy of the + * enables, because we want to put them back if they were + * present. + */ + prev_mode = smcp->smc_smcmr; + smcp->smc_smcmr = smcr_mk_clen(bits) | cval | SMCMR_SM_UART; + smcp->smc_smcmr |= (prev_mode & (SMCMR_REN | SMCMR_TEN)); + } else { + sccp->scc_psmr = (sbits << 12) | scval; + } + + cpm_set_brg(pinfo->brg - 1, baud); + 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; +} + +/* + * 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; +} + +/* + * init buffer descriptors + */ +static void cpm_uart_initbd(struct uart_cpm_port *pinfo) +{ + int i; + u8 *mem_addr; + volatile cbd_t *bdp; + + pr_debug("CPM uart[%d]:initbd\n", pinfo->port.line); + + /* 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; + bdp = pinfo->rx_cur = pinfo->rx_bd_base; + for (i = 0; i < (pinfo->rx_nrfifos - 1); i++, bdp++) { + bdp->cbd_bufaddr = virt_to_bus(mem_addr); + bdp->cbd_sc = BD_SC_EMPTY | BD_SC_INTRPT; + mem_addr += pinfo->rx_fifosize; + } + + bdp->cbd_bufaddr = virt_to_bus(mem_addr); + bdp->cbd_sc = BD_SC_WRAP | BD_SC_EMPTY | BD_SC_INTRPT; + + /* 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); + bdp = pinfo->tx_cur = pinfo->tx_bd_base; + for (i = 0; i < (pinfo->tx_nrfifos - 1); i++, bdp++) { + bdp->cbd_bufaddr = virt_to_bus(mem_addr); + bdp->cbd_sc = BD_SC_INTRPT; + mem_addr += pinfo->tx_fifosize; + } + + bdp->cbd_bufaddr = virt_to_bus(mem_addr); + bdp->cbd_sc = BD_SC_WRAP | BD_SC_INTRPT; +} + +static void cpm_uart_init_scc(struct uart_cpm_port *pinfo) +{ + int line = pinfo - cpm_uart_ports; + volatile scc_t *scp; + volatile scc_uart_t *sup; + + pr_debug("CPM uart[%d]:init_scc\n", pinfo->port.line); + + scp = pinfo->sccp; + sup = pinfo->sccup; + + /* 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(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 = 0; + scp->scc_scce = 0xffff; + scp->scc_dsr = 0x7e7e; + scp->scc_psmr = 0x3000; + + scp->scc_gsmrl |= (SCC_GSMRL_ENR | SCC_GSMRL_ENT); +} + +static void cpm_uart_init_smc(struct uart_cpm_port *pinfo) +{ + int line = pinfo - cpm_uart_ports; + volatile smc_t *sp; + volatile smc_uart_t *up; + + pr_debug("CPM uart[%d]:init_smc\n", pinfo->port.line); + + sp = pinfo->smcp; + up = pinfo->smcup; + + /* 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; + +/* + * In case SMC1 is being relocated... + */ +#if defined (CONFIG_I2C_SPI_SMC1_UCODE_PATCH) + up->smc_rbptr = pinfo->smcup->smc_rbase; + up->smc_tbptr = pinfo->smcup->smc_tbase; + up->smc_rstate = 0; + up->smc_tstate = 0; + up->smc_brkcr = 1; /* number of break chars */ + up->smc_brkec = 0; +#endif + + /* 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(line, CPM_CR_INIT_TRX); + + /* Set UART mode, 8 bit, no parity, one stop. + * Enable receive and transmit. + */ + sp->smc_smcmr = smcr_mk_clen(9) | SMCMR_SM_UART; + + /* Enable only rx interrupts clear all pending events. */ + sp->smc_smcm = 0; + sp->smc_smce = 0xff; + + sp->smc_smcmr |= (SMCMR_REN | SMCMR_TEN); +} + +/* + * 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); + + if (IS_SMC(pinfo)) { + pinfo->smcp->smc_smcm &= ~(SMCM_RX | SMCM_TX); + pinfo->smcp->smc_smcmr &= ~(SMCMR_REN | SMCMR_TEN); + } else { + pinfo->sccp->scc_sccm &= ~(UART_SCCM_TX | UART_SCCM_RX); + pinfo->sccp->scc_gsmrl &= ~(SCC_GSMRL_ENR | SCC_GSMRL_ENT); + } + + ret = cpm_uart_allocbuf(pinfo, 0); + + if (ret) + return ret; + + cpm_uart_initbd(pinfo); + + 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, + .lock = SPIN_LOCK_UNLOCKED, + }, + .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, + .lock = SPIN_LOCK_UNLOCKED, + }, + .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, +#ifdef CONFIG_SERIAL_CPM_ALT_SMC2 + .is_portb = 1, +#endif + }, + [UART_SCC1] = { + .port = { + .irq = SCC1_IRQ, + .ops = &cpm_uart_pops, + .iotype = SERIAL_IO_MEM, + .lock = SPIN_LOCK_UNLOCKED, + }, + .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, + .lock = SPIN_LOCK_UNLOCKED, + }, + .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, + .lock = SPIN_LOCK_UNLOCKED, + }, + .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, + .lock = SPIN_LOCK_UNLOCKED, + }, + .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); + + if (IS_SMC(pinfo)) { + pinfo->smcp->smc_smcm &= ~(SMCM_RX | SMCM_TX); + pinfo->smcp->smc_smcmr &= ~(SMCMR_REN | SMCMR_TEN); + } else { + pinfo->sccp->scc_sccm &= ~(UART_SCCM_TX | UART_SCCM_RX); + pinfo->sccp->scc_gsmrl &= ~(SCC_GSMRL_ENR | SCC_GSMRL_ENT); + } + + ret = cpm_uart_allocbuf(pinfo, 1); + + if (ret) + return ret; + + cpm_uart_initbd(pinfo); + + if (IS_SMC(pinfo)) + cpm_uart_init_smc(pinfo); + else + cpm_uart_init_scc(pinfo); + + 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); diff --git a/drivers/serial/cpm_uart/cpm_uart_cpm1.c b/drivers/serial/cpm_uart/cpm_uart_cpm1.c new file mode 100644 index 00000000000..de26cf7b003 --- /dev/null +++ b/drivers/serial/cpm_uart/cpm_uart_cpm1.c @@ -0,0 +1,290 @@ +/* + * linux/drivers/serial/cpm_uart.c + * + * Driver for CPM (SCC/SMC) serial ports; CPM1 definitions + * + * Maintainer: Kumar Gala (kumar.gala@freescale.com) (CPM2) + * Pantelis Antoniou (panto@intracom.gr) (CPM1) + * + * Copyright (C) 2004 Freescale Semiconductor, 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; + unsigned int iobits = 0x000000c0; + + if (!pinfo->is_portb) { + cp->cp_pbpar |= iobits; + cp->cp_pbdir &= ~iobits; + cp->cp_pbodr &= ~iobits; + } else { + ((immap_t *)IMAP_ADDR)->im_ioport.iop_papar |= iobits; + ((immap_t *)IMAP_ADDR)->im_ioport.iop_padir &= ~iobits; + ((immap_t *)IMAP_ADDR)->im_ioport.iop_paodr &= ~iobits; + } + + 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; + u8 *dp_mem; + uint dp_offset; + 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_offset = cpm_dpalloc(dpmemsz, 8); + if (IS_DPERR(dp_offset)) { + printk(KERN_ERR + "cpm_uart_cpm1.c: could not allocate buffer descriptors\n"); + return -ENOMEM; + } + dp_mem = cpm_dpram_addr(dp_offset); + + 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); + + if (mem_addr == NULL) { + cpm_dpfree(dp_offset); + printk(KERN_ERR + "cpm_uart_cpm1.c: could not allocate coherent memory\n"); + return -ENOMEM; + } + + pinfo->dp_addr = dp_offset; + 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 *)dp_mem; + 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); + + cpm_dpfree(pinfo->dp_addr); +} + +/* 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]; +/* + * Is SMC1 being relocated? + */ +# ifdef CONFIG_I2C_SPI_SMC1_UCODE_PATCH + cpm_uart_ports[UART_SMC1].smcup = + (smc_uart_t *) & cpmp->cp_dparam[0x3C0]; +# else + cpm_uart_ports[UART_SMC1].smcup = + (smc_uart_t *) & cpmp->cp_dparam[PROFF_SMC1]; +# endif + 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; +} diff --git a/drivers/serial/cpm_uart/cpm_uart_cpm1.h b/drivers/serial/cpm_uart/cpm_uart_cpm1.h new file mode 100644 index 00000000000..5d867ab581b --- /dev/null +++ b/drivers/serial/cpm_uart/cpm_uart_cpm1.h @@ -0,0 +1,45 @@ +/* + * 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) +{ + 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 diff --git a/drivers/serial/cpm_uart/cpm_uart_cpm2.c b/drivers/serial/cpm_uart/cpm_uart_cpm2.c new file mode 100644 index 00000000000..b422c3abfba --- /dev/null +++ b/drivers/serial/cpm_uart/cpm_uart_cpm2.c @@ -0,0 +1,328 @@ +/* + * linux/drivers/serial/cpm_uart_cpm2.c + * + * Driver for CPM (SCC/SMC) serial ports; CPM2 definitions + * + * Maintainer: Kumar Gala (kumar.gala@freescale.com) (CPM2) + * Pantelis Antoniou (panto@intracom.gr) (CPM1) + * + * Copyright (C) 2004 Freescale Semiconductor, 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; + u8 *dp_mem; + uint dp_offset; + 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_offset = cpm_dpalloc(dpmemsz, 8); + if (IS_DPERR(dp_offset)) { + printk(KERN_ERR + "cpm_uart_cpm.c: could not allocate buffer descriptors\n"); + return -ENOMEM; + } + + dp_mem = cpm_dpram_addr(dp_offset); + + 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); + + if (mem_addr == NULL) { + cpm_dpfree(dp_offset); + printk(KERN_ERR + "cpm_uart_cpm.c: could not allocate coherent memory\n"); + return -ENOMEM; + } + + pinfo->dp_addr = dp_offset; + 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 *)dp_mem; + 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); + + cpm_dpfree(pinfo->dp_addr); +} + +/* 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; +} diff --git a/drivers/serial/cpm_uart/cpm_uart_cpm2.h b/drivers/serial/cpm_uart/cpm_uart_cpm2.h new file mode 100644 index 00000000000..4793fecf8ec --- /dev/null +++ b/drivers/serial/cpm_uart/cpm_uart_cpm2.h @@ -0,0 +1,45 @@ +/* + * 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) +{ + cpm_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 |