mbed-os/targets/TARGET_Samsung/TARGET_SIDK_S5JS100/serial_usi_api.c

345 lines
9.8 KiB
C

/****************************************************************************
*
* 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