X-Git-Url: https://git.ucc.asn.au/?a=blobdiff_plain;f=KernelLand%2FModules%2FInterfaces%2FUDI%2Ftrans%2Fbus_pci.c;h=990965554acac3ceec30b5f6ebaea3270fa05f1d;hb=845b6f9d90bb87b5e760e4d49aa93b0e003ab750;hp=b586947725d30d5ec95fca40ae0d20f68d2ec59c;hpb=35f7d0523c463a6b6cc4d859c2e05234bdb0c6b8;p=tpg%2Facess2.git diff --git a/KernelLand/Modules/Interfaces/UDI/trans/bus_pci.c b/KernelLand/Modules/Interfaces/UDI/trans/bus_pci.c index b5869477..99096555 100644 --- a/KernelLand/Modules/Interfaces/UDI/trans/bus_pci.c +++ b/KernelLand/Modules/Interfaces/UDI/trans/bus_pci.c @@ -5,10 +5,17 @@ * trans/bus_pci.c * - PCI Bus Driver */ +#define DEBUG 0 +#define UDI_VERSION 0x101 +#define UDI_PHYSIO_VERSION 0x101 +#define UDI_PCI_VERSION 0x101 #include #include +#include #include #include // acess +#include +#include // === MACROS === /* Copied from http://projectudi.cvs.sourceforge.net/viewvc/projectudi/udiref/driver/udi_dpt/udi_dpt.h */ @@ -36,6 +43,10 @@ (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 @@ -45,6 +56,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); @@ -55,13 +86,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) { @@ -83,16 +143,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; } @@ -110,23 +189,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, @@ -137,28 +408,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 = { @@ -178,6 +460,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" "";