/* * omap-uart.c * Based on drivers/char/ns16550.c * * Driver for OMAP-UART controller * * Copyright (C) 2013, Chen Baozi * * Note: This driver is made separate from 16550-series UART driver as * omap platform has some specific configurations */ #include #include #include #include #include #include #include #include #include #include #include #include #include #define REG_SHIFT 2 #define omap_read(uart, off) readl((uart)->regs + (off<regs + (off<uart; u32 lsr; uint32_t reg; while ( !(omap_read(uart, UART_IIR) & UART_IIR_NOINT) ) { lsr = omap_read(uart, UART_LSR) & 0xff; if ( lsr & UART_LSR_THRE ) serial_tx_interrupt(port, regs); if ( lsr & UART_LSR_DR ) serial_rx_interrupt(port, regs); if ( port->txbufc == port->txbufp ) { reg = omap_read(uart, UART_IER); omap_write(uart, UART_IER, reg & (~UART_IER_ETHREI)); } }; } static void baud_protocol_setup(struct omap_uart *uart) { u32 dll, dlh, efr; unsigned int divisor; divisor = uart->clock_hz / (uart->baud << 4); dll = divisor & 0xff; dlh = divisor >> 8; /* * Switch to register configuration mode B to access the UART_OMAP_EFR * register. */ omap_write(uart, UART_LCR, UART_LCR_CONF_MODE_B); /* * Enable access to the UART_IER[7:4] bit field. */ efr = omap_read(uart, UART_OMAP_EFR); omap_write(uart, UART_OMAP_EFR, efr|UART_OMAP_EFR_ECB); /* * Switch to register operation mode to access the UART_IER register. */ omap_write(uart, UART_LCR, 0); /* * Clear the UART_IER register (set the UART_IER[4] SLEEP_MODE bit * to 0 to change the UART_DLL and UART_DLM register). Set the * UART_IER register value to 0x0000. */ omap_write(uart, UART_IER, 0); /* * Switch to register configuartion mode B to access the UART_DLL and * UART_DLM registers. */ omap_write(uart, UART_LCR, UART_LCR_CONF_MODE_B); /* * Load divisor value. */ omap_write(uart, UART_DLL, dll); omap_write(uart, UART_DLM, dlh); /* * Restore the UART_OMAP_EFR */ omap_write(uart, UART_OMAP_EFR, efr); /* * Load the new protocol formatting (parity, stop-bit, character length) * and switch to register operational mode. */ omap_write(uart, UART_LCR, (uart->data_bits - 5) | ((uart->stop_bits - 1) << 2) | uart->parity); } static void fifo_setup(struct omap_uart *uart) { u32 lcr, efr, mcr; /* * Switch to register configuration mode B to access the UART_OMAP_EFR * register. */ lcr = omap_read(uart, UART_LCR); omap_write(uart, UART_LCR, UART_LCR_CONF_MODE_B); /* * Enable register submode TCR_TLR to access the UART_OMAP_TLR register. */ efr = omap_read(uart, UART_OMAP_EFR); omap_write(uart, UART_OMAP_EFR, efr|UART_OMAP_EFR_ECB); /* * Switch to register configuration mode A to access the UART_MCR * register. */ omap_write(uart, UART_LCR, UART_LCR_CONF_MODE_A); /* * Enable register submode TCR_TLR to access the UART_OMAP_TLR register */ mcr = omap_read(uart, UART_MCR); omap_write(uart, UART_MCR, mcr|UART_MCR_TCRTLR); /* * Enable the FIFO; load the new FIFO trigger and the new DMA mode. */ omap_write(uart, UART_FCR, UART_FCR_R_TRIG_01| UART_FCR_T_TRIG_10|UART_FCR_ENABLE); /* * Switch to register configuration mode B to access the UART_EFR * register. */ omap_write(uart, UART_LCR, UART_LCR_CONF_MODE_B); /* * Load the new FIFO triggers and the new DMA mode bit. */ omap_write(uart, UART_OMAP_SCR, OMAP_UART_SCR_RX_TRIG_GRANU1_MASK); /* * Restore the UART_OMAP_EFR[4] value. */ omap_write(uart, UART_OMAP_EFR, efr); /* * Switch to register configuration mode A to access the UART_MCR * register. */ omap_write(uart, UART_LCR, UART_LCR_CONF_MODE_A); /* * Restore UART_MCR[6] value. */ omap_write(uart, UART_MCR, mcr); /* * Restore UART_LCR value. */ omap_write(uart, UART_LCR, lcr); uart->fifo_size = 64; } static void __init omap_uart_init_preirq(struct serial_port *port) { struct omap_uart *uart = port->uart; /* * Clear the FIFO buffers. */ omap_write(uart, UART_FCR, UART_FCR_ENABLE); omap_write(uart, UART_FCR, UART_FCR_ENABLE|UART_FCR_CLRX|UART_FCR_CLTX); omap_write(uart, UART_FCR, 0); /* * The TRM says the mode should be disabled while UART_DLL and UART_DHL * are being changed so we disable before setup, then enable. */ omap_write(uart, UART_OMAP_MDR1, UART_OMAP_MDR1_DISABLE); /* Baud rate & protocol format setup */ baud_protocol_setup(uart); /* FIFO setup */ fifo_setup(uart); /* No flow control */ omap_write(uart, UART_MCR, UART_MCR_DTR|UART_MCR_RTS); omap_write(uart, UART_OMAP_MDR1, UART_OMAP_MDR1_16X_MODE); } static void __init omap_uart_init_postirq(struct serial_port *port) { struct omap_uart *uart = port->uart; uart->irqaction.handler = omap_uart_interrupt; uart->irqaction.name = "omap_uart"; uart->irqaction.dev_id = port; if ( setup_dt_irq(&uart->irq, &uart->irqaction) != 0 ) { dprintk(XENLOG_ERR, "Failed to allocated omap_uart IRQ %d\n", uart->irq.irq); return; } /* Enable interrupts */ omap_write(uart, UART_IER, UART_IER_ERDAI|UART_IER_ETHREI|UART_IER_ELSI); } static void omap_uart_suspend(struct serial_port *port) { BUG(); } static void omap_uart_resume(struct serial_port *port) { BUG(); } static int omap_uart_tx_ready(struct serial_port *port) { struct omap_uart *uart = port->uart; uint32_t reg; reg = omap_read(uart, UART_IER); omap_write(uart, UART_IER, reg | UART_IER_ETHREI); return omap_read(uart, UART_LSR) & UART_LSR_THRE ? uart->fifo_size : 0; } static void omap_uart_putc(struct serial_port *port, char c) { struct omap_uart *uart = port->uart; omap_write(uart, UART_THR, (uint32_t)(unsigned char)c); } static int omap_uart_getc(struct serial_port *port, char *pc) { struct omap_uart *uart = port->uart; if ( !(omap_read(uart, UART_LSR) & UART_LSR_DR) ) return 0; *pc = omap_read(uart, UART_RBR) & 0xff; return 1; } static int __init omap_uart_irq(struct serial_port *port) { struct omap_uart *uart = port->uart; return ((uart->irq.irq > 0) ? uart->irq.irq : -1); } static const struct dt_irq __init *omap_uart_dt_irq(struct serial_port *port) { struct omap_uart *uart = port->uart; return &uart->irq; } static const struct vuart_info *omap_vuart_info(struct serial_port *port) { struct omap_uart *uart = port->uart; return &uart->vuart; } static struct uart_driver __read_mostly omap_uart_driver = { .init_preirq = omap_uart_init_preirq, .init_postirq = omap_uart_init_postirq, .endboot = NULL, .suspend = omap_uart_suspend, .resume = omap_uart_resume, .tx_ready = omap_uart_tx_ready, .putc = omap_uart_putc, .getc = omap_uart_getc, .irq = omap_uart_irq, .dt_irq_get = omap_uart_dt_irq, .vuart_info = omap_vuart_info, }; static int __init omap_uart_init(struct dt_device_node *dev, const void *data) { const char *config = data; struct omap_uart *uart; u32 clkspec; int res; u64 addr, size; if ( strcmp(config, "") ) early_printk("WARNING: UART configuration is not supported\n"); uart = &omap_com; res = dt_property_read_u32(dev, "clock-frequency", &clkspec); if ( !res ) { early_printk("omap-uart: Unable to retrieve the clock frequency\n"); return -EINVAL; } uart->clock_hz = clkspec; uart->baud = 115200; uart->data_bits = 8; uart->parity = UART_PARITY_NONE; uart->stop_bits = 1; res = dt_device_get_address(dev, 0, &addr, &size); if ( res ) { early_printk("omap-uart: Unable to retrieve the base" " address of the UART\n"); return res; } uart->regs = ioremap_attr(addr, size, PAGE_HYPERVISOR_NOCACHE); if ( !uart->regs ) { early_printk("omap-uart: Unable to map the UART memory\n"); return -ENOMEM; } res = dt_device_get_irq(dev, 0, &uart->irq); if ( res ) { early_printk("omap-uart: Unable to retrieve the IRQ\n"); return res; } uart->vuart.base_addr = addr; uart->vuart.size = size; uart->vuart.data_off = UART_THR; uart->vuart.status_off = UART_LSR << REG_SHIFT; uart->vuart.status = UART_LSR_THRE; /* Register with generic serial driver */ serial_register_uart(SERHND_DTUART, &omap_uart_driver, uart); dt_device_set_used_by(dev, DOMID_XEN); return 0; } static const char * const omap_uart_dt_compat[] __initdata = { "ti,omap4-uart", NULL }; DT_DEVICE_START(omap_uart, "OMAP UART", DEVICE_SERIAL) .compatible = omap_uart_dt_compat, .init = omap_uart_init, DT_DEVICE_END /* * Local variables: * mode: C * c-file-style: "BSD" * c-basic-offset: 4 * indent-tabs-mode: nil * End: */