Kernel/UDI - Fix omission of UDI_PCI_VERSION in bus_pci.c
[tpg/acess2.git] / KernelLand / Modules / Interfaces / UDI / trans / bus_pci.c
index 96e1462..651329d 100644 (file)
@@ -5,9 +5,15 @@
  * trans/bus_pci.c
  * - PCI Bus Driver
  */
+#define DEBUG  0
+#define UDI_PCI_VERSION        0x101
 #include <udi.h>
 #include <udi_physio.h>
+#include <udi_pci.h>
+#include <acess.h>
 #include <drv_pci.h>   // acess
+#include <udi_internal.h>
+#include <trans_pci.h>
 
 // === MACROS ===
 /* Copied from http://projectudi.cvs.sourceforge.net/viewvc/projectudi/udiref/driver/udi_dpt/udi_dpt.h */
                (attr)->attr_length = (len); \
                udi_strncpy_rtrim((char *)(attr)->attr_value, (val), (len))
 
+#define PCI_OPS_BRIDGE 1
+#define PCI_OPS_IRQ    2
+
+#define PCI_MAX_EVENT_CBS      8
 
 // === TYPES ===
 typedef struct
@@ -44,6 +54,26 @@ typedef struct
        tPCIDev cur_iter;
 } pci_rdata_t;
 
+typedef struct
+{
+       udi_child_chan_context_t        child_chan_context;
+       
+       udi_channel_t   interrupt_channel;
+       struct {
+               tPAddr  paddr;
+               void    *vaddr;
+               size_t  length;
+       } bars[6];
+
+        int    interrupt_handle;
+
+       udi_pio_handle_t        intr_preprocessing;
+       udi_intr_event_cb_t     *event_cbs[PCI_MAX_EVENT_CBS];
+       udi_index_t     event_cb_wr_ofs;
+       udi_index_t     event_cb_rd_ofs;
+        int    bIntrEnabled;
+} pci_child_chan_context_t;
+
 // === PROTOTYPES ===
 void   pci_usage_ind(udi_usage_cb_t *cb, udi_ubit8_t resource_level);
 void   pci_enumerate_req(udi_enumerate_cb_t *cb, udi_ubit8_t enumeration_level);
@@ -54,13 +84,42 @@ void        pci_bridge_ch_event_ind(udi_channel_event_cb_t *cb);
 void   pci_unbind_req(udi_bus_bind_cb_t *cb);
 void   pci_bind_req_op(udi_bus_bind_cb_t *cb);
 void   pci_intr_attach_req(udi_intr_attach_cb_t *cb);
+void   pci_intr_attach_req__channel_spawned(udi_cb_t *gcb, udi_channel_t new_channel);
 void   pci_intr_detach_req(udi_intr_detach_cb_t *cb);
 
+void   pci_intr_ch_event_ind(udi_channel_event_cb_t *cb);
+void   pci_intr_event_rdy(udi_intr_event_cb_t *cb);
+void   pci_intr_event_rdy__irqs_enabled(udi_cb_t *gcb, udi_buf_t *newbuf, udi_status_t status, udi_ubit16_t result);
+void   pci_intr_handler(int irq, void *void_context);
+void   pci_intr_handle__trans_done(udi_cb_t *gcb, udi_buf_t *new_buf, udi_status_t status, udi_ubit16_t result);
+
+// - Hook to physio (UDI doesn't define these)
+ int   pci_pio_get_regset(udi_cb_t *gcb, udi_ubit32_t regset_idx, void **baseptr, size_t *lenptr);
 
 // === CODE ===
 void pci_usage_ind(udi_usage_cb_t *cb, udi_ubit8_t resource_level)
 {
-       UNIMPLEMENTED();
+       pci_rdata_t     *rdata = UDI_GCB(cb)->context;
+       
+       switch(cb->meta_idx)
+       {
+       case 1: // mgmt
+               break;
+       }
+
+       switch(resource_level)
+       {
+       case UDI_RESOURCES_CRITICAL:
+       case UDI_RESOURCES_LOW:
+       case UDI_RESOURCES_NORMAL:
+       case UDI_RESOURCES_PLENTIFUL:
+               break;
+       }
+
+       // TODO: Initialise rdata
+       rdata->cur_iter = -1;
+
+       udi_usage_res(cb);
 }
 void pci_enumerate_req(udi_enumerate_cb_t *cb, udi_ubit8_t enumeration_level)
 {
@@ -82,16 +141,35 @@ void pci_enumerate_req(udi_enumerate_cb_t *cb, udi_ubit8_t enumeration_level)
                        Uint16  ven, dev;
                        Uint32  class;
                        PCI_GetDeviceInfo(rdata->cur_iter, &ven, &dev, &class);
+                       Uint8   revision;
+                       PCI_GetDeviceVersion(rdata->cur_iter, &revision);
+                       Uint16  sven, sdev;
+                       PCI_GetDeviceSubsys(rdata->cur_iter, &sven, &sdev);
 
+                       udi_strcpy(attr_list->attr_name, "identifier");
+                       attr_list->attr_length = sprintf((char*)attr_list->attr_value,
+                               "%04x%04x%02x%04x%04x",
+                               ven, dev, revision, sven, sdev);
+                       attr_list ++;
                        DPT_SET_ATTR_STRING(attr_list, "bus_type", "pci", 3);
                        attr_list ++;
                        DPT_SET_ATTR32(attr_list, "pci_vendor_id", ven);
                        attr_list ++;
                        DPT_SET_ATTR32(attr_list, "pci_device_id", dev);
                        attr_list ++;
+                       
+                       DPT_SET_ATTR32(attr_list, "pci_baseclass", class >> 16);
+                       attr_list ++;
+                       DPT_SET_ATTR32(attr_list, "pci_base_class", class >> 16);       // E20010702.1
+                       attr_list ++;
+                       DPT_SET_ATTR32(attr_list, "pci_sub_class", (class >> 8) & 0xFF);
+                       attr_list ++;
+                       DPT_SET_ATTR32(attr_list, "pci_prog_if", (class >> 0) & 0xFF);
+                       attr_list ++;
 
                        cb->attr_valid_length = attr_list - cb->attr_list;
-                       udi_enumerate_ack(cb, UDI_ENUMERATE_OK, 0);
+                       cb->child_ID = rdata->cur_iter;
+                       udi_enumerate_ack(cb, UDI_ENUMERATE_OK, 1);
                }
                break;
        }
