From: John Hodge Date: Sun, 9 Feb 2014 02:04:14 +0000 (+0800) Subject: UDI/uart_16c550 - RX support, apply erratum E20010702.1 (pci_base_class) X-Git-Url: https://git.ucc.asn.au/?a=commitdiff_plain;h=78c3ffa5189daa66d7096eb0170203d743288967;p=tpg%2Facess2.git UDI/uart_16c550 - RX support, apply erratum E20010702.1 (pci_base_class) --- diff --git a/UDI/drivers/uart_16c550/uart16c550.c b/UDI/drivers/uart_16c550/uart16c550.c index c67c8833..0f7bdc9f 100644 --- a/UDI/drivers/uart_16c550/uart16c550.c +++ b/UDI/drivers/uart_16c550/uart16c550.c @@ -118,7 +118,7 @@ void uart_bus_dev_bind__pio_map(udi_cb_t *gcb, udi_pio_handle_t new_pio_handle) rdata->interrupt_channel = new_channel; // Allocate an RX buffer - CONTIN( uart_bus_dev_bind, udi_buf_write, (NULL, RX_BUFFER_SIZE, NULL, 0, 0, UDI_NULL_BUF_PATH), + CONTIN( uart_bus_dev_bind, udi_buf_write, (NULL, 0, NULL, 0, 0, UDI_NULL_BUF_PATH), (udi_buf_t *new_buf)); if( !new_buf ) { @@ -156,12 +156,19 @@ void uart_bus_dev_intr_attach_ack(udi_intr_attach_cb_t *intr_attach_cb, udi_stat udi_cb_t *gcb = UDI_GCB(intr_attach_cb); rdata_t *rdata = gcb->context; - // TODO: Allocate interrupt cbs - CONTIN(uart_bus_dev_intr_attach_ack, udi_cb_alloc, (UART_CB_INTR_EVENT, rdata->interrupt_channel), - (udi_cb_t *new_cb)) + // Allocate interrupt cbs + CONTIN(uart_bus_dev_intr_attach_ack, udi_cb_alloc_batch, + (UART_CB_INTR_EVENT, NUM_INTR_CBS, TRUE, INTR_CB_BUF_SIZE, UDI_NULL_BUF_PATH), + (udi_cb_t *first_new_cb)) - udi_intr_event_cb_t *cb = UDI_MCB(new_cb, udi_intr_event_cb_t); - udi_intr_event_rdy(cb); + while( first_new_cb ) + { + udi_intr_event_cb_t *cb = UDI_MCB(first_new_cb, udi_intr_event_cb_t); + first_new_cb = first_new_cb->initiator_context; + // - Set channel (udi_cb_alloc_batch sets the channel to gcb->channel) + cb->gcb.channel = rdata->interrupt_channel; + udi_intr_event_rdy(cb); + } udi_channel_event_cb_t *channel_cb = UDI_MCB(rdata->active_cb, udi_channel_event_cb_t); @@ -177,18 +184,59 @@ void uart_bus_irq_channel_event_ind(udi_channel_event_cb_t *cb) } void uart_bus_irq_intr_event_ind(udi_intr_event_cb_t *cb, udi_ubit8_t flags) { + udi_cb_t *gcb = UDI_GCB(cb); + rdata_t *rdata = gcb->context; + udi_debug_printf("%s: flags=%x, intr_result=%x\n", __func__, flags, cb->intr_result); - if( cb->intr_result & 0x01 ) + if( cb->intr_result == 0 ) { - //ne2k_intr__rx_ok( UDI_GCB(cb) ); + // An IRQ from something other than RX + udi_intr_event_rdy(cb); + return ; } - // TODO: TX IRQs + + if( rdata->rx_buffer && rdata->rx_buffer->buf_size + cb->intr_result > MAX_RX_BUFFER_SIZE ) + { + // Drop, buffer is full + udi_debug_printf("%s: dropping %i bytes, full rx buffer\n", __func__, cb->intr_result); + udi_intr_event_rdy(cb); + return ; + } + + // Copy cb->intr_result bytes into the RX buffer + CONTIN(uart_bus_irq_intr_event_ind, udi_buf_copy, + (cb->event_buf, 0, cb->intr_result, + rdata->rx_buffer, rdata->rx_buffer->buf_size, 0, + UDI_NULL_BUF_PATH), + (udi_buf_t *new_buf)); + + udi_intr_event_cb_t *cb = UDI_MCB(gcb, udi_intr_event_cb_t); + + rdata->rx_buffer = new_buf; + + udi_debug_printf("%s: copied %i bytes\n", __func__, cb->intr_result); + + // Emit a signal to GIO client + if( rdata->event_cb && !rdata->event_cb_used ) + { + rdata->event_pending = FALSE; + rdata->event_cb_used = TRUE; + udi_gio_event_ind(rdata->event_cb); + } + else + { + udi_debug_printf("%s: event_cb=%p, event_cb_used=%i - Queueing\n", + __func__, rdata->event_cb, rdata->event_cb_used); + rdata->event_pending = TRUE; + } + udi_intr_event_rdy(cb); } // --- void uart_gio_prov_channel_event_ind(udi_channel_event_cb_t *cb); void uart_gio_prov_bind_req(udi_gio_bind_cb_t *cb); void uart_gio_prov_xfer_req(udi_gio_xfer_cb_t *cb); +void uart_gio_read_complete_short(udi_cb_t *gcb, udi_buf_t *new_buf); void uart_gio_read_complete(udi_cb_t *gcb, udi_buf_t *new_buf); void uart_gio_write_complete(udi_cb_t *gcb, udi_buf_t *new_buf, udi_status_t status, udi_ubit16_t res); void uart_gio_prov_event_res(udi_gio_event_cb_t *cb); @@ -198,10 +246,33 @@ void uart_gio_prov_channel_event_ind(udi_channel_event_cb_t *cb) } void uart_gio_prov_bind_req(udi_gio_bind_cb_t *cb) { + udi_cb_t *gcb = UDI_GCB(cb); + rdata_t *rdata = gcb->context; + + // TODO: Should this device allow multiple children? + if( rdata->event_cb ) { + udi_debug_printf("%s: only one client allowed\n", __func__); + udi_gio_bind_ack(cb, 0, 0, UDI_STAT_CANNOT_BIND_EXCL); + return; + } + + // Allocate the event CB + CONTIN( uart_gio_prov_bind_req, udi_cb_alloc, (UART_CB_GIO_EVENT, gcb->channel), + (udi_cb_t *new_cb)); + udi_gio_bind_cb_t *cb = UDI_MCB(gcb, udi_gio_bind_cb_t); + + rdata->event_cb = UDI_MCB(new_cb, udi_gio_event_cb_t); + rdata->event_cb_used = FALSE; + udi_gio_bind_ack(cb, 0, 0, UDI_OK); } void uart_gio_prov_unbind_req(udi_gio_bind_cb_t *cb) { + udi_cb_t *gcb = UDI_GCB(cb); + rdata_t *rdata = gcb->context; + + rdata->event_cb = NULL; + rdata->event_cb_used = FALSE; udi_gio_unbind_ack(cb); } void uart_gio_prov_xfer_req(udi_gio_xfer_cb_t *cb) @@ -210,21 +281,25 @@ void uart_gio_prov_xfer_req(udi_gio_xfer_cb_t *cb) rdata_t *rdata = gcb->context; switch(cb->op) { - case UDI_GIO_OP_READ: + case UDI_GIO_OP_READ: { + udi_buf_copy_call_t *callback; + udi_size_t len; // Read from local FIFO - if( rdata->rx_bytes < cb->data_buf->buf_size ) { - // Local FIFO was empty - udi_gio_xfer_nak(cb, UDI_STAT_DATA_UNDERRUN); + if( rdata->rx_buffer->buf_size < cb->data_buf->buf_size ) { + len = rdata->rx_buffer->buf_size; + callback = uart_gio_read_complete_short; } else { - udi_buf_copy(uart_gio_read_complete, gcb, - rdata->rx_buffer, - 0, cb->data_buf->buf_size, - cb->data_buf, - 0, cb->data_buf->buf_size, - UDI_NULL_BUF_PATH); + len = cb->data_buf->buf_size; + callback = uart_gio_read_complete; } - break; + udi_debug_printf("%s: Read %i/%i bytes\n", __func__, len, rdata->rx_buffer->buf_size); + // Replace entire destination buffer with `len` bytes from source + udi_buf_copy(callback, gcb, + rdata->rx_buffer, 0, len, + cb->data_buf, 0, cb->data_buf->buf_size, + UDI_NULL_BUF_PATH); + break; } case UDI_GIO_OP_WRITE: // Send to device udi_pio_trans(uart_gio_write_complete, gcb, @@ -235,16 +310,35 @@ void uart_gio_prov_xfer_req(udi_gio_xfer_cb_t *cb) break; } } +void uart_gio_read_complete_short(udi_cb_t *gcb, udi_buf_t *new_buf) +{ + rdata_t *rdata = gcb->context; + udi_gio_xfer_cb_t *cb = UDI_MCB(gcb, udi_gio_xfer_cb_t); + cb->data_buf = new_buf; + + // Delete read bytes from the RX buffer + CONTIN(uart_gio_read_complete, udi_buf_write, + (NULL, 0, rdata->rx_buffer, 0, cb->data_buf->buf_size, UDI_NULL_BUF_PATH), + (udi_buf_t *new_buf)) + udi_gio_xfer_cb_t *cb = UDI_MCB(gcb, udi_gio_xfer_cb_t); + rdata->rx_buffer = new_buf; + + // Return underrun + udi_gio_xfer_nak(cb, UDI_STAT_DATA_UNDERRUN); +} void uart_gio_read_complete(udi_cb_t *gcb, udi_buf_t *new_buf) { rdata_t *rdata = gcb->context; udi_gio_xfer_cb_t *cb = UDI_MCB(gcb, udi_gio_xfer_cb_t); cb->data_buf = new_buf; // Delete read bytes from the RX buffer - CONTIN(uart_gio_read_complete, udi_buf_write, (NULL, 0, rdata->rx_buffer, 0, cb->data_buf->buf_size, UDI_NULL_BUF_PATH), + CONTIN(uart_gio_read_complete, udi_buf_write, + (NULL, 0, rdata->rx_buffer, 0, cb->data_buf->buf_size, UDI_NULL_BUF_PATH), (udi_buf_t *new_buf)) udi_gio_xfer_cb_t *cb = UDI_MCB(gcb, udi_gio_xfer_cb_t); rdata->rx_buffer = new_buf; + + // Return completed udi_gio_xfer_ack(cb); } void uart_gio_write_complete(udi_cb_t *gcb, udi_buf_t *new_buf, udi_status_t status, udi_ubit16_t res) @@ -255,6 +349,20 @@ void uart_gio_write_complete(udi_cb_t *gcb, udi_buf_t *new_buf, udi_status_t sta } void uart_gio_prov_event_res(udi_gio_event_cb_t *cb) { + udi_cb_t *gcb = UDI_GCB(cb); + rdata_t *rdata = gcb->context; + + // If there was an event during dispatch, re-send + if( rdata->event_pending ) + { + udi_gio_event_ind(cb); + rdata->event_pending = FALSE; + } + // otherwise, clear 'Event CB used' flag + else + { + rdata->event_cb_used = FALSE; + } } // ==================================================================== @@ -325,6 +433,7 @@ udi_cb_init_t uart_cb_init_list[] = { {UART_CB_BUS_BIND, UART_META_BUS, UDI_BUS_BIND_CB_NUM, 0, 0,NULL}, {UART_CB_INTR, UART_META_BUS, UDI_BUS_INTR_ATTACH_CB_NUM, 0, 0,NULL}, {UART_CB_INTR_EVENT, UART_META_BUS, UDI_BUS_INTR_EVENT_CB_NUM, 0, 0,NULL}, + {UART_CB_GIO_EVENT, UART_META_GIO, UDI_GIO_EVENT_CB_NUM, 0, 0,NULL}, {0} }; const udi_init_t udi_init_info = { diff --git a/UDI/drivers/uart_16c550/uart16c550_common.h b/UDI/drivers/uart_16c550/uart16c550_common.h index 864f461b..a8a62c27 100644 --- a/UDI/drivers/uart_16c550/uart16c550_common.h +++ b/UDI/drivers/uart_16c550/uart16c550_common.h @@ -11,6 +11,7 @@ enum { UART_CB_BUS_BIND = 1, UART_CB_INTR, UART_CB_INTR_EVENT, + UART_CB_GIO_EVENT, }; enum { @@ -20,7 +21,9 @@ enum { N_PIO }; -#define RX_BUFFER_SIZE 32 +#define NUM_INTR_CBS 2 +#define INTR_CB_BUF_SIZE 16 // 16550 has a 16-byte fifo +#define MAX_RX_BUFFER_SIZE 32 typedef struct { udi_init_context_t init_context; @@ -33,8 +36,11 @@ typedef struct { udi_pio_handle_t pio_handles[N_PIO]; udi_channel_t interrupt_channel; - udi_ubit8_t rx_bytes; udi_buf_t *rx_buffer; + + udi_boolean_t event_cb_used; + udi_boolean_t event_pending; + udi_gio_event_cb_t *event_cb; } rdata_t; // === MACROS === diff --git a/UDI/drivers/uart_16c550/uart16c550_pio.h b/UDI/drivers/uart_16c550/uart16c550_pio.h index 850cb81d..f02d8f05 100644 --- a/UDI/drivers/uart_16c550/uart16c550_pio.h +++ b/UDI/drivers/uart_16c550/uart16c550_pio.h @@ -23,8 +23,6 @@ udi_pio_trans_t uart_pio_reset[] = { PIO_OUT_RI1(R0, 2), // +2 = 0xC7 - Clear FIFO, 14-byte threshold PIO_MOV_RI1(R0, 0x0B), PIO_OUT_RI1(R0, 4), // +4 = 0x0B - IRQs enabled, RTS/DSR set - PIO_MOV_RI1(R0, 0x0B), - PIO_OUT_RI1(R0, 1), // +1 = 0x05 - Enable ERBFI (Rx Full), ELSI (Line Status) {UDI_PIO_END, UDI_PIO_2BYTE, 0} }; // @@ -76,24 +74,51 @@ udi_pio_trans_t uart_pio_tx[] = { // udi_pio_trans_t uart_pio_intr[] = { // 0: Enable interrupts + PIO_MOV_RI1(R0, 0x09), + PIO_OUT_RI1(R0, 1), // +1 = EDSSI (Delta Line status), ELSI (Line Status), ERBFI (Rx Full) {UDI_PIO_END, UDI_PIO_2BYTE, 0}, // 1: Interrupt - // if( (inb(SERIAL_PORT+5) & 0x01) == 0 ) - // return -1; + // - Check if the interrupt was for this device, + // if so rx into buffer and return byte count // return inb(SERIAL_PORT); {UDI_PIO_LABEL, 0, 1}, + // if( (inb(SERIAL_PORT+5) & 0x01) == 0 ) { PIO_IN_RI1(R0, 5), PIO_op_RI(AND_IMM, R0, 1, 0x01), {UDI_PIO_CSKIP+UDI_PIO_R0, UDI_PIO_1BYTE, UDI_PIO_Z}, - {UDI_PIO_END, UDI_PIO_2BYTE, 0}, + {UDI_PIO_BRANCH, 0, 3}, + // SetUnclaimed() PIO_MOV_RI1(R2, 0), PIO_MOV_RI1(R1, UDI_INTR_UNCLAIMED), {UDI_PIO_STORE+UDI_PIO_SCRATCH+UDI_PIO_R2, UDI_PIO_1BYTE, UDI_PIO_R1}, - {UDI_PIO_END, UDI_PIO_2BYTE, 0}, + // return 0; + {UDI_PIO_END_IMM, UDI_PIO_2BYTE, 0}, + // } + // else { + {UDI_PIO_LABEL, 0, 3}, + // buf_ofs = 0; + PIO_MOV_RI2(R2, 0), // Buffer offset + // do { + {UDI_PIO_LABEL, 0, 4}, + // buffer[buf_ofs] = inb(SERIAL_PORT+0); + PIO_IN_RI1(R0, 0), + {UDI_PIO_STORE+UDI_PIO_BUF+UDI_PIO_R2, UDI_PIO_1BYTE, UDI_PIO_R0}, + // buf_ofs ++; + PIO_op_RI(ADD_IMM, R2, 1, 0x01), + // } while( inb(SERIAL_PORT+5) & 0x01 ); + PIO_IN_RI1(R0, 5), + PIO_op_RI(AND_IMM, R0, 1, 0x01), + {UDI_PIO_CSKIP+UDI_PIO_R0, UDI_PIO_1BYTE, UDI_PIO_Z}, + {UDI_PIO_BRANCH, 0, 4}, + // return 0; + {UDI_PIO_END, UDI_PIO_1BYTE, UDI_PIO_R2}, + // } // 2: Interrupt Overrun {UDI_PIO_LABEL, 0, 2}, - {UDI_PIO_END, UDI_PIO_2BYTE, 0} + PIO_MOV_RI1(R0, 0x08), // EDSSI only + PIO_OUT_RI1(R0, 1), + {UDI_PIO_END_IMM, UDI_PIO_2BYTE, 0} }; #define ARRAY_SIZEOF(arr) (sizeof(arr)/sizeof(arr[0])) diff --git a/UDI/drivers/uart_16c550/udiprops.txt b/UDI/drivers/uart_16c550/udiprops.txt index 7fb8577b..f8ca8761 100644 --- a/UDI/drivers/uart_16c550/udiprops.txt +++ b/UDI/drivers/uart_16c550/udiprops.txt @@ -28,11 +28,11 @@ device 102 1 bus_type string system sysbus_io_addr_lo ubit32 0x2F8 sysbus_io_ message 102 PC Serial (COM2) # Generic XT-Compatible Serial Controller -device 103 1 bus_type string pci pci_baseclass ubit32 0x07 pci_sub_class ubit32 0x00 pci_prog_if ubit32 0x00 +device 103 1 bus_type string pci pci_base_class ubit32 0x07 pci_sub_class ubit32 0x00 pci_prog_if ubit32 0x00 message 103 Generic XT-Compatible Serial Controller # PCI 16550 compatibles -device 104 1 bus_type string pci pci_baseclass ubit32 0x07 pci_sub_class ubit32 0x00 pci_prog_if ubit32 0x02 +device 104 1 bus_type string pci pci_base_class ubit32 0x07 pci_sub_class ubit32 0x00 pci_prog_if ubit32 0x02 message 104 PCI 16550 Compatible module uart16c550