/* * Freescale i.MX23/i.MX28 AUART driver * * Copyright (C) 2013 Andreas Wass * * Based on the MXC serial driver: * * (c) 2007 Sascha Hauer * * Further based on the Linux mxs-auart.c driver: * * Freescale STMP37XX/STMP38X Application UART drkiver * Copyright 2008-2010 Freescale Semiconductor, Inc. * * SPDX-License-Identifier: GPL-2.0+ */ #include #include #include #include #include #include #include DECLARE_GLOBAL_DATA_PTR; #ifndef CONFIG_MXS_AUART_BASE #error "CONFIG_MXS_AUART_BASE must be set to the base UART to use" #endif /* AUART clock always supplied by XTAL and always 24MHz */ #define MXS_AUART_CLK 24000000 static struct mxs_uartapp_regs *get_uartapp_registers(void) { return (struct mxs_uartapp_regs *)CONFIG_MXS_AUART_BASE; } /** * Sets the baud rate and settings. * The settings are: 8 data bits, no parit and 1 stop bit. */ static void mxs_auart_setbrg(void) { u32 div; u32 linectrl = 0; struct mxs_uartapp_regs *regs = get_uartapp_registers(); if (!gd->baudrate) gd->baudrate = CONFIG_BAUDRATE; /* * From i.MX28 datasheet: * div is calculated by calculating UARTCLK*32/baudrate, rounded to int * div must be between 0xEC and 0x003FFFC0 inclusive * Lowest 6 bits of div goes in BAUD_DIVFRAC part of LINECTRL register * Next 16 bits goes in BAUD_DIVINT part of LINECTRL register */ div = (MXS_AUART_CLK * 32) / gd->baudrate; if (div < 0xEC || div > 0x003FFFC0) return; linectrl |= ((div & UARTAPP_LINECTRL_EXTRACT_BAUD_DIVFRAC_MASK) << UARTAPP_LINECTRL_BAUD_DIVFRAC_OFFSET) & UARTAPP_LINECTRL_BAUD_DIVFRAC_MASK; linectrl |= ((div >> UARTAPP_LINECTRL_EXTRACT_BAUD_DIVINT_OFFSET) << UARTAPP_LINECTRL_BAUD_DIVINT_OFFSET) & UARTAPP_LINECTRL_BAUD_DIVINT_MASK; /* Word length: 8 bits */ linectrl |= UARTAPP_LINECTRL_WLEN_8BITS; /* Enable FIFOs. */ linectrl |= UARTAPP_LINECTRL_FEN_MASK; /* Write above settings, no parity, 1 stop bit */ writel(linectrl, ®s->hw_uartapp_linectrl); } static int mxs_auart_init(void) { struct mxs_uartapp_regs *regs = get_uartapp_registers(); /* Reset everything */ mxs_reset_block(®s->hw_uartapp_ctrl0_reg); /* Disable interrupts */ writel(0, ®s->hw_uartapp_intr); /* Set baud rate and settings */ serial_setbrg(); /* Disable RTS and CTS, ignore LINECTRL2 register */ writel(UARTAPP_CTRL2_RTSEN_MASK | UARTAPP_CTRL2_CTSEN_MASK | UARTAPP_CTRL2_USE_LCR2_MASK, ®s->hw_uartapp_ctrl2_clr); /* Enable receiver, transmitter and UART */ writel(UARTAPP_CTRL2_RXE_MASK | UARTAPP_CTRL2_TXE_MASK | UARTAPP_CTRL2_UARTEN_MASK, ®s->hw_uartapp_ctrl2_set); return 0; } static void mxs_auart_putc(const char c) { struct mxs_uartapp_regs *regs = get_uartapp_registers(); /* Wait in loop while the transmit FIFO is full */ while (readl(®s->hw_uartapp_stat) & UARTAPP_STAT_TXFF_MASK) ; writel(c, ®s->hw_uartapp_data); if (c == '\n') mxs_auart_putc('\r'); } static int mxs_auart_tstc(void) { struct mxs_uartapp_regs *regs = get_uartapp_registers(); /* Checks if receive FIFO is empty */ return !(readl(®s->hw_uartapp_stat) & UARTAPP_STAT_RXFE_MASK); } static int mxs_auart_getc(void) { struct mxs_uartapp_regs *regs = get_uartapp_registers(); /* Wait until a character is available to read */ while (!mxs_auart_tstc()) ; /* Read the character from the data register */ return readl(®s->hw_uartapp_data) & 0xFF; } static struct serial_device mxs_auart_drv = { .name = "mxs_auart_serial", .start = mxs_auart_init, .stop = NULL, .setbrg = mxs_auart_setbrg, .putc = mxs_auart_putc, .puts = default_serial_puts, .getc = mxs_auart_getc, .tstc = mxs_auart_tstc, }; void mxs_auart_initialize(void) { serial_register(&mxs_auart_drv); } __weak struct serial_device *default_serial_console(void) { return &mxs_auart_drv; }