Forked.

Fork of mbed-dev by mbed official

targets/TARGET_ublox/TARGET_HI2110/serial_api.c

Committer:
<>
Date:
2016-11-08
Revision:
150:02e0a0aed4ec

File content as of revision 150:02e0a0aed4ec:

/* mbed Microcontroller Library
 * Copyright (c) 2016 u-blox
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/* The serial driver connects UART HW to mbed and also associates the UART
 * HW with physical pins.  Any physical pin can be linked to any UART,
 * however the mbed serial port initialisation API makes no mention of
 * which UART HW is to be used (only the pins) and hence the driver needs
 * to make some decisions for itself.
 *
 * There are two and a half UARTs on the chip: UART0, UART1 and a
 * lower-power, receive-only UART that is clocked from 32 kHz and can
 * therefore be awake while the rest of the chip is sleeping peacefully.
 * This provides maximal power saving, however the LP UART can only run
 * at 9600 bits/s (which is quite sufficient for all NB-IoT needs).
 *
 * So, if the baud rate is 9600 the driver code configures the LP UART
 * for Rx and UART0 for Tx. If the baud rate is not 9600 then it configures
 * UART0 for both Rx and Tx.  Unless... the Tx pin is the pin UART1_TX (it
 * is an mbed convention to use the Tx pin), which is p6, in which case UART1
 * is configured instead.  This latter is not the normal case as this pin
 * is intended to be used as a GPIO.
 *
 * If the baud rate is changed the driver reconfigures to match.
 *
 * TODO: implement asynchronous and flow control APIs.
 */

#include "mbed_assert.h"
#include "serial_api.h"
#include "pinmap.h"

#include "cmsis.h"

/* ----------------------------------------------------------------
 * MACROS
 * ----------------------------------------------------------------*/

/* Registers banks for the standard UARTs */
#define UART0_REG (*(volatile uart_ctrl_t *) UART0_BASE)
#define UART1_REG (*(volatile uart_ctrl_t *) UART1_BASE)

/* Masks for the UART control bits in the reset and clock enable registers */
#define UART0_CTRL  (1 << 3)
#define UART1_CTRL  (1 << 4)
#define UARTLP_CTRL (1 << 6)

/* Convert number of data bits to register values */
#define MIN_NUM_UART_DATA_BITS 5
#define MAX_NUM_UART_DATA_BITS 8
#define REGISTER_DATA_BITS(x) ((x) - MIN_NUM_UART_DATA_BITS)

/* Number of stop bits */
#define NUM_UART_STOP_BITS_1 1
#define NUM_UART_STOP_BITS_2 2

/* ----------------------------------------------------------------
 * TYPES
 * ----------------------------------------------------------------*/

/* Enum to identify the interrupt to the UART handler */
typedef enum {
    IRQ_UART_ID_0_AND_LP,
    IRQ_UART_ID_1,
    NUM_IRQ_IDS
} irq_uart_id_t;

/* ----------------------------------------------------------------
 * GLOBAL VARIABLES
 * ----------------------------------------------------------------*/

/* The IRQ configuration variables, set up and named by mbed */
static uint32_t serial_irq_ids[NUM_IRQ_IDS] = {0};
static uart_irq_handler irq_handler = NULL;

/* RTX needs these */
int stdio_uart_inited = 0;
serial_t stdio_uart;

/* ----------------------------------------------------------------
 * FUNCTION PROTOTYPES
 * ----------------------------------------------------------------*/

static void init_config(serial_t *obj);
static void deinit_config(serial_t *obj);
static void set_baud(serial_t *obj, uint32_t baud_rate);
static void irq_enable(serial_t *obj);
static void irq_disable(serial_t *obj);

/* ----------------------------------------------------------------
 * NON-API FUNCTIONS
 * ----------------------------------------------------------------*/

/* Initialise the given serial config by setting the pin functions
 * and then resetting the relevant HW */
