/**************************************************************************** * * 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 "gpio_api.h" #include "s5js100.h" #include "mbed_wait_api.h" #include "mbed_thread.h" #define USI_PTR(ptr) ((S5JS100_USI_UART_TypeDef *)(ptr)) static uart_irq_handler irq_handler[USI_MAX_PORTS]; static uint32_t serial_irq_id[USI_MAX_PORTS] = {0}; /****************** Internal code ************************/ static inline void uart_irq(uint32_t intstatus, uint32_t index, UARTName uart) { if (intstatus & (1 << 0)) { (irq_handler[index])(serial_irq_id[index], RxIrq); } else if (intstatus & (1 << 1)) { // RTS Int } else if (intstatus & (1 << 2)) { (irq_handler[index])(serial_irq_id[index], TxIrq); } else if (intstatus & (1 << 3)) { // CTS Int } else { } USI_PTR(uart)->UINTP = intstatus; } static void uart0_irq() { uart_irq(S5JS100_USI0_UART->UINTP & 0xF, 0, UART_0); } static void uart1_irq() { uart_irq(S5JS100_USI1_UART->UINTP & 0xF, 1, UART_1); } /*********************** API ******************************/ // serial_baud // set the baud rate, taking in to account the current SystemFrequency static void usi_serial_baud(void *obj, int baudrate) { struct serial_s *priv = (struct serial_s *)obj; int32_t sclk = 0; float div, frac; S5JS100_USI_UART_TypeDef *p_USI_UART = USI_PTR(priv->uart); switch ((int)priv->uart) { case UART_0: /* UBRDIV and UFRACVAL */ sclk = cal_clk_getrate(d1_usi0); break; case UART_1: /* UBRDIV and UFRACVAL */ sclk = cal_clk_getrate(d1_usi1); break; default: MBED_ASSERT(false); } div = ((float)sclk / (float)(baudrate * 16)) - 1.0; frac = (uint32_t)(((div - (int32_t)div) * 16)); p_USI_UART->UBRDIV = (uint32_t)div; p_USI_UART->UFRACVAL = (uint32_t)frac; } static void usi_serial_format(void *obj, int data_bits, SerialParity parity, int stop_bits) { struct serial_s *priv = (struct serial_s *)obj; S5JS100_USI_UART_TypeDef *p_USI_UART = USI_PTR(priv->uart); uint32_t reg; switch (data_bits) { case 5: reg = UART_ULCON_DATABITS_5BITS; break; case 6: reg = UART_ULCON_DATABITS_6BITS; break; case 7: reg = UART_ULCON_DATABITS_7BITS; break; default: reg = UART_ULCON_DATABITS_8BITS; } switch (parity) { case ParityNone: reg |= UART_ULCON_PARITY_NONE; break; case ParityOdd: reg |= UART_ULCON_PARITY_ODD; break; case ParityEven: reg |= UART_ULCON_PARITY_EVEN; break; case ParityForced1: reg |= UART_ULCON_PARITY_FORCE1; break; case ParityForced0: reg |= UART_ULCON_PARITY_FORCE0; break; } if (stop_bits == 2) { reg |= UART_ULCON_STOPBITS_2BITS; } p_USI_UART->ULCON = reg; } static void usi_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; serial_irq_id[priv->index] = id; } static void usi_serial_irq_set(void *obj, SerialIrq irq, uint32_t enable) { struct serial_s *priv = (struct serial_s *)obj; S5JS100_USI_UART_TypeDef *p_USI_UART = USI_PTR(priv->uart); switch (irq) { case RxIrq: if (enable) { p_USI_UART->UINTM &= ~(UART_UINTM_RXD_MASK); } else { p_USI_UART->UINTM |= UART_UINTM_RXD_MASK; p_USI_UART->UINTP = UART_UINTP_RXD; } break; case TxIrq: if (enable) { p_USI_UART->UINTM &= ~(UART_UINTM_TXD_MASK); } else { p_USI_UART->UINTM |= UART_UINTM_TXD_MASK; p_USI_UART->UINTP = UART_UINTP_TXD; } break; } } static int usi_serial_writable(void *obj) { struct serial_s *priv = (struct serial_s *)obj; S5JS100_USI_UART_TypeDef *p_USI_UART = USI_PTR(priv->uart); return !(p_USI_UART->UFSTAT & UART_UFSTAT_TX_FIFO_FULL); } static void usi_serial_putc(void *obj, int c) { struct serial_s *priv = (struct serial_s *)obj; S5JS100_USI_UART_TypeDef *p_USI_UART = USI_PTR(priv->uart); while (!serial_writable(obj)); p_USI_UART->UTXH = c; } static int usi_serial_readable(void *obj) { struct serial_s *priv = (struct serial_s *)obj; S5JS100_USI_UART_TypeDef *p_USI_UART = USI_PTR(priv->uart); return (p_USI_UART->UFSTAT & (UART_UFSTAT_RX_FIFO_COUNT_MASK | UART_UFSTAT_RX_FIFO_FULL)); } static int usi_serial_getc(void *obj) { struct serial_s *priv = (struct serial_s *)obj; S5JS100_USI_UART_TypeDef *p_USI_UART = USI_PTR(priv->uart); int data; while (!serial_readable(obj)); data = p_USI_UART->URXH & 0xFF; return data; } #if DEVICE_SERIAL_FC static void usi_serial_set_flow_control(struct serial_s *obj, FlowControl type, PinName rxflow, PinName txflow) { struct serial_s *priv = (struct serial_s *)obj; S5JS100_USI_UART_TypeDef *p_USI_UART = USI_PTR(priv->uart); // Checked used UART name (UART_1, UART_2, ...) if (type == FlowControlRTSCTS) { p_USI_UART->UMCON = UART_UMCON_RTS_TRIG_14BYTES | UART_UMCON_AFC_ENABLE; } else { p_USI_UART->UMCON = UART_UMCON_AFC_DISABLE; } } #endif void usi_serial_init(void *obj, PinName tx, PinName rx) { struct serial_s *priv = (struct serial_s *)obj; S5JS100_USI_UART_TypeDef *p_USI_UART = USI_PTR(priv->uart); IRQn_Type irq_n = (IRQn_Type)0; uint32_t vector = 0; p_USI_UART->UINTM = 0xF; p_USI_UART->UINTP = 0xF; switch ((int)priv->uart) { case UART_0: if (! USI_VERSION_UART(S5JS100_USI0_REG->VERSION)) { /* UART NOT SUPPORTED */ MBED_ASSERT(false); } S5JS100_USI0_REG->CON = USI_CON_RST; S5JS100_SYSCFG_USI0_CONF = USI_CONFIG_UART; S5JS100_USI0_REG->CON &= ~USI_CON_RST; priv->index = USI0_PORT_ID; priv->rx_fifo_depth = USI_RXFIFO(S5JS100_USI0_REG->FIFO_DEPTH); priv->tx_fifo_depth = USI_TXFIFO(S5JS100_USI0_REG->FIFO_DEPTH); irq_n = S5JS100_IRQ_USI0; vector = (uint32_t)&uart0_irq; break; case UART_1: if (! USI_VERSION_UART(S5JS100_USI1_REG->VERSION)) { /* UART NOT SUPPORTED */ while (1); } S5JS100_USI1_REG->CON = USI_CON_RST; S5JS100_SYSCFG_USI1_CONF = USI_CONFIG_UART; S5JS100_USI1_REG->CON &= ~USI_CON_RST; priv->index = USI1_PORT_ID; priv-> rx_fifo_depth = USI_RXFIFO(S5JS100_USI1_REG->FIFO_DEPTH); priv-> tx_fifo_depth = USI_TXFIFO(S5JS100_USI1_REG->FIFO_DEPTH); irq_n = S5JS100_IRQ_USI1; vector = (uint32_t)&uart1_irq; break; } // Disable all interrupts and clear pendings p_USI_UART->UINTM = UART_UINTM_MODEM_MASK | UART_UINTM_TXD_MASK | UART_UINTM_RXD_MASK | UART_UINTM_ERROR_MASK; p_USI_UART->UINTP = UART_UINTP_MODEM | UART_UINTP_TXD | UART_UINTP_ERROR | UART_UINTP_RXD; // reset FIFO and set interrupt trigger level p_USI_UART->UFCON = UART_UFCON_TX_FIFO_TRIG_14BYTES | UART_UFCON_RX_FIFO_TRIG_14BYTES | UART_UFCON_TX_FIFO_RESET | UART_UFCON_RX_FIFO_RESET | UART_UFCON_FIFO_ENABLE ; //wait_ms(10); thread_sleep_for(10); //Enable TX/RX fifo int/poll mode with RX timeout of 32 bits duration p_USI_UART->UCON = UART_UCON_RX_TOUT_32FRAMES | UART_UCON_RX_TOUTINT_ENABLE | UART_UCON_TX_INTTYPE_LEVEL | UART_UCON_RX_INTTYPE_LEVEL | UART_UCON_TX_MODE_IRQPOLL | UART_UCON_RX_MODE_IRQPOLL; priv->ops.serial_baud = usi_serial_baud; priv->ops.serial_format = usi_serial_format; priv->ops.serial_irq_handler = usi_serial_irq_handler; priv->ops.serial_irq_set = usi_serial_irq_set; priv->ops.serial_putc = usi_serial_putc; priv->ops.serial_writable = usi_serial_writable; priv->ops.serial_getc = usi_serial_getc; priv->ops.serial_readable = usi_serial_readable; #if DEVICE_SERIAL_FC priv->ops.serial_set_flow_control = usi_serial_set_flow_control; #endif /* 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