aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--hw/lpc-uart.c100
1 files changed, 74 insertions, 26 deletions
diff --git a/hw/lpc-uart.c b/hw/lpc-uart.c
index 979a617..10c5ed4 100644
--- a/hw/lpc-uart.c
+++ b/hw/lpc-uart.c
@@ -56,6 +56,7 @@ DEFINE_LOG_ENTRY(OPAL_RC_UART_INIT, OPAL_PLATFORM_ERR_EVT, OPAL_UART,
static struct lock uart_lock = LOCK_UNLOCKED;
static struct dt_node *uart_node;
static uint32_t uart_base;
+static uint64_t uart_tx_full_time;
static bool has_irq = false, irq_ok, rx_full, tx_full;
static uint8_t tx_room;
static uint8_t cached_ier;
@@ -95,28 +96,59 @@ static inline void uart_write(unsigned int reg, uint8_t val)
lpc_outb(val, uart_base + reg);
}
-static void uart_check_tx_room(void)
+static bool uart_check_tx_room(void)
{
+ if (tx_room)
+ return true;
+
if (uart_read(REG_LSR) & LSR_THRE) {
/* FIFO is 16 entries */
tx_room = 16;
tx_full = false;
+ return true;
}
+
+ return false;
}
-static void uart_wait_tx_room(void)
+/* Must be called with UART lock held */
+static void uart_write_thr(uint8_t val)
{
- while (!tx_room) {
- uart_check_tx_room();
- if (!tx_room) {
- smt_lowest();
- do {
- barrier();
- uart_check_tx_room();
- } while (!tx_room);
+ uart_write(REG_THR, val);
+
+ tx_room--;
+ if (tx_room == 0) {
+ if (!uart_check_tx_room())
+ uart_tx_full_time = mftb();
+ }
+}
+
+static bool uart_timed_out(unsigned long msecs)
+{
+ if (uart_check_tx_room())
+ return false;
+
+ if (tb_compare(mftb(), uart_tx_full_time + msecs_to_tb(msecs)) == TB_AAFTERB)
+ return true;
+
+ return false;
+}
+
+static bool uart_wait_tx_room(void)
+{
+ if (uart_check_tx_room())
+ return true;
+
+ smt_lowest();
+ while (!uart_check_tx_room()) {
+ if (uart_timed_out(100)) {
smt_medium();
+ return false;
}
}
+ smt_medium();
+
+ return true;
}
static void uart_update_ier(void)
@@ -160,18 +192,20 @@ static size_t uart_con_write(const char *buf, size_t len)
return written;
lock(&uart_lock);
- while(written < len) {
- if (tx_room == 0) {
- uart_wait_tx_room();
- if (tx_room == 0)
- goto bail;
- } else {
- uart_write(REG_THR, buf[written++]);
- tx_room--;
- }
+ while (written < len) {
+ if (!uart_wait_tx_room())
+ break;
+
+ uart_write_thr(buf[written++]);
+ }
+
+ if (!written && uart_timed_out(1000)) {
+ unlock(&uart_lock);
+ return len; /* swallow data */
}
- bail:
+
unlock(&uart_lock);
+
return written;
}
@@ -232,16 +266,19 @@ static int64_t uart_con_flush(void)
tx_full = true;
break;
}
- uart_write(REG_THR, out_buf[out_buf_cons++]);
+
+ uart_write_thr(out_buf[out_buf_cons++]);
out_buf_cons %= OUT_BUF_SIZE;
- tx_room--;
}
if (tx_full != tx_was_full)
uart_update_ier();
if (out_buf_prod != out_buf_cons) {
/* Return busy if nothing was flushed this call */
- if (out_buf_cons == out_buf_cons_initial)
+ if (out_buf_cons == out_buf_cons_initial) {
+ if (uart_timed_out(1000))
+ return OPAL_TIMEOUT;
return OPAL_BUSY;
+ }
/* Return partial if there's more to flush */
return OPAL_PARTIAL;
}
@@ -259,6 +296,7 @@ static int64_t uart_opal_write(int64_t term_number, __be64 *__length,
const uint8_t *buffer)
{
size_t written = 0, len = be64_to_cpu(*__length);
+ int64_t ret = OPAL_SUCCESS;
if (term_number != 0)
return OPAL_PARAMETER;
@@ -275,24 +313,34 @@ static int64_t uart_opal_write(int64_t term_number, __be64 *__length,
/* Flush out buffer again */
uart_con_flush();
+ if (!written && uart_timed_out(1000))
+ ret = OPAL_TIMEOUT;
unlock(&uart_lock);
*__length = cpu_to_be64(written);
- return OPAL_SUCCESS;
+ return ret;
}
static int64_t uart_opal_write_buffer_space(int64_t term_number,
__be64 *__length)
{
+ int64_t ret = OPAL_SUCCESS;
+ int64_t tx_buf_len;
+
if (term_number != 0)
return OPAL_PARAMETER;
lock(&uart_lock);
- *__length = cpu_to_be64(uart_tx_buf_space());
+ tx_buf_len = uart_tx_buf_space();
+
+ if ((tx_buf_len < be64_to_cpu(*__length)) && uart_timed_out(1000))
+ ret = OPAL_TIMEOUT;
+
+ *__length = cpu_to_be64(tx_buf_len);
unlock(&uart_lock);
- return OPAL_SUCCESS;
+ return ret;
}
/* Must be called with UART lock held */