static void init_config(serial_t *obj)
{
    uint32_t x;

    switch (obj->config) {
        case SERIAL_CONFIG_UARTLP_RX_UART0_TX:
        {
            pin_function(obj->rx_pin, PIN_FUNCTION_LP_UART);
            pin_function(obj->tx_pin, PIN_FUNCTION_UART0_TXD);
            CLKEN_REG_BITSET = UARTLP_CTRL | UART0_CTRL;
            obj->reg_base = &UART0_REG;
            obj->index = IRQ_UART_ID_0_AND_LP;
            /* Reset the LPUART and UART0 HW */
            /* NOTE: RESET_REG_BITTOG doesn't have the desired
             * effect, need to use BITSET and then BITCLR */
            RESET_REG_BITSET |= 1ul << 6;
            RESET_REG_BITCLR |= 1ul << 6;
            RESET_REG_BITSET |= 1ul << 3;
            RESET_REG_BITCLR |= 1ul << 3;
        }
        break;
        case SERIAL_CONFIG_UART0_RX_UART0_TX:
        {
            pin_function(obj->rx_pin, PIN_FUNCTION_UART0_RXD);
            pin_function(obj->tx_pin, PIN_FUNCTION_UART0_TXD);
            CLKEN_REG_BITSET = UART0_CTRL;
            obj->reg_base = &UART0_REG;
            obj->index = IRQ_UART_ID_0_AND_LP;
            /* Reset the UART0 HW */
            RESET_REG_BITSET |= 1ul << 3;
            RESET_REG_BITCLR |= 1ul << 3;
        }
        break;
        case SERIAL_CONFIG_UART1_RX_UART1_TX:
        {
            pin_function(obj->rx_pin, PIN_FUNCTION_UART1_RXD);
            pin_function(obj->tx_pin, PIN_FUNCTION_UART1_TXD);
            CLKEN_REG_BITSET = UART1_CTRL;
            obj->reg_base = &UART1_REG;
            obj->index = IRQ_UART_ID_1;
            /* Reset the UART1 HW */
            RESET_REG_BITSET |= 1ul << 4;
            RESET_REG_BITCLR |= 1ul << 4;
        }
        break;
        default:
        {
            MBED_ASSERT(false);
        }
        break;
    }

    /* Tickle the UART control register to make sure it is updated */
    x = obj->reg_base->UARTLCR_H;
    obj->reg_base->UARTLCR_H = x;

    /* Set the FIFO. The meaning of the three FIFO interrupt-level
     * bits are as follows:
     *
     * 0 = 1/8 full
     * 1 = 1/4 full
     * 2 = 1/2 full
     * 3 = 3/4 full
     * 4 = 7/8 full
     *
     * Set up the Rx FIFO to be used fully (but we will also set
     * a timeout to get immediate notice) and also the Tx FIFO
     * to be fully used. */
     obj->reg_base->UARTIFLS = (obj->reg_base->UARTIFLS & ~(0x07 << 0)) | (4 << 0);
     obj->reg_base->UARTIFLS = (obj->reg_base->UARTIFLS & ~(0x07 << 3)) | (4 << 3);
     obj->reg_base->UARTLCR_H |= 1 << 4;

    /* Enable for Tx and Rx (TODO: add CTS when we add flow control) */
    obj->reg_base->UARTCR |= (1 << 8) | (1 << 9);

    /* Now enable it */
    obj->reg_base->UARTCR |= 1 << 0;

    obj->format_set = false;
    obj->baud_rate = 0;
    obj->irq_rx_setting = IRQ_NOT_SET;
    obj->irq_tx_setting = IRQ_NOT_SET;
}