@@ -109,23 +187,215 @@ void pci_bridge_ch_event_ind(udi_channel_event_cb_t *cb)
 {
        UNIMPLEMENTED();
 }
-void pci_unbind_req(udi_bus_bind_cb_t *cb)
+void pci_bind_req(udi_bus_bind_cb_t *cb)
 {
-       UNIMPLEMENTED();
+       // TODO: "Lock" PCI device
+
+       // TODO: DMA constraints
+       udi_bus_bind_ack(cb, 0, UDI_DMA_LITTLE_ENDIAN, UDI_OK);
 }
-void pci_bind_req_op(udi_bus_bind_cb_t *cb)
+void pci_unbind_req(udi_bus_bind_cb_t *cb)
 {
        UNIMPLEMENTED();
 }
 void pci_intr_attach_req(udi_intr_attach_cb_t *cb)
 {
-       UNIMPLEMENTED();
+       pci_child_chan_context_t *context = UDI_GCB(cb)->context;
+
+       ASSERT(cb->interrupt_idx == 0); 
+
+       context->intr_preprocessing = cb->preprocessing_handle;
+       // Check if interrupt is already bound
+       if( !UDI_HANDLE_IS_NULL(context->interrupt_channel, udi_channel_t) )
+       {
+               udi_intr_attach_ack(cb, UDI_OK);
+               return ;
+       }
+       // Create a channel
+       udi_channel_spawn(pci_intr_attach_req__channel_spawned, UDI_GCB(cb),
+               cb->gcb.channel, cb->interrupt_idx, PCI_OPS_IRQ, context);
+}
+void pci_intr_attach_req__channel_spawned(udi_cb_t *gcb, udi_channel_t new_channel)
+{
+       udi_intr_attach_cb_t *cb = UDI_MCB(gcb, udi_intr_attach_cb_t);
+       pci_child_chan_context_t *context = UDI_GCB(cb)->context;
+
+       if( UDI_HANDLE_IS_NULL(new_channel, udi_channel_t) )
+       {
+               // oops
+               return ;
+       }       
+
+       context->interrupt_channel = new_channel;
+       
+       context->interrupt_handle = IRQ_AddHandler(
+               PCI_GetIRQ(context->child_chan_context.child_ID),
+               pci_intr_handler, context);
+
+       udi_intr_attach_ack(cb, UDI_OK);
 }
 void pci_intr_detach_req(udi_intr_detach_cb_t *cb)
 {
        UNIMPLEMENTED();
 }
 
