blob: a85fb280977bd6c293814a686e13a2b9d479518a [file] [log] [blame]
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
#include <assert.h>
#include <stdlib.h>
#include "os/mynewt.h"
#include "hal/hal_uart.h"
#include "bsp/bsp.h"
#include "mcu/mips_hal.h"
#include "mcu/pps.h"
#include <xc.h>
#define UxMODE(U) (base_address[U][0x0 / 0x4])
#define UxMODESET(U) (base_address[U][0x8 / 0x4])
#define UxSTA(U) (base_address[U][0x10 / 0x4])
#define UxTXREG(U) (base_address[U][0x20 / 0x4])
#define UxRXREG(U) (base_address[U][0x30 / 0x4])
#define UxBRG(U) (base_address[U][0x40 / 0x4])
static volatile uint32_t* base_address[UART_CNT] = {
(volatile uint32_t *)_UART1_BASE_ADDRESS,
(volatile uint32_t *)_UART2_BASE_ADDRESS,
(volatile uint32_t *)_UART3_BASE_ADDRESS,
(volatile uint32_t *)_UART4_BASE_ADDRESS,
(volatile uint32_t *)_UART5_BASE_ADDRESS,
(volatile uint32_t *)_UART6_BASE_ADDRESS
};
struct hal_uart {
volatile uint8_t u_rx_stall:1;
volatile uint8_t u_rx_data;
hal_uart_rx_char u_rx_func;
hal_uart_tx_char u_tx_func;
hal_uart_tx_done u_tx_done;
void *u_func_arg;
const struct mips_uart_cfg *u_pins;
};
static struct hal_uart uarts[UART_CNT];
int
hal_uart_init_cbs(int port, hal_uart_tx_char tx_func, hal_uart_tx_done tx_done,
hal_uart_rx_char rx_func, void *arg)
{
uarts[port].u_rx_func = rx_func;
uarts[port].u_tx_func = tx_func;
uarts[port].u_tx_done = tx_done;
uarts[port].u_func_arg = arg;
return 0;
}
static void
uart_disable_tx_int(int port)
{
switch (port) {
case 0:
IEC3CLR = _IEC3_U1TXIE_MASK;
break;
case 1:
IEC4CLR = _IEC4_U2TXIE_MASK;
break;
case 2:
IEC4CLR = _IEC4_U3TXIE_MASK;
break;
case 3:
IEC5CLR = _IEC5_U4TXIE_MASK;
break;
case 4:
IEC5CLR = _IEC5_U5TXIE_MASK;
break;
case 5:
IEC5CLR = _IEC5_U6TXIE_MASK;
break;
}
}
static void
uart_enable_tx_int(int port)
{
switch (port) {
case 0:
IEC3SET = _IEC3_U1TXIE_MASK;
break;
case 1:
IEC4SET = _IEC4_U2TXIE_MASK;
break;
case 2:
IEC4SET = _IEC4_U3TXIE_MASK;
break;
case 3:
IEC5SET = _IEC5_U4TXIE_MASK;
break;
case 4:
IEC5SET = _IEC5_U5TXIE_MASK;
break;
case 5:
IEC5SET = _IEC5_U6TXIE_MASK;
break;
}
}
static void
uart_disable_rx_int(int port)
{
switch (port) {
case 0:
IEC3CLR = _IEC3_U1RXIE_MASK;
break;
case 1:
IEC4CLR = _IEC4_U2RXIE_MASK;
break;
case 2:
IEC4CLR = _IEC4_U3RXIE_MASK;
break;
case 3:
IEC5CLR = _IEC5_U4RXIE_MASK;
break;
case 4:
IEC5CLR = _IEC5_U5RXIE_MASK;
break;
case 5:
IEC5CLR = _IEC5_U6RXIE_MASK;
break;
}
}
static void
uart_enable_rx_int(int port)
{
switch (port) {
case 0:
IEC3SET = _IEC3_U1RXIE_MASK;
break;
case 1:
IEC4SET = _IEC4_U2RXIE_MASK;
break;
case 2:
IEC4SET = _IEC4_U3RXIE_MASK;
break;
case 3:
IEC5SET = _IEC5_U4RXIE_MASK;
break;
case 4:
IEC5SET = _IEC5_U5RXIE_MASK;
break;
case 5:
IEC5SET = _IEC5_U6RXIE_MASK;
break;
}
}
static void
uart_receive_ready(int port)
{
uarts[port].u_rx_data = UxRXREG(port);
int c = uarts[port].u_rx_func(uarts[port].u_func_arg,
uarts[port].u_rx_data);
if (c < 0) {
uart_disable_rx_int(port);
uarts[port].u_rx_stall = 1;
}
}
static void
uart_transmit_ready(int port)
{
int c;
while(!(UxSTA(port) & _U1STA_UTXBF_MASK)) {
c = uarts[port].u_tx_func(uarts[port].u_func_arg);
if (c < 0) {
uart_disable_tx_int(port);
/* call tx done cb */
if (uarts[port].u_tx_done) {
uarts[port].u_tx_done(uarts[port].u_func_arg);
}
break;
}
UxTXREG(port) = (uint32_t)c & 0xff;
}
}
void
__attribute__((interrupt(IPL1AUTO), vector(_UART1_RX_VECTOR))) uart_1_rx_isr(void)
{
uart_receive_ready(0);
IFS3CLR = _IFS3_U1RXIF_MASK;
}
void
__attribute__((interrupt(IPL1AUTO), vector(_UART1_TX_VECTOR))) uart_1_tx_isr(void)
{
uart_transmit_ready(0);
IFS3CLR = _IFS3_U1TXIF_MASK;
}
void
__attribute__((interrupt(IPL1AUTO), vector(_UART2_RX_VECTOR))) uart_2_rx_isr(void)
{
uart_receive_ready(1);
IFS4CLR = _IFS4_U2RXIF_MASK;
}
void
__attribute__((interrupt(IPL1AUTO), vector(_UART2_TX_VECTOR))) uart_2_tx_isr(void)
{
uart_transmit_ready(1);
IFS4CLR = _IFS4_U2TXIF_MASK;
}
void
__attribute__((interrupt(IPL1AUTO), vector(_UART3_RX_VECTOR))) uart_3_rx_isr(void)
{
uart_receive_ready(2);
IFS4CLR = _IFS4_U3RXIF_MASK;
}
void
__attribute__((interrupt(IPL1AUTO), vector(_UART3_TX_VECTOR))) uart_3_tx_isr(void)
{
uart_transmit_ready(2);
IFS4CLR = _IFS4_U3TXIF_MASK;
}
void
__attribute__((interrupt(IPL1AUTO), vector(_UART4_RX_VECTOR))) uart_4_rx_isr(void)
{
uart_receive_ready(3);
IFS5CLR = _IFS5_U4RXIF_MASK;
}
void
__attribute__((interrupt(IPL1AUTO), vector(_UART4_TX_VECTOR))) uart_4_tx_isr(void)
{
uart_transmit_ready(3);
IFS5CLR = _IFS5_U4TXIF_MASK;
}
void
__attribute__((interrupt(IPL1AUTO), vector(_UART5_RX_VECTOR))) uart_5_rx_isr(void)
{
uart_receive_ready(4);
IFS5CLR = _IFS5_U5RXIF_MASK;
}
void
__attribute__((interrupt(IPL1AUTO), vector(_UART5_TX_VECTOR))) uart_5_tx_isr(void)
{
uart_transmit_ready(4);
IFS5CLR = _IFS5_U5TXIF_MASK;
}
void
__attribute__((interrupt(IPL1AUTO), vector(_UART6_RX_VECTOR))) uart_6_rx_isr(void)
{
uart_receive_ready(5);
IFS5CLR = _IFS5_U6RXIF_MASK;
}
void
__attribute__((interrupt(IPL1AUTO), vector(_UART6_TX_VECTOR))) uart_6_tx_isr(void)
{
uart_transmit_ready(5);
IFS5CLR = _IFS5_U6TXIF_MASK;
}
void
hal_uart_start_rx(int port)
{
if (uarts[port].u_rx_stall) {
/* recover saved data */
uint32_t sr;
__HAL_DISABLE_INTERRUPTS(sr);
int c = uarts[port].u_rx_func(uarts[port].u_func_arg,
uarts[port].u_rx_data);
if (c >= 0) {
uarts[port].u_rx_stall = 0;
/* enable RX interrupt */
uart_enable_rx_int(port);
}
__HAL_ENABLE_INTERRUPTS(sr);
}
}
void
hal_uart_start_tx(int port)
{
uart_enable_tx_int(port);
}
void
hal_uart_blocking_tx(int port, uint8_t data)
{
/* wait for transmit holding register to be empty */
while(!(UxSTA(port) & _U1STA_TRMT_MASK)) {
}
UxTXREG(port) = data;
}
int
hal_uart_init(int port, void *arg)
{
if (port >= UART_CNT) {
return -1;
}
uarts[port].u_pins = arg;
return 0;
}
int
hal_uart_config(int port, int32_t baudrate, uint8_t databits, uint8_t stopbits,
enum hal_uart_parity parity, enum hal_uart_flow_ctl flow_ctl)
{
uint32_t peripheral_clk = MYNEWT_VAL(CLOCK_FREQ) / 2;
// check input
if ((databits < 8) || (databits > 9) || (stopbits < 1) || (stopbits > 2)) {
return -1;
}
/* XXX: flow control currently unsupported */
(void) flow_ctl;
uarts[port].u_rx_stall = 0;
uint16_t mode = _U1MODE_BRGH_MASK | (stopbits >> 1);
switch (parity) {
case HAL_UART_PARITY_NONE:
if (databits == 9) {
mode |= _U1MODE_PDSEL_MASK;
}
break;
case HAL_UART_PARITY_ODD:
if (databits == 9) { // PIC does not do 9 bit data + parity.
return -1;
}
mode |= _U1MODE_PDSEL1_MASK;
break;
case HAL_UART_PARITY_EVEN:
if (databits == 9) {
return -1;
}
mode |= _U1MODE_PDSEL0_MASK;
break;
default:
return -1;
}
/* Configure TX/RX pins */
if (uarts[port].u_pins) {
int ret = 0;
switch(port) {
case 0:
ret += pps_configure_output(uarts[port].u_pins->tx, U1TX_OUT_FUNC);
ret += pps_configure_input(uarts[port].u_pins->rx, U1RX_IN_FUNC);
break;
case 1:
ret += pps_configure_output(uarts[port].u_pins->tx, U2TX_OUT_FUNC);
ret += pps_configure_input(uarts[port].u_pins->rx, U2RX_IN_FUNC);
break;
case 2:
ret += pps_configure_output(uarts[port].u_pins->tx, U3TX_OUT_FUNC);
ret += pps_configure_input(uarts[port].u_pins->rx, U3RX_IN_FUNC);
break;
case 3:
ret += pps_configure_output(uarts[port].u_pins->tx, U4TX_OUT_FUNC);
ret += pps_configure_input(uarts[port].u_pins->rx, U4RX_IN_FUNC);
break;
case 4:
ret += pps_configure_output(uarts[port].u_pins->tx, U5TX_OUT_FUNC);
ret += pps_configure_input(uarts[port].u_pins->rx, U5RX_IN_FUNC);
break;
case 5:
ret += pps_configure_output(uarts[port].u_pins->tx, U6TX_OUT_FUNC);
ret += pps_configure_input(uarts[port].u_pins->rx, U6RX_IN_FUNC);
break;
default:
return -1;
}
if (ret) {
return -1;
}
}
uint16_t divisor = peripheral_clk / (4 * baudrate) - 1;
/* disable */
UxMODE(port) = 0;
__asm__("nop");
UxBRG(port) = divisor;
UxMODE(port) = mode;
UxSTA(port) = _U1STA_URXEN_MASK | _U1STA_UTXEN_MASK;
switch (port) {
case 0:
/* clear RX interrupt flag */
IFS3CLR = _IFS3_U1RXIF_MASK;
/* enable RX interrupt */
IEC3SET = _IEC3_U1RXIE_MASK;
/* set rx interrupt priority */
IPC28CLR = _IPC28_U1RXIP_MASK;
IPC28SET = (1 << _IPC28_U1RXIP_POSITION); // priority 1
/* set rx interrupt subpriority */
IPC28CLR = _IPC28_U1RXIS_MASK;
IPC28SET = (0 << _IPC28_U1RXIS_POSITION); // subpriority 0
/* set tx interrupt priority */
IPC28CLR = _IPC28_U1TXIP_MASK;
IPC28SET = (1 << _IPC28_U1TXIP_POSITION); // priority 1
/* set tx interrupt subpriority */
IPC28CLR = _IPC28_U1TXIS_MASK;
IPC28SET = (0 << _IPC28_U1TXIS_POSITION); // subpriority 0
break;
case 1:
/* clear RX interrupt flag */
IFS4CLR = _IFS4_U2RXIF_MASK;
/* enable RX interrupt */
IEC4SET = _IEC4_U2RXIE_MASK;
/* set rx interrupt priority */
IPC36CLR = _IPC36_U2RXIP_MASK;
IPC36SET = (1 << _IPC36_U2RXIP_POSITION); // priority 1
/* set rx interrupt subpriority */
IPC36CLR = _IPC36_U2RXIS_MASK;
IPC36SET = (0 << _IPC36_U2RXIS_POSITION); // subpriority 0
/* set tx interrupt priority */
IPC36CLR = _IPC36_U2TXIP_MASK;
IPC36SET = (1 << _IPC36_U2TXIP_POSITION); // priority 1
/* set tx interrupt subpriority */
IPC36CLR = _IPC36_U2TXIS_MASK;
IPC36SET = (0 << _IPC36_U2TXIS_POSITION); // subpriority 0
break;
case 2:
/* clear RX interrupt flag */
IFS4CLR = _IFS4_U3RXIF_MASK;
/* enable RX interrupt */
IEC4SET = _IEC4_U3RXIE_MASK;
/* set rx interrupt priority */
IPC39CLR = _IPC39_U3RXIP_MASK;
IPC39SET = (1 << _IPC39_U3RXIP_POSITION); // priority 1
/* set rx interrupt subpriority */
IPC39CLR = _IPC39_U3RXIS_MASK;
IPC39SET = (0 << _IPC39_U3RXIS_POSITION); // subpriority 0
/* set tx interrupt priority */
IPC39CLR = _IPC39_U3TXIP_MASK;
IPC39SET = (1 << _IPC39_U3TXIP_POSITION); // priority 1
/* set tx interrupt subpriority */
IPC39CLR = _IPC39_U3TXIS_MASK;
IPC39SET = (0 << _IPC39_U3TXIS_POSITION); // subpriority 0
break;
case 3:
/* clear RX interrupt flag */
IFS5CLR = _IFS5_U4RXIF_MASK;
/* enable RX interrupt */
IEC5SET = _IEC5_U4RXIE_MASK;
/* set rx interrupt priority */
IPC42CLR = _IPC42_U4RXIP_MASK;
IPC42SET = (1 << _IPC42_U4RXIP_POSITION); // priority 1
/* set rx interrupt subpriority */
IPC42CLR = _IPC42_U4RXIS_MASK;
IPC42SET = (0 << _IPC42_U4RXIS_POSITION); // subpriority 0
/* set tx interrupt priority */
IPC43CLR = _IPC43_U4TXIP_MASK;
IPC43SET = (1 << _IPC43_U4TXIP_POSITION); // priority 1
/* set tx interrupt subpriority */
IPC43CLR = _IPC43_U4TXIS_MASK;
IPC43SET = (0 << _IPC43_U4TXIS_POSITION); // subpriority 0
break;
case 4:
/* clear RX interrupt flag */
IFS5CLR = _IFS5_U5RXIF_MASK;
/* enable RX interrupt */
IEC5SET = _IEC5_U5RXIE_MASK;
/* set rx interrupt priority */
IPC45CLR = _IPC45_U5RXIP_MASK;
IPC45SET = (1 << _IPC45_U5RXIP_POSITION); // priority 1
/* set rx interrupt subpriority */
IPC45CLR = _IPC45_U5RXIS_MASK;
IPC45SET = (0 << _IPC45_U5RXIS_POSITION); // subpriority 0
/* set tx interrupt priority */
IPC45CLR = _IPC45_U5TXIP_MASK;
IPC45SET = (1 << _IPC45_U5TXIP_POSITION); // priority 1
/* set tx interrupt subpriority */
IPC45CLR = _IPC45_U5TXIS_MASK;
IPC45SET = (0 << _IPC45_U5TXIS_POSITION); // subpriority 0
break;
case 5:
/* clear RX interrupt flag */
IFS5CLR = _IFS5_U6RXIF_MASK;
/* enable RX interrupt */
IEC5SET = _IEC5_U6RXIE_MASK;
/* set rx interrupt priority */
IPC47CLR = _IPC47_U6RXIP_MASK;
IPC47SET = (1 << _IPC47_U6RXIP_POSITION); // priority 1
/* set rx interrupt subpriority */
IPC47CLR = _IPC47_U6RXIS_MASK;
IPC47SET = (0 << _IPC47_U6RXIS_POSITION); // subpriority 0
/* set tx interrupt priority */
IPC47CLR = _IPC47_U6TXIP_MASK;
IPC47SET = (1 << _IPC47_U6TXIP_POSITION); // priority 1
/* set tx interrupt subpriority */
IPC47CLR = _IPC47_U6TXIS_MASK;
IPC47SET = (0 << _IPC47_U6TXIS_POSITION); // subpriority 0
break;
}
UxMODESET(port) = _U1MODE_ON_MASK;
return 0;
}
int
hal_uart_close(int port)
{
if (port >= UART_CNT) {
return -1;
}
UxMODE(port) = 0;
uart_disable_rx_int(port);
return 0;
}