/* Release a serial port */
static void deinit_config(serial_t *obj)
{
    pin_function(obj->rx_pin, PIN_FUNCTION_UNCLAIMED);
    pin_function(obj->tx_pin, PIN_FUNCTION_UNCLAIMED);

    /* Disable UART */
    obj->reg_base->UARTCR &= ~(1 << 0);

    /* Flush transmit FIFO */
    obj->reg_base->UARTLCR_H = 0;

    /* Disable everything */
    obj->reg_base->UARTCR = 0;

    switch (obj->config) {
        case SERIAL_CONFIG_UARTLP_RX_UART0_TX:
        {
            CLKEN_REG_BITCLR = UARTLP_CTRL | UART0_CTRL;
            LP_UART_CTRL &= ~(0xF << 20); /* Disable all LP interrupts */
        }
        break;
        case SERIAL_CONFIG_UART0_RX_UART0_TX:
        {
            CLKEN_REG_BITCLR = UART0_CTRL;
        }
        break;
        case SERIAL_CONFIG_UART1_RX_UART1_TX:
        {
            CLKEN_REG_BITCLR = UART1_CTRL;
        }
        break;
        default:
        {
            MBED_ASSERT(false);
        }
        break;
    }

    obj->config = MAX_NUM_SERIAL_CONFIGS;
    obj->reg_base = NULL;
}

/* Set the baud rate for either of the two (non-LP) UARTS */
static void set_baud(serial_t *obj, uint32_t baud_rate)
{
    uint32_t baud_rate_divider_i;
    uint32_t baud_rate_divider_f;
    uint32_t remainder;
    uint32_t core_clock;
    uint32_t x;

    /* Baud rate divider calculation:
     *
     * The integer part is: clock / (16 * baud)
     *
     * The fractional part is:  round (decimal_part * 64),
     * ...where decimal part is, for example, 0.085
     *
     * decimal_part is: remainder / (16 * baud),
     * ...where: remainder = core_clock % (baud * 16),
     *
     * So the fractional part becomes:
     * round (decimal_part * 64) = round (remainder * 64 / (16 * baud)) = round (remainder * 4 / baud)
     */

    /* Get the mean clock frequency */
    core_clock = (CLK_FREQ_HIGHTARGET >> 1) + (CLK_FREQ_LOWTARGET >> 1);
    /* Work out the actual clock frequency */
    core_clock = (core_clock * CLOCKS_REFERENCE_CLOCK_FREQ) / (((CLK_FREQ_NREFCLKS + 1) & 0xFFFF) * (CLK_GATE_SYS & 0xFF));
    baud_rate_divider_i = core_clock / (baud_rate << 4);
    remainder = core_clock % (baud_rate << 4);
    baud_rate_divider_f = ((remainder << 3) / baud_rate) >> 1;
    /* Round it */
    baud_rate_divider_f += ((remainder << 3) / baud_rate) & 1;

    /* Disable UART while writing to control registers */
    obj->reg_base->UARTCR &= ~(1 << 0);

    obj->reg_base->UARTIBRD = baud_rate_divider_i;
    obj->reg_base->UARTFBRD = baud_rate_divider_f;

    /* Make IBRD and FBRD update */
    x = obj->reg_base->UARTLCR_H;
    obj->reg_base->UARTLCR_H = x;

    /* Now enable the UART again */
    obj->reg_base->UARTCR |= 1 << 0;
}

/* Set the NVIC bits */
static void irq_enable(serial_t *obj)
{
    switch (obj->config) {
        case SERIAL_CONFIG_UARTLP_RX_UART0_TX:
        {
            NVIC_EnableIRQ(UART0_IRQn);
            NVIC_EnableIRQ(LPUART_IRQn);
        }
        break;
        case SERIAL_CONFIG_UART0_RX_UART0_TX:
        {
            NVIC_EnableIRQ(UART0_IRQn);
        }
        break;
        case SERIAL_CONFIG_UART1_RX_UART1_TX:
        {
            NVIC_EnableIRQ(UART1_IRQn);
        }
        break;
        default:
        {
            MBED_ASSERT(false);
        }
        break;
    }
}

/* Unset the NVIC bits */
static void irq_disable(serial_t *obj)
{
    switch (obj->config) {
        case SERIAL_CONFIG_UARTLP_RX_UART0_TX:
        {
            NVIC_DisableIRQ(UART0_IRQn);
            NVIC_DisableIRQ(LPUART_IRQn);
        }
        break;
        case SERIAL_CONFIG_UART0_RX_UART0_TX:
        {
            NVIC_DisableIRQ(UART0_IRQn);
        }
        break;
        case SERIAL_CONFIG_UART1_RX_UART1_TX:
        {
            NVIC_DisableIRQ(UART1_IRQn);
        }
        break;
        default:
        {
            MBED_ASSERT(false);
        }
        break;
    }
}

