aboutsummaryrefslogtreecommitdiff
path: root/hw/lpc-uart.c
diff options
context:
space:
mode:
authorBenjamin Herrenschmidt <benh@kernel.crashing.org>2014-10-13 10:23:09 +1100
committerBenjamin Herrenschmidt <benh@kernel.crashing.org>2014-10-15 14:45:28 +1100
commitb6be61860788e0eaba95c4e2d87f1047923a49f6 (patch)
tree52e704b725a9657e8201f9be0fc6714efe4a34f8 /hw/lpc-uart.c
parent658e2b1558d446a4dd9effc24308cfbe8d16618f (diff)
downloadskiboot-b6be61860788e0eaba95c4e2d87f1047923a49f6.zip
skiboot-b6be61860788e0eaba95c4e2d87f1047923a49f6.tar.gz
skiboot-b6be61860788e0eaba95c4e2d87f1047923a49f6.tar.bz2
uart: Add interrupt driver output ring buffer for Linux ouput
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Diffstat (limited to 'hw/lpc-uart.c')
-rw-r--r--hw/lpc-uart.c126
1 files changed, 85 insertions, 41 deletions
diff --git a/hw/lpc-uart.c b/hw/lpc-uart.c
index 3d29d4c..51f744d 100644
--- a/hw/lpc-uart.c
+++ b/hw/lpc-uart.c
@@ -65,21 +65,6 @@ static bool has_irq, rx_full, tx_full;
static uint8_t tx_room;
static uint8_t cached_ier;
-/*
- * We implement a simple buffer to buffer input data as some bugs in
- * Linux make it fail to read fast enough after we get an interrupt.
- *
- * We use it on non-interrupt operations as well while at it because
- * it doesn't cost us much and might help in a few cases where Linux
- * is calling opal_poll_events() but not actually reading.
- *
- * Most of the time I expect we'll flush it completely to Linux into
- * it's tty flip buffers so I don't bother with a ring buffer.
- */
-#define IN_BUF_SIZE 0x1000
-static uint8_t *in_buf;
-static uint32_t in_count;
-
static void uart_trace(u8 ctx, u8 cnt, u8 irq_state, u8 in_count)
{
union trace t;
@@ -103,9 +88,11 @@ static inline void uart_write(unsigned int reg, uint8_t val)
static void uart_check_tx_room(void)
{
- if (uart_read(REG_LSR) & LSR_THRE)
+ if (uart_read(REG_LSR) & LSR_THRE) {
/* FIFO is 16 entries */
tx_room = 16;
+ tx_full = false;
+ }
}
static void uart_wait_tx_room(void)
@@ -164,30 +151,82 @@ static struct con_ops uart_con_driver = {
* OPAL console driver
*/
+/*
+ * We implement a simple buffer to buffer input data as some bugs in
+ * Linux make it fail to read fast enough after we get an interrupt.
+ *
+ * We use it on non-interrupt operations as well while at it because
+ * it doesn't cost us much and might help in a few cases where Linux
+ * is calling opal_poll_events() but not actually reading.
+ *
+ * Most of the time I expect we'll flush it completely to Linux into
+ * it's tty flip buffers so I don't bother with a ring buffer.
+ */
+#define IN_BUF_SIZE 0x1000
+static uint8_t *in_buf;
+static uint32_t in_count;
+
+/*
+ * We implement a ring buffer for output data as well to speed things
+ * up a bit. This allows us to have interrupt driven sends. This is only
+ * for the output data coming from the OPAL API, not the internal one
+ * which is already bufferred.
+ */
+#define OUT_BUF_SIZE 0x1000
+static uint8_t *out_buf;
+static uint8_t out_buf_prod;
+static uint8_t out_buf_cons;
+
+static void uart_flush_out(void)
+{
+ bool tx_was_full = tx_full;
+
+ while(out_buf_prod != out_buf_cons) {
+ if (tx_room == 0)
+ uart_check_tx_room();
+ if (tx_room == 0) {
+ tx_full = true;
+ break;
+ }
+ uart_write(REG_THR, out_buf[out_buf_cons++]);
+ out_buf_cons %= OUT_BUF_SIZE;
+ tx_room--;
+ }
+ if (tx_full != tx_was_full)
+ uart_update_ier();
+}
+
+static uint32_t uart_tx_buf_space(void)
+{
+ return OUT_BUF_SIZE - 1 -
+ (out_buf_prod + OUT_BUF_SIZE - out_buf_cons) % OUT_BUF_SIZE;
+}
+
static int64_t uart_opal_write(int64_t term_number, int64_t *length,
const uint8_t *buffer)
{
- size_t req = *length, written = 0;
+ size_t written = 0, len = *length;
if (term_number != 0)
return OPAL_PARAMETER;
lock(&uart_lock);
- while(written < req) {
- if (tx_room == 0) {
- uart_check_tx_room();
- if (tx_room == 0)
- goto bail;
- } else {
- uart_write(REG_THR, buffer[written++]);
- tx_room--;
- }
+
+ /* Copy data to out buffer */
+ while (uart_tx_buf_space() && len--) {
+ out_buf[out_buf_prod++] = *(buffer++);
+ out_buf_prod %= OUT_BUF_SIZE;
+ written++;
}
- bail:
+
+ /* Flush out buffer again */
+ uart_flush_out();
+
unlock(&uart_lock);
+
*length = written;
- return written ? OPAL_SUCCESS : OPAL_BUSY_EVENT;
+ return OPAL_SUCCESS;
}
static int64_t uart_opal_write_buffer_space(int64_t term_number,
@@ -197,9 +236,9 @@ static int64_t uart_opal_write_buffer_space(int64_t term_number,
return OPAL_PARAMETER;
lock(&uart_lock);
- uart_check_tx_room();
- *length = tx_room;
+ *length = uart_tx_buf_space();
unlock(&uart_lock);
+
return OPAL_SUCCESS;
}
@@ -285,29 +324,34 @@ static int64_t uart_opal_read(int64_t term_number, int64_t *length,
return OPAL_SUCCESS;
}
-static void uart_console_poll(void *data __unused)
+static void __uart_do_poll(u8 trace_ctx)
{
+ if (!in_buf)
+ return;
+
lock(&uart_lock);
uart_read_to_buffer();
+ uart_flush_out();
+ uart_trace(trace_ctx, 0, tx_full, in_count);
unlock(&uart_lock);
uart_adjust_opal_event();
}
-void uart_irq(void)
+static void uart_console_poll(void *data __unused)
{
- if (!in_buf)
- return;
-
- /* Grab what's in the UART and stash it into our buffer */
- lock(&uart_lock);
- uart_read_to_buffer();
- uart_trace(TRACE_UART_CTX_IRQ, 0, tx_full, in_count);
- unlock(&uart_lock);
+ __uart_do_poll(TRACE_UART_CTX_POLL);
+}
- uart_adjust_opal_event();
+void uart_irq(void)
+{
+ __uart_do_poll(TRACE_UART_CTX_IRQ);
}
+/*
+ * Common setup/inits
+ */
+
void uart_setup_linux_passthrough(void)
{
char *path;