+void pci_intr_ch_event_ind(udi_channel_event_cb_t *cb)
+{
+       UNIMPLEMENTED();
+}
+void pci_intr_event_rdy(udi_intr_event_cb_t *cb)
+{
+       pci_child_chan_context_t        *context = UDI_GCB(cb)->context;
+
+       ASSERTC(context->event_cb_rd_ofs, <, PCI_MAX_EVENT_CBS);
+       ASSERTC(context->event_cb_wr_ofs, <, PCI_MAX_EVENT_CBS);
+
+       LOG("Rd %i, Wr %i [WR %p{%p}]", context->event_cb_rd_ofs, context->event_cb_wr_ofs, cb, cb->event_buf);
+       if( context->event_cbs[context->event_cb_wr_ofs] )
+       {
+               // oops, overrun.
+               return ;
+       }
+       context->event_cbs[context->event_cb_wr_ofs++] = cb;
+       if( context->event_cb_wr_ofs == PCI_MAX_EVENT_CBS )
+               context->event_cb_wr_ofs = 0;
+       
+       // TODO: Fire once >= min_event_pend CBs are recieved
+       if( !context->bIntrEnabled )
+       {
+               context->bIntrEnabled = 1;
+               udi_pio_trans(pci_intr_event_rdy__irqs_enabled, NULL, context->intr_preprocessing, 0, NULL, NULL);
+       }
+}
+void pci_intr_event_rdy__irqs_enabled(udi_cb_t *gcb, udi_buf_t *newbuf, udi_status_t status, udi_ubit16_t result)
+{
+       // Do nothing
+}
+
+void pci_intr_handler(int irq, void *void_context)
+{
+       pci_child_chan_context_t *context = void_context;
+
+       LOG("irq=%i, context=%p", irq, context);
+
+       if( context->event_cb_rd_ofs == context->event_cb_wr_ofs ) {
+               // Dropped
+               return ;
+       }
+
+       ASSERTC(context->event_cb_rd_ofs, <, PCI_MAX_EVENT_CBS);
+       ASSERTC(context->event_cb_wr_ofs, <, PCI_MAX_EVENT_CBS);
+
+       udi_intr_event_cb_t *cb = context->event_cbs[context->event_cb_rd_ofs];
+       LOG("Rd %i, Wr %i [RD %p]", context->event_cb_rd_ofs, context->event_cb_wr_ofs, cb);
+       context->event_cbs[context->event_cb_rd_ofs] = NULL;
+       context->event_cb_rd_ofs ++;
+       if( context->event_cb_rd_ofs == PCI_MAX_EVENT_CBS )
+               context->event_cb_rd_ofs = 0;
+       ASSERT(cb);
+       ASSERT(cb->gcb.scratch);
+       
+       if( UDI_HANDLE_IS_NULL(context->intr_preprocessing, udi_pio_handle_t) )
+       {
+               // TODO: Ensure region is an interrupt region
+               udi_intr_event_ind(cb, 0);
+       }
+       else
+       {
+               // Processing
+               *(udi_ubit8_t*)(cb->gcb.scratch) = 0;
+               // - no event info, so mem_ptr=NULL
+               udi_pio_trans(pci_intr_handle__trans_done, UDI_GCB(cb),
+                       context->intr_preprocessing, 1, cb->event_buf, NULL);
+               // V V V
+       }
+}
+
+void pci_intr_handle__trans_done(udi_cb_t *gcb, udi_buf_t *new_buf, udi_status_t status, udi_ubit16_t result)
+{
+       udi_intr_event_cb_t *cb = UDI_MCB(gcb, udi_intr_event_cb_t);
+       LOG("cb(%p)->event_buf=%p, new_buf=%p",
+               cb, cb->event_buf, new_buf);
+       
+       // TODO: Buffers should not change
+       cb->event_buf = new_buf;
+       cb->intr_result = result;
+       
+       udi_ubit8_t     intr_status = *(udi_ubit8_t*)(gcb->scratch);
+       if( intr_status & UDI_INTR_UNCLAIMED )
+       {
+               // Not claimed, next please.
+               // NOTE: Same as no event in the acess model
+               LOG("Unclaimed");
+               pci_intr_event_rdy(cb);
+       }
+       else if( intr_status & UDI_INTR_NO_EVENT )
+       {
+               // No event should be generated, return cb to pool
+               // EVIL!
+               pci_intr_event_rdy(cb);
+               LOG("No event, return cb to pool");
+       }
+       else
+       {
+               LOG("Inform driver");
+               udi_intr_event_ind(cb, UDI_INTR_PREPROCESSED);  
+       }
+}
+
+// - physio hooks
+udi_status_t pci_pio_do_io(uint32_t child_ID, udi_ubit32_t regset_idx, udi_ubit32_t ofs, udi_ubit8_t len,
+       void *data, bool isOutput)
+{
+//     LOG("child_ID=%i, regset_idx=%i,ofs=0x%x,len=%i,data=%p,isOutput=%b",
+//             child_ID, regset_idx, ofs, len, data, isOutput);
+       tPCIDev pciid = child_ID;
+       // TODO: Cache child mappings   
+
+       switch(regset_idx)
+       {
+       case UDI_PCI_CONFIG_SPACE:
+               // TODO:
+               return UDI_STAT_NOT_SUPPORTED;
+       case UDI_PCI_BAR_0 ... UDI_PCI_BAR_5: {
+               Uint32 bar = PCI_GetBAR(pciid, regset_idx);
+               if(bar & 1)
+               {
+                       // IO BAR
+                       bar &= ~3;
+                       #define _IO(fc, type) do {\
+                               if( isOutput ) { \
+                                       /*LOG("out"#fc"(0x%x, 0x%x)",bar+ofs,*(type*)data);*/\
+                                       out##fc(bar+ofs, *(type*)data); \
+                               } \
+                               else { \
+                                       *(type*)data = in##fc(bar+ofs); \
+                                       /*LOG("in"#fc"(0x%x) = 0x%x",bar+ofs,*(type*)data);*/\
+                               }\
+                               } while(0)
+                       switch(len)
+                       {
+                       case UDI_PIO_1BYTE:     _IO(b, udi_ubit8_t);    return UDI_OK;
+                       case UDI_PIO_2BYTE:     _IO(w, udi_ubit16_t);   return UDI_OK;
+                       case UDI_PIO_4BYTE:     _IO(d, udi_ubit32_t);   return UDI_OK;
+                       //case UDI_PIO_8BYTE:   _IO(q, uint64_t);       return UDI_OK;
+                       default:
+                               return UDI_STAT_NOT_SUPPORTED;
+                       }
+                       #undef _IO
+               }
+               else
+               {
+                       // Memory BAR
+                       //Uint64 longbar = PCI_GetValidBAR(pciid, regset_idx, PCI_BARTYPE_MEM);
+                       return UDI_STAT_NOT_SUPPORTED;
+               }
+               break; }
+       default:
+               return UDI_STAT_NOT_UNDERSTOOD;
+       }
+}
+
 // === UDI Functions ===
 udi_mgmt_ops_t pci_mgmt_ops = {
        pci_usage_ind,
@@ -136,28 +406,39 @@ udi_mgmt_ops_t    pci_mgmt_ops = {
 udi_ubit8_t    pci_mgmt_op_flags[4] = {0,0,0,0};
 udi_bus_bridge_ops_t   pci_bridge_ops = {
        pci_bridge_ch_event_ind,
+       pci_bind_req,
        pci_unbind_req,
-       pci_bind_req_op,
        pci_intr_attach_req,
        pci_intr_detach_req
 };
 udi_ubit8_t    pci_bridge_op_flags[5] = {0,0,0,0,0};
+udi_intr_dispatcher_ops_t      pci_irq_ops = {
+       pci_intr_ch_event_ind,
+       pci_intr_event_rdy
+};
+udi_ubit8_t    pci_irq_ops_flags[2] = {0,0};
 udi_primary_init_t     pci_pri_init = {
        .mgmt_ops = &pci_mgmt_ops,
        .mgmt_op_flags = pci_mgmt_op_flags,
        .mgmt_scratch_requirement = 0,
-       .enumeration_attr_list_length = 4,
+       .enumeration_attr_list_length = 7,
        .rdata_size = sizeof(pci_rdata_t),
        .child_data_size = 0,
        .per_parent_paths = 0
 };
 udi_ops_init_t pci_ops_list[] = {
        {
-               1, 1, UDI_BUS_BRIDGE_OPS_NUM,
-               0,
+               PCI_OPS_BRIDGE, 1, UDI_BUS_BRIDGE_OPS_NUM,
+               sizeof(pci_child_chan_context_t),
                (udi_ops_vector_t*)&pci_bridge_ops,
                pci_bridge_op_flags
        },
+       {
+               PCI_OPS_IRQ, 1, UDI_BUS_INTR_DISPATCH_OPS_NUM,
+               0,
+               (udi_ops_vector_t*)&pci_irq_ops,
+               pci_irq_ops_flags
+       },
        {0}
 };
 udi_init_t     pci_init = {
@@ -177,6 +458,7 @@ const char  pci_udiprops[] =
        "requires udi 0x101\0"
        "provides udi_bridge 0x101\0"
        "meta 1 udi_bridge\0"
+       "enumerates 4 0 100 1 bus_name string pci\0"
        "region 0\0"
        "child_bind_ops 1 0 1\0"
        "";

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