/* UART0 IRQ */
void IRQ7_UART0_Handler()
{
    uint32_t id = serial_irq_ids[IRQ_UART_ID_0_AND_LP];

    /* Check Rx and Rx Timeout bit */
    if (UART0_REG.UARTMIS & ((1 << 4) | (1 << 6))) {
        if (id != 0) {
            irq_handler(id, RxIrq);
            /* Reading the character clears the interrupt,
             * no way to protect against another arriving
             * while processing one */
        }
    }
    /* Check Tx bit */
    if (UART0_REG.UARTMIS & (1 << 5)) {
        if (id != 0) {
            irq_handler(id, TxIrq);
        }
        /* Not sure what clears the interrupt so clear it explicitly */
        NVIC_ClearPendingIRQ(UART1_IRQn);
    }
}

/* UART1 IRQ */
void IRQ8_UART1_Handler()
{
    uint32_t id = serial_irq_ids[IRQ_UART_ID_1];

    /* Check Rx and Rx Timeout bit */
    if (UART1_REG.UARTMIS & ((1 << 4) | (1 << 6))) {
        if (id != 0) {
            irq_handler(id, RxIrq);
        }
        /* Reading the character clears the interrupt,
         * no way to protect against another arriving
         * while processing one */
    }
    /* Check Tx bit */
    if (UART1_REG.UARTMIS & (1 << 5)) {
        if (id != 0) {
            irq_handler(id, TxIrq);
        }
        /* Not sure what clears the interrupt so clear it explicitly */
        NVIC_ClearPendingIRQ(UART1_IRQn);
    }
}

/* LP UART IRQ, receive only */
void IRQ16_LPUART_Handler()
{
    uint32_t id = serial_irq_ids[IRQ_UART_ID_0_AND_LP];

    if (id != 0) {
        irq_handler(id, RxIrq);

        /* Another character might have arrived while
         * we are processing the last, so
         * check status bits 8 to 10 again and pend
         * interrupt if there's something there */
        if (((LP_UART_STATUS >> 8) & 0x07) != 0) {
            NVIC_SetPendingIRQ(LPUART_IRQn);
        } else {
            LP_UART_CTRL |= 1 << 27; /* Clear the interrupt */
        }
    }
}

/* ----------------------------------------------------------------
 * MBED API CALLS: SETUP FUNCTIONS
 * ----------------------------------------------------------------*/

void serial_init(serial_t *obj, PinName tx, PinName rx)
{
    uint32_t clock = CLK_FREQ_DIG_CLKS;

    /* There are two and a half UARTs on the chip. The normal
     * configuration is to use the LP_UART for Rx and UART0 for
     * Tx.  This gives maximal power saving in that the chip can
     * wake up on receipt of data. However, this only works if the
     * data rate is 9600 because that's the only data rate that
     * the 32 kHz (i.e. RTC) clock driving the LP UART can support.
     *
     * So, if the data rate is 9600, use the LP_UART/UART0
     * combination, otherwise use UART0 for both Rx and Tx.  However,
     * we don't know the data rate at this point so assume LP_UART
     * (as this works at the default baud rate) and we can change
     * our minds later.
     *
     * There is another serial port, UART1, which is normally used
     * by the modem processor to send out debug.  We only initialise
     * that here if the Tx pin is UART1_TX. */

    /* Wait for the clock to be stable */
    while ((clock < CLK_FREQ_LOWTARGET) || (clock > CLK_FREQ_HIGHTARGET)) {
        clock = CLK_FREQ_DIG_CLKS;
    }

    if (tx == UART1_TX) {
        /* Use UART1 for Rx and Tx */
        obj->config = SERIAL_CONFIG_UART1_RX_UART1_TX;
    } else {
        /* Use LP_UART for Rx, UART0 for Tx */
        obj->config = SERIAL_CONFIG_UARTLP_RX_UART0_TX;
    }

    obj->rx_pin = rx;
    obj->tx_pin = tx;
    init_config(obj);

    /* TODO: set rx pin Pull mode ? */

    /* set default baud rate and format */
    serial_baud(obj, 9600);
    serial_format(obj, 8, ParityNone, 1);

    if (tx == UART0_TX) {
        /* The UART0 pins are the stdio pins */
        stdio_uart_inited = 1;
        stdio_uart = *obj;
    }
}

