* trans/bus_pci.c
* - PCI Bus Driver
*/
+#define DEBUG 1
#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
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];
+ int event_cb_wr_ofs;
+ int event_cb_rd_ofs;
+} 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);
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_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)
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);
{
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, new_channel);
+
+ 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;
+ 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;
+}
+
+void pci_intr_handler(int irq, void *void_context)
+{
+ pci_child_chan_context_t *context = void_context;
+
+ if( context->event_cb_rd_ofs == context->event_cb_wr_ofs ) {
+ // Dropped
+ return ;
+ }
+
+ udi_intr_event_cb_t *cb = context->event_cbs[context->event_cb_rd_ofs];
+ 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;
+
+ if( UDI_HANDLE_IS_NULL(context->intr_preprocessing, udi_pio_handle_t) )
+ {
+ udi_intr_event_ind(cb, 0);
+ }
+ else
+ {
+ // Processing
+ // - 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);
+ }
+}
+
+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);
+
+ cb->intr_result = result;
+
+ 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: {
+ Uint64 bar = PCI_GetBAR(pciid, regset_idx);
+ if(bar & 1)
+ {
+ // IO BAR
+ bar &= ~3;
+ #define _IO(fc, type) do {\
+ if( isOutput ) out##fc(bar+ofs, *(type*)data); \
+ else *(type*)data = in##fc(bar+ofs); \
+ } 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
+ bar = 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,
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,
};
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 = {