/*
* Copyright (c) 2017, Intel Corporation
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the Intel Corporation nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* Author: Zhigang Wu <zhigang.wu@intel.com>
*/
#include <errno.h>
#include <reef/io.h>
#include <reef/dw-uart.h>
/* uart register list */
#define DW_REG_THR 0
#define DW_REG_RBR 0
#define DW_REG_BRDL 0
#define DW_REG_BRDH 4
#define DW_REG_FCR 8
#define DW_REG_LCR 12
#define DW_REG_LSR 20
#define uart_read(dev, reg)\
io_reg_read(dev->port + reg)
#define uart_write(dev, reg, value)\
io_reg_write(dev->port + reg, value)
/* lcr register */
/*
* 0x0 -- 5bits
* 0x1 -- 6bits
* 0x2 -- 7bits
* 0x3 -- 8bits
*/
#define LCR_DLS(x) (x << 0)
/* 0-1stop, 1-1.5stop */
#define LCR_STOP(x) (x << 2)
/* 0-parity disabled, 1-parity enabled */
#define LCR_PEN(x) (x << 3)
#define LCR_DLAB_BIT (1 << 7)
/* fcr register */
/* 0-fifo disabled; 1-enabled */
#define FCR_FIFOE(x) (x << 0)
/* 0-mode0, 1-mode1 */
#define FCR_MODE(x) (x << 3)
#define FCR_RCVR_RST (1 << 1
#define FCR_XMIT_RST (1 << 2)
/* lsr register */
#define LSR_TEMT (1 << 2) /* transmitter empty */
/* TODO: this goes in sof/uart.h */
struct uart *uart {
char *tx_buffer;
char *rx_buffer;
char *tx_read_ptr; /* read this char into HW */
char *tx_write_ptr; /* write this char into circular buffer */
char *rx_read_ptr;
char *rx_write_ptr;
int rx_buffer_size;
int tx_buffer_size;
void *private;
struct uart_pdata *pdata;
};
/* TODO: this goes in platform/uart.c */
static struct uart_pdata dw_pdata = {
.baud = 1152000, /* Baud rate */
.port = DW_REG_BASEADDR,
.timeout = DW_TIMEOUT,
};
/* All Tx/Rx is done by IRQ handler */
static int dw_uart_irq(void *data)
{
struct uart *uart = data;
/* TODO check we have new data to send */
/* check Tx is ready for next char */
if (uart_read(dev, DW_REG_LSR) & LSR_TEMT) {
/* write to output reg */
uart_write(dev, DW_REG_THR, uart->tx_read_ptr);
/* update tx read ptr and check for wrap */
uart->tx_read_ptr++;
if (uart->tx_read_ptr >= uart->rx_buffer + uart->rx_buffer_size)
uart->tx_read_ptr = uart->rx_buffer;
}
/* TODO check Rx HW for new data */
}
static int dw_uart_write_data(struct uart *uart, char *data, int len)
{
/* TODO copy data to Tx circular buffer */
}
static int dw_uart_read_data(struct uart *uart, char *data, int len)
{
/* TODO copy data from Rx circular buffer */
}
static int dw_uart_probe(struct uart *uart)
{
struct uart_pdata *pdata = uart->pdata;
uint32_t divisor, tmp;
/* TODO: register IRQ handler for Tx/Rx UART FIFO updates.*/
if (uart->baud != 0) {
divisor = (SUE_SYS_CLK_FREQ / baud) >> 4;
/* configure the baudrate */
tmp = uart_read(dev, DW_REG_LCR);
uart_write(dev, DW_REG_LCR, LCR_DLAB_BIT);
uart_write(dev, DW_REG_BRDL,
(uint8_t)(divisor & 0xFF));
uart_write(dev, DW_REG_BRDH,
(uint8_t)((divisor >> 8) & 0xFF));
/* restore to access baudrate divisor registers */
uart_write(dev, DW_REG_LCR, tmp);
}
/* TODO read this from pdata, i.e. stop bits, parity etc */
/* 8Bit-data, 1-stop bit, no parity, clear DLAB */
uart_write(dev, DW_REG_LCR,
LCR_DLS(3) | LCR_STOP(0) | LCR_PEN(0));
/* FIFO enalbe, mode0, Tx/Rx FIFO reset */
uart_write(dev, DW_REG_FCR,
FCR_FIFOE(1) | FCR_MODE(0) | FCR_RCVR_RST | FCR_XMIT_RST);
/* reset port */
uart_write(dev, DW_REG_RBR, 0);
return 0;
}
const struct uart_ops dw_uart_ops = {
.set_config = dw_uart_set_config,
.probe = dw_uart_probe,
.tx_data = dw_uart_write_data,
.rx_data = dw_uart_read_data,
};
@zhigang-wu please use this as starting point for UART, it must not block and should use IRQ for Tx/Rx data.