void serial_free(serial_t *obj)
{
    if (obj->tx_pin == UART0_TX) {
        stdio_uart_inited = 0;
    }

    serial_irq_ids[obj->index] = 0;

    /* Release the port HW */
    deinit_config(obj);
}

void serial_baud(serial_t *obj, int baudrate)
{
    bool switch_port_config = false;
    bool format_set = obj->format_set;
    uint8_t stop_bits = obj->format.stop_bits;
    uint8_t data_bits = obj->format.data_bits;
    SerialParity parity = (SerialParity) obj->format.parity;
    irq_setting_t irq_rx_setting = obj->irq_rx_setting;
    irq_setting_t irq_tx_setting = obj->irq_tx_setting;

    if ((obj->config == SERIAL_CONFIG_UARTLP_RX_UART0_TX) && (baudrate != 9600)) {
        /* If we were on LP UART but the baud rate is not 9600 then
         * switch to the standard UART (as the LP UART can't go any higher
         * because it's clocked from 32 kHz) */
        deinit_config(obj);
        obj->config = SERIAL_CONFIG_UART0_RX_UART0_TX;
        init_config(obj);
        switch_port_config = true;
    } else if ((obj->config == SERIAL_CONFIG_UART0_RX_UART0_TX) && (baudrate == 9600)) {
        /* If we were on UART0 but the baud rate is 9600 then switch to the
         * LP UART for receive */
        deinit_config(obj);
        obj->config = SERIAL_CONFIG_UARTLP_RX_UART0_TX;
        init_config(obj);
        switch_port_config = true;
    }

    /* Disable UART while writing to control registers */
    obj->reg_base->UARTCR &= ~(1 << 0);

    if (switch_port_config) {
        /* If the port was switched, switch the port configuration also */
        if (format_set) {
            /* This serial port has been previously set up so switch the
             * settings across to this new configuration */
            serial_format(obj, data_bits, parity, stop_bits);
        }
        if (irq_rx_setting != IRQ_NOT_SET) {
            /* Do the same for Rx interrupts, if they were set */
            serial_irq_set(obj, RxIrq, (irq_rx_setting == IRQ_ON));
        }
        if (irq_tx_setting != IRQ_NOT_SET) {
            /* Do the same for Tx interrupts, if they were set */
            serial_irq_set(obj, TxIrq, (irq_tx_setting == IRQ_ON));
        }
    }

    switch (obj->config) {
        case SERIAL_CONFIG_UARTLP_RX_UART0_TX:
        {
            /* Set LP UART to 9600 (numerator 75 (0x4B), denominator 256 (00 == 256)) */
            LP_UART_CTRL = (LP_UART_CTRL & ~0xFFFF) | 0x004B;
            set_baud(obj, baudrate);
        }
        break;
        case SERIAL_CONFIG_UART0_RX_UART0_TX:
        case SERIAL_CONFIG_UART1_RX_UART1_TX:
        {
            set_baud(obj, baudrate);
        }
        break;
        default:
        {
            MBED_ASSERT(false);
        }
        break;
    }

    /* Enable the UART again */
    obj->reg_base->UARTCR |= 1 << 0;

    obj->baud_rate = baudrate;
}

