linux/arch/mips/pmcs-msp71xx/msp_serial.c
<<
>>
Prefs
   1/*
   2 * The setup file for serial related hardware on PMC-Sierra MSP processors.
   3 *
   4 * Copyright 2005 PMC-Sierra, Inc.
   5 *
   6 *  This program is free software; you can redistribute  it and/or modify it
   7 *  under  the terms of  the GNU General  Public License as published by the
   8 *  Free Software Foundation;  either version 2 of the  License, or (at your
   9 *  option) any later version.
  10 *
  11 *  THIS  SOFTWARE  IS PROVIDED   ``AS  IS'' AND   ANY  EXPRESS OR IMPLIED
  12 *  WARRANTIES,   INCLUDING, BUT NOT  LIMITED  TO, THE IMPLIED WARRANTIES OF
  13 *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN
  14 *  NO  EVENT  SHALL   THE AUTHOR  BE    LIABLE FOR ANY   DIRECT, INDIRECT,
  15 *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  16 *  NOT LIMITED   TO, PROCUREMENT OF  SUBSTITUTE GOODS  OR SERVICES; LOSS OF
  17 *  USE, DATA,  OR PROFITS; OR  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
  18 *  ANY THEORY OF LIABILITY, WHETHER IN  CONTRACT, STRICT LIABILITY, OR TORT
  19 *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  20 *  THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  21 *
  22 *  You should have received a copy of the  GNU General Public License along
  23 *  with this program; if not, write  to the Free Software Foundation, Inc.,
  24 *  675 Mass Ave, Cambridge, MA 02139, USA.
  25 */
  26
  27#include <linux/serial.h>
  28#include <linux/serial_core.h>
  29#include <linux/serial_reg.h>
  30#include <linux/slab.h>
  31
  32#include <asm/bootinfo.h>
  33#include <asm/io.h>
  34#include <asm/processor.h>
  35#include <asm/serial.h>
  36#include <linux/serial_8250.h>
  37
  38#include <msp_prom.h>
  39#include <msp_int.h>
  40#include <msp_regs.h>
  41
  42struct msp_uart_data {
  43        int     last_lcr;
  44};
  45
  46static void msp_serial_out(struct uart_port *p, int offset, int value)
  47{
  48        struct msp_uart_data *d = p->private_data;
  49
  50        if (offset == UART_LCR)
  51                d->last_lcr = value;
  52
  53        offset <<= p->regshift;
  54        writeb(value, p->membase + offset);
  55}
  56
  57static unsigned int msp_serial_in(struct uart_port *p, int offset)
  58{
  59        offset <<= p->regshift;
  60
  61        return readb(p->membase + offset);
  62}
  63
  64static int msp_serial_handle_irq(struct uart_port *p)
  65{
  66        struct msp_uart_data *d = p->private_data;
  67        unsigned int iir = readb(p->membase + (UART_IIR << p->regshift));
  68
  69        if (serial8250_handle_irq(p, iir)) {
  70                return 1;
  71        } else if ((iir & UART_IIR_BUSY) == UART_IIR_BUSY) {
  72                /*
  73                 * The DesignWare APB UART has an Busy Detect (0x07) interrupt
  74                 * meaning an LCR write attempt occurred while the UART was
  75                 * busy. The interrupt must be cleared by reading the UART
  76                 * status register (USR) and the LCR re-written.
  77                 *
  78                 * Note: MSP reserves 0x20 bytes of address space for the UART
  79                 * and the USR is mapped in a separate block at an offset of
  80                 * 0xc0 from the start of the UART.
  81                 */
  82                (void)readb(p->membase + 0xc0);
  83                writeb(d->last_lcr, p->membase + (UART_LCR << p->regshift));
  84
  85                return 1;
  86        }
  87
  88        return 0;
  89}
  90
  91void __init msp_serial_setup(void)
  92{
  93        char    *s;
  94        char    *endp;
  95        struct uart_port up;
  96        unsigned int uartclk;
  97
  98        memset(&up, 0, sizeof(up));
  99
 100        /* Check if clock was specified in environment */
 101        s = prom_getenv("uartfreqhz");
 102        if(!(s && *s && (uartclk = simple_strtoul(s, &endp, 10)) && *endp == 0))
 103                uartclk = MSP_BASE_BAUD;
 104        ppfinit("UART clock set to %d\n", uartclk);
 105
 106        /* Initialize first serial port */
 107        up.mapbase      = MSP_UART0_BASE;
 108        up.membase      = ioremap_nocache(up.mapbase, MSP_UART_REG_LEN);
 109        up.irq          = MSP_INT_UART0;
 110        up.uartclk      = uartclk;
 111        up.regshift     = 2;
 112        up.iotype       = UPIO_MEM;
 113        up.flags        = ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST;
 114        up.type         = PORT_16550A;
 115        up.line         = 0;
 116        up.serial_out   = msp_serial_out;
 117        up.serial_in    = msp_serial_in;
 118        up.handle_irq   = msp_serial_handle_irq;
 119        up.private_data = kzalloc(sizeof(struct msp_uart_data), GFP_KERNEL);
 120        if (!up.private_data) {
 121                pr_err("failed to allocate uart private data\n");
 122                return;
 123        }
 124        if (early_serial_setup(&up)) {
 125                kfree(up.private_data);
 126                pr_err("Early serial init of port 0 failed\n");
 127        }
 128
 129        /* Initialize the second serial port, if one exists */
 130        switch (mips_machtype) {
 131                case MACH_MSP4200_EVAL:
 132                case MACH_MSP4200_GW:
 133                case MACH_MSP4200_FPGA:
 134                case MACH_MSP7120_EVAL:
 135                case MACH_MSP7120_GW:
 136                case MACH_MSP7120_FPGA:
 137                        /* Enable UART1 on MSP4200 and MSP7120 */
 138                        *GPIO_CFG2_REG = 0x00002299;
 139                        break;
 140
 141                default:
 142                        return; /* No second serial port, good-bye. */
 143        }
 144
 145        up.mapbase      = MSP_UART1_BASE;
 146        up.membase      = ioremap_nocache(up.mapbase, MSP_UART_REG_LEN);
 147        up.irq          = MSP_INT_UART1;
 148        up.line         = 1;
 149        up.private_data         = (void*)UART1_STATUS_REG;
 150        if (early_serial_setup(&up)) {
 151                kfree(up.private_data);
 152                pr_err("Early serial init of port 1 failed\n");
 153        }
 154}
 155