UDI/uart_16c550 - RX support, apply erratum E20010702.1 (pci_base_class)
authorJohn Hodge <[email protected]>
Sun, 9 Feb 2014 02:04:14 +0000 (10:04 +0800)
committerJohn Hodge <[email protected]>
Sun, 9 Feb 2014 02:04:14 +0000 (10:04 +0800)
UDI/drivers/uart_16c550/uart16c550.c
UDI/drivers/uart_16c550/uart16c550_common.h
UDI/drivers/uart_16c550/uart16c550_pio.h
UDI/drivers/uart_16c550/udiprops.txt

index c67c883..0f7bdc9 100644 (file)
@@ -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 = {
index 864f461..a8a62c2 100644 (file)
@@ -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 ===
index 850cb81..f02d8f0 100644 (file)
@@ -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]))
index 7fb8577..f8ca876 100644 (file)
@@ -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

UCC git Repository :: git.ucc.asn.au