void serial_format(serial_t *obj, int data_bits, SerialParity parity, int stop_bits)
{
    bool lp_also = false;

    MBED_ASSERT(data_bits >= MIN_NUM_UART_DATA_BITS);
    MBED_ASSERT(data_bits <= MAX_NUM_UART_DATA_BITS);
    MBED_ASSERT(stop_bits >= NUM_UART_STOP_BITS_1);
    MBED_ASSERT(stop_bits <= NUM_UART_STOP_BITS_2);

    /* The LP UART is different to UARTs 0 and 1 so deal with it
     * explicitly when required */
    if (obj->config == SERIAL_CONFIG_UARTLP_RX_UART0_TX) {
        lp_also = true;
    }

    /* Disable UART while writing to control registers */
    obj->reg_base->UARTCR &= ~(1 << 0);

    /* Set data bits (bits 5 and 6 of the UART0/1 register, bits 18 and 19 of the LP UART register) */
    obj->reg_base->UARTLCR_H = (obj->reg_base->UARTLCR_H & ~(0x03 << 5)) | (REGISTER_DATA_BITS(data_bits) << 5);
    if (lp_also) {
        LP_UART_CTRL = (LP_UART_CTRL & ~(0x03 << 18)) | (REGISTER_DATA_BITS(data_bits) << 18);
    }
    obj->format.data_bits = (uint8_t) data_bits;

    /* Set stop bits (bit 7 of the UART0/1 register) (there is no such setting for the LP UART) */
    if (stop_bits == NUM_UART_STOP_BITS_1) {
        /* Clear 2-stop-bits bit */
        obj->reg_base->UARTLCR_H &= ~(1 << 7);
    } else {
        /* Set 2-stop-bits bit */
        obj->reg_base->UARTLCR_H |= 1 << 7;
    }
    obj->format.stop_bits = (uint8_t) stop_bits;

    /* Set parity */
    switch (parity) {
        case ParityNone:
        {
            /* Disable parity */
            obj->reg_base->UARTLCR_H &= ~0x02;
            if (lp_also)
            {
                LP_UART_CTRL &= ~(1 << 24);
            }
        }
        break;
        case ParityOdd:
        {
            /* Set even bit and enable parity */
            obj->reg_base->UARTLCR_H = (obj->reg_base->UARTLCR_H | (1 << 3)) | (1 << 2);
            if (lp_also)
            {
                LP_UART_CTRL |= (1 << 24) | (1 << 25);
            }
        }
        break;
        case ParityEven:
        {
            /* Clear even bit and enable parity */
            obj->reg_base->UARTLCR_H = (obj->reg_base->UARTLCR_H & ~(1 << 3)) | (1 << 2);
            if (lp_also)
            {
                LP_UART_CTRL &= ~(1 << 25);
                LP_UART_CTRL |= 1 << 24;
            }
        }
        break;
        default:
        {
            MBED_ASSERT(false);
        }
        break;
    }

    /* Enable the UART again */
    obj->reg_base->UARTCR |= 1 << 0;

    obj->format.parity = (uint8_t) parity;
    obj->format_set = true;
}

/* ----------------------------------------------------------------
 * MBED API CALLS: INTERRUPT FUNCTIONS
 * ----------------------------------------------------------------*/

void serial_irq_handler(serial_t *obj, uart_irq_handler handler, uint32_t id)
{
    irq_handler = handler;
    serial_irq_ids[obj->index] = id;
}

