/**************************************************************************** * * Copyright 2020 Samsung Electronics All Rights Reserved. * SPDX-License-Identifier: Apache-2.0 * * 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. * ****************************************************************************/ #if DEVICE_SERIAL // math.h required for floating point operations for baud rate calculation #include <math.h> #include <stdio.h> #include <string.h> #include <stdlib.h> #include "device.h" #include "serial_api.h" #include "cmsis.h" #include "pinmap.h" #include "PinNames.h" #include "mbed_error.h" #include "mbed_assert.h" #include "gpio_api.h" #define UART_PTR(ptr) ((S5JS100_UART_TypeDef *)(ptr)) static uart_irq_handler irq_handler[PL011_UART_MAX]; struct serial_context_data_s { uint32_t serial_irq_id; gpio_t sw_rts, sw_cts; uint8_t count, rx_irq_set_flow, rx_irq_set_api; }; static struct serial_context_data_s uart_data[PL011_UART_MAX]; /****************************************************************************** * INTERRUPTS HANDLING ******************************************************************************/ static inline void uart_irq(t_pl011_ports_enum index, UARTName uart) { S5JS100_UART_TypeDef *p_PL011_UART = UART_PTR(uart); int irq_type = 0xFFFF; /* type none */ if (!(p_PL011_UART->FR & 1u << 5)) { if (p_PL011_UART->IMSC & 1u << 5) { irq_type = TxIrq; } } if (p_PL011_UART->MIS & (1u << 4) || p_PL011_UART->MIS & (1u << 6)) { /* Rx Interrupt & Rx Timeout Interrupt The receive timeout interrupt is asserted when the receive FIFO is not empty, and no further data is received over a 32-bit period. */ irq_type = RxIrq; } if (irq_type == RxIrq) { if (uart_data[index].rx_irq_set_api) { (irq_handler[index])(uart_data[index].serial_irq_id, irq_type); } } if (irq_type == TxIrq) { /* Clear the TX interrupt Flag */ /* UART TX */ } else { /* Clear the Rx interupt Flag */ /* UART RX */ } } static void uart2_irq() { uart_irq(PL011_UART0_ID, UART_2); } static void uart3_irq() { uart_irq(PL011_UART1_ID, UART_3); } static void pl011_serial_irq_set_internal(void *obj, SerialIrq irq, uint32_t enable) { struct serial_s *priv = (struct serial_s *)obj; S5JS100_UART_TypeDef *p_PL011_UART = UART_PTR(priv->uart); if (enable) { p_PL011_UART->IMSC = 0x50; //interrupt by Rx Timeout & Rx (for fifo mode) } else if ((irq == TxIrq) || (uart_data[priv->index].rx_irq_set_api + uart_data[priv->index].rx_irq_set_flow == 0)) { p_PL011_UART->IMSC = 0; //interrupt by Rx Timeout & Rx (for fifo mode) } } // serial_baud // set the baud rate, taking in to account the current SystemFrequency static void pl011_serial_baud(void *obj, int baudrate) { struct serial_s *priv = (struct serial_s *)obj; S5JS100_UART_TypeDef *p_PL011_UART = UART_PTR(priv->uart); uint32_t sclk = 0; float div, frac; switch (priv->index) { case PL011_UART0_ID: sclk = cal_clk_getrate(d1_uart0); break; case PL011_UART1_ID: sclk = cal_clk_getrate(d1_uart1); break; default: MBED_ASSERT(false); } div = ((float)sclk / (float)(baudrate * 16)); frac = (uint32_t)(((div - (int32_t)div) * 64)); p_PL011_UART->IBRD = (uint32_t)div; p_PL011_UART->FBRD = (uint32_t)frac; } static void pl011_serial_format(void *obj, int data_bits, SerialParity parity, int stop_bits) { struct serial_s *priv = (struct serial_s *)obj; S5JS100_UART_TypeDef *p_PL011_UART = UART_PTR(priv->uart); uint32_t reg; switch (data_bits) { case 5: reg = 0; break; case 6: reg = 1 << 5; break; case 7: reg = 2 << 5; break; default: reg = 3 << 5; } switch (parity) { case ParityNone: reg |= 0; break; case ParityOdd: reg |= 4; break; case ParityEven: reg |= 6; break; case ParityForced1: reg |= 5; break; case ParityForced0: reg |= 7; break; } if (stop_bits == 2) { reg |= 1 << 3; } /* Enable FIFO */ reg |= 1 << 4; p_PL011_UART->LCRH = reg; } static void pl011_serial_irq_handler(void *obj, uart_irq_handler handler, uint32_t id) { struct serial_s *priv = (struct serial_s *)obj; irq_handler[priv->index] = handler; uart_data[priv->index].serial_irq_id = id; } static void pl011_serial_irq_set(void *obj, SerialIrq irq, uint32_t enable) { struct serial_s *priv = (struct serial_s *)obj; if (RxIrq == irq) { uart_data[priv->index].rx_irq_set_api = enable; } pl011_serial_irq_set_internal(obj, irq, enable); } static int pl011_serial_readable(void *obj) { struct serial_s *priv = (struct serial_s *)obj; S5JS100_UART_TypeDef *p_PL011_UART = UART_PTR(priv->uart); return !(p_PL011_UART->FR & (1u << 4)); } static int pl011_serial_writable(void *obj) { struct serial_s *priv = (struct serial_s *)obj; S5JS100_UART_TypeDef *p_PL011_UART = UART_PTR(priv->uart); return !(p_PL011_UART->FR & (1u << 5)); } static int pl011_serial_getc(void *obj) { struct serial_s *priv = (struct serial_s *)obj; S5JS100_UART_TypeDef *p_PL011_UART = UART_PTR(priv->uart); int data; while (!serial_readable(priv)); data = p_PL011_UART->DR & 0xFF; return data; } static void pl011_serial_putc(void *obj, int c) { struct serial_s *priv = (struct serial_s *)obj; S5JS100_UART_TypeDef *p_PL011_UART = UART_PTR(priv->uart); while (!serial_writable(priv)); p_PL011_UART->DR = c; } #if DEVICE_SERIAL_FC static void pl011_serial_set_flow_control(struct serial_s *obj, FlowControl type, PinName rxflow, PinName txflow) { error("pl011 flow control is not implenemted"); } #endif void pl011_serial_init(void *obj, PinName tx, PinName rx) { struct serial_s *priv = (struct serial_s *)obj; S5JS100_UART_TypeDef *p_PL011_UART = UART_PTR(priv->uart); /* Declare a variable of type IRQn, initialise to 0 */ IRQn_Type irq_n = (IRQn_Type)0; uint32_t vector = 0; switch ((int)priv->uart) { case UART_2: irq_n = S5JS100_IRQ_UART0; vector = (uint32_t)&uart2_irq; priv->index = PL011_UART0_ID; break; case UART_3 : irq_n = S5JS100_IRQ_UART1; vector = (uint32_t)&uart3_irq; priv->index = PL011_UART1_ID; break; } /* Enable UART and RX/TX path*/ p_PL011_UART->CR = 0x301; /* RX/TX fifo half full interrupt*/ p_PL011_UART->IFLS = (2 << 3) | 2; /* clear all interrupts mask */ p_PL011_UART->IMSC = 0x0; /* Clear all interripts */ p_PL011_UART->ICR = 0x7FF; /* MODESEL as default UART*/ p_PL011_UART->MODESEL = 0x0; priv->ops.serial_baud = pl011_serial_baud; priv->ops.serial_format = pl011_serial_format; priv->ops.serial_irq_handler = pl011_serial_irq_handler; priv->ops.serial_irq_set = pl011_serial_irq_set; priv->ops.serial_putc = pl011_serial_putc; priv->ops.serial_writable = pl011_serial_writable; priv->ops.serial_getc = pl011_serial_getc; priv->ops.serial_readable = pl011_serial_readable; #if DEVICE_SERIAL_FC priv->ops.serial_set_flow_control = pl011_serial_set_flow_control; #endif uart_data[priv->index].sw_rts.pin = NC; uart_data[priv->index].sw_cts.pin = NC; /* Assign IRQ in advance and enable, all are masked anyways */ NVIC_SetVector(irq_n, vector); #if defined (__ICACHE_PRESENT) && (__ICACHE_PRESENT == 1U) SCB_InvalidateICache(); #endif NVIC_EnableIRQ(irq_n); /* dissable IRQ by this if needed: NVIC_DisableIRQ(irq_n); */ } #endif // DEVICE_SERIAL