void serial_irq_set(serial_t *obj, SerialIrq irq, uint32_t enable)
{
    bool lp_also = false;

    if (obj->config == SERIAL_CONFIG_UARTLP_RX_UART0_TX) {
        lp_also = true;
    }

    /* Disable UART while writing to control registers */
    obj->reg_base->UARTCR &= ~(1 << 0);

    if (enable) {
        switch (irq) {
            case RxIrq:
            {
                /* Bit 4 for Rx and bit 6 for Rx Timeout */
                obj->reg_base->UARTIMSC |= (1 << 4) | (1 << 6);
                if (lp_also)
                {
                    /* "Word Received" IRQ */
                    LP_UART_CTRL |= 1 << 23;
                }
                obj->irq_rx_setting = IRQ_ON;
                irq_enable(obj);
            }
            break;
            case TxIrq:
            {
                /* Bit 5 */
                obj->reg_base->UARTIMSC |= 1 << 5;
                obj->irq_tx_setting = IRQ_ON;
                irq_enable(obj);
            }
            break;
            default:
            {
                MBED_ASSERT(false);
            }
            break;
        }
    } else {
        switch (irq) {
            case RxIrq:
            {
                /* Bit 4  for Rx and bit 6 for Rx Timeout */
                obj->reg_base->UARTIMSC &= ~((1 << 4) | (1 << 6));
                if (lp_also)
                {
                    /* "Word Received" IRQ */
                    LP_UART_CTRL &= ~(1 << 23);
                }
                obj->irq_rx_setting = IRQ_OFF;
            }
            break;
            case TxIrq:
            {
                /* Bit 5 */
                obj->reg_base->UARTIMSC &= ~(1 << 5);
                obj->irq_tx_setting = IRQ_OFF;
            }
            break;
            default:
            {
                MBED_ASSERT(false);
            }
            break;
        }

        if ((obj->irq_rx_setting == IRQ_OFF) && (obj->irq_tx_setting == IRQ_OFF)) {
            irq_disable(obj);
        }
    }

    /* Enable the UART again */
    obj->reg_base->UARTCR |= 1 << 0;
}

/* ----------------------------------------------------------------
 * MBED API CALLS: TRANSMIT AND RECEIVE FUNCTIONS
 * ----------------------------------------------------------------*/

int serial_getc(serial_t *obj)
{
    uint8_t data = 0;

    /* Block until there is data to read */
    while (!serial_readable(obj)) {}

    /* Read the data */
    switch (obj->config) {
        case SERIAL_CONFIG_UARTLP_RX_UART0_TX:
        {
            data = (uint8_t) LP_UART_DATA;
        }
        break;
        case SERIAL_CONFIG_UART0_RX_UART0_TX:
        case SERIAL_CONFIG_UART1_RX_UART1_TX:
        {
            data = (uint8_t) obj->reg_base->UARTDR;
        }
        break;
        default:
        {
            MBED_ASSERT(false);
        }
        break;
    }

    return (int) data;
}

void serial_putc(serial_t *obj, int c)
{
    /* Block until there is room to write */
    while (!serial_writable(obj)) {}

    /* Write the data */
    obj->reg_base->UARTDR = (uint8_t) c;
}

int serial_readable(serial_t *obj)
{
    bool readable = false;

    switch (obj->config) {
        case SERIAL_CONFIG_UARTLP_RX_UART0_TX:
        {
            /* Check the status register, bits 8 to 10 indicate
             * the number of Rx bytes (make sure it's the status
             * register not the data register as a read from that
             * register would clear the Rx interrupt) */
            readable = (((LP_UART_STATUS >> 8) & 0x07) != 0);
        }
        break;
        case SERIAL_CONFIG_UART0_RX_UART0_TX:
        case SERIAL_CONFIG_UART1_RX_UART1_TX:
        {
            /* Check the Rx FIFO Empty bit */
            readable = ((obj->reg_base->UARTFR & (1 << 4)) != (1 << 4));
        }
        break;
        default:
        {
            MBED_ASSERT(false);
        }
        break;
    }

    return (int) readable;
}

int serial_writable(serial_t *obj)
{
    /* Check the "UART TX FIFO full" bit:
     * only if this is 0 can we transmit  */
    return (obj->reg_base->UARTFR & (1 << 5)) != (1 << 5);
}

void serial_break_set(serial_t *obj)
{
    /* Disable UART while writing to control registers */
    obj->reg_base->UARTCR &= ~(1 << 0);

    /* Set bit 1 of the line control register */
    obj->reg_base->UARTLCR_H |= 1 << 0;

    /* Enable the UART again */
    obj->reg_base->UARTCR |= 1 << 0;
}

void serial_break_clear(serial_t *obj)
{
    /* Disable UART while writing to control registers */
    obj->reg_base->UARTCR &= ~(1 << 0);

    /* Clear bit 1 of the line control register */
    obj->reg_base->UARTLCR_H &= ~(1 << 0);

    /* Enable the UART again */
    obj->reg_base->UARTCR |= 1 << 0;
}