Merge branch 'master' of github.com:thepowersgang/acess2
[tpg/acess2.git] / KernelLand / Modules / Interfaces / UDI / trans / bus_pci.c
1 /*
2  * Acess2 UDI Layer
3  * - By John Hodge (thePowersGang)
4  *
5  * trans/bus_pci.c
6  * - PCI Bus Driver
7  */
8 #define DEBUG   0
9 #define UDI_VERSION     0x101
10 #define UDI_PHYSIO_VERSION      0x101
11 #define UDI_PCI_VERSION 0x101
12 #include <udi.h>
13 #include <udi_physio.h>
14 #include <udi_pci.h>
15 #include <acess.h>
16 #include <drv_pci.h>    // acess
17 #include <udi_internal.h>
18 #include <trans_pci.h>
19
20 // === MACROS ===
21 /* Copied from http://projectudi.cvs.sourceforge.net/viewvc/projectudi/udiref/driver/udi_dpt/udi_dpt.h */
22 #define DPT_SET_ATTR_BOOLEAN(attr, name, val)   \
23                 udi_strcpy((attr)->attr_name, (name)); \
24                 (attr)->attr_type = UDI_ATTR_BOOLEAN; \
25                 (attr)->attr_length = sizeof(udi_boolean_t); \
26                 UDI_ATTR32_SET((attr)->attr_value, (val))
27
28 #define DPT_SET_ATTR32(attr, name, val) \
29                 udi_strcpy((attr)->attr_name, (name)); \
30                 (attr)->attr_type = UDI_ATTR_UBIT32; \
31                 (attr)->attr_length = sizeof(udi_ubit32_t); \
32                 UDI_ATTR32_SET((attr)->attr_value, (val))
33
34 #define DPT_SET_ATTR_ARRAY8(attr, name, val, len) \
35                 udi_strcpy((attr)->attr_name, (name)); \
36                 (attr)->attr_type = UDI_ATTR_ARRAY8; \
37                 (attr)->attr_length = (len); \
38                 udi_memcpy((attr)->attr_value, (val), (len))
39
40 #define DPT_SET_ATTR_STRING(attr, name, val, len) \
41                 udi_strcpy((attr)->attr_name, (name)); \
42                 (attr)->attr_type = UDI_ATTR_STRING; \
43                 (attr)->attr_length = (len); \
44                 udi_strncpy_rtrim((char *)(attr)->attr_value, (val), (len))
45
46 #define PCI_OPS_BRIDGE  1
47 #define PCI_OPS_IRQ     2
48
49 #define PCI_MAX_EVENT_CBS       8
50
51 // === TYPES ===
52 typedef struct
53 {
54         udi_init_context_t      init_context;
55         
56         tPCIDev cur_iter;
57 } pci_rdata_t;
58
59 typedef struct
60 {
61         udi_child_chan_context_t        child_chan_context;
62         
63         udi_channel_t   interrupt_channel;
64         struct {
65                 tPAddr  paddr;
66                 void    *vaddr;
67                 size_t  length;
68         } bars[6];
69
70          int    interrupt_handle;
71
72         udi_pio_handle_t        intr_preprocessing;
73         udi_intr_event_cb_t     *event_cbs[PCI_MAX_EVENT_CBS];
74         udi_index_t     event_cb_wr_ofs;
75         udi_index_t     event_cb_rd_ofs;
76          int    bIntrEnabled;
77 } pci_child_chan_context_t;
78
79 // === PROTOTYPES ===
80 void    pci_usage_ind(udi_usage_cb_t *cb, udi_ubit8_t resource_level);
81 void    pci_enumerate_req(udi_enumerate_cb_t *cb, udi_ubit8_t enumeration_level);
82 void    pci_devmgmt_req(udi_mgmt_cb_t *cb, udi_ubit8_t mgmt_op, udi_ubit8_t parent_ID);
83 void    pci_final_cleanup_req(udi_mgmt_cb_t *cb);
84
85 void    pci_bridge_ch_event_ind(udi_channel_event_cb_t *cb);
86 void    pci_unbind_req(udi_bus_bind_cb_t *cb);
87 void    pci_bind_req_op(udi_bus_bind_cb_t *cb);
88 void    pci_intr_attach_req(udi_intr_attach_cb_t *cb);
89 void    pci_intr_attach_req__channel_spawned(udi_cb_t *gcb, udi_channel_t new_channel);
90 void    pci_intr_detach_req(udi_intr_detach_cb_t *cb);
91
92 void    pci_intr_ch_event_ind(udi_channel_event_cb_t *cb);
93 void    pci_intr_event_rdy(udi_intr_event_cb_t *cb);
94 void    pci_intr_event_rdy__irqs_enabled(udi_cb_t *gcb, udi_buf_t *newbuf, udi_status_t status, udi_ubit16_t result);
95 void    pci_intr_handler(int irq, void *void_context);
96 void    pci_intr_handle__trans_done(udi_cb_t *gcb, udi_buf_t *new_buf, udi_status_t status, udi_ubit16_t result);
97
98 // - Hook to physio (UDI doesn't define these)
99  int    pci_pio_get_regset(udi_cb_t *gcb, udi_ubit32_t regset_idx, void **baseptr, size_t *lenptr);
100
101 // === CODE ===
102 void pci_usage_ind(udi_usage_cb_t *cb, udi_ubit8_t resource_level)
103 {
104         pci_rdata_t     *rdata = UDI_GCB(cb)->context;
105         
106         switch(cb->meta_idx)
107         {
108         case 1: // mgmt
109                 break;
110         }
111
112         switch(resource_level)
113         {
114         case UDI_RESOURCES_CRITICAL:
115         case UDI_RESOURCES_LOW:
116         case UDI_RESOURCES_NORMAL:
117         case UDI_RESOURCES_PLENTIFUL:
118                 break;
119         }
120
121         // TODO: Initialise rdata
122         rdata->cur_iter = -1;
123
124         udi_usage_res(cb);
125 }
126 void pci_enumerate_req(udi_enumerate_cb_t *cb, udi_ubit8_t enumeration_level)
127 {
128         pci_rdata_t     *rdata = UDI_GCB(cb)->context;
129         switch(enumeration_level)
130         {
131         case UDI_ENUMERATE_START:
132         case UDI_ENUMERATE_START_RESCAN:
133                 rdata->cur_iter = -1;
134         case UDI_ENUMERATE_NEXT:
135                 // TODO: Filters
136                 if( (rdata->cur_iter = PCI_GetDeviceByClass(0,0, rdata->cur_iter)) == -1 )
137                 {
138                         udi_enumerate_ack(cb, UDI_ENUMERATE_DONE, 0);
139                 }
140                 else
141                 {
142                         udi_instance_attr_list_t *attr_list = cb->attr_list;
143                         Uint16  ven, dev;
144                         Uint32  class;
145                         PCI_GetDeviceInfo(rdata->cur_iter, &ven, &dev, &class);
146                         Uint8   revision;
147                         PCI_GetDeviceVersion(rdata->cur_iter, &revision);
148                         Uint16  sven, sdev;
149                         PCI_GetDeviceSubsys(rdata->cur_iter, &sven, &sdev);
150
151                         udi_strcpy(attr_list->attr_name, "identifier");
152                         attr_list->attr_length = sprintf((char*)attr_list->attr_value,
153                                 "%04x%04x%02x%04x%04x",
154                                 ven, dev, revision, sven, sdev);
155                         attr_list ++;
156                         DPT_SET_ATTR_STRING(attr_list, "bus_type", "pci", 3);
157                         attr_list ++;
158                         DPT_SET_ATTR32(attr_list, "pci_vendor_id", ven);
159                         attr_list ++;
160                         DPT_SET_ATTR32(attr_list, "pci_device_id", dev);
161                         attr_list ++;
162                         
163                         DPT_SET_ATTR32(attr_list, "pci_baseclass", class >> 16);
164                         attr_list ++;
165                         DPT_SET_ATTR32(attr_list, "pci_base_class", class >> 16);       // E20010702.1
166                         attr_list ++;
167                         DPT_SET_ATTR32(attr_list, "pci_sub_class", (class >> 8) & 0xFF);
168                         attr_list ++;
169                         DPT_SET_ATTR32(attr_list, "pci_prog_if", (class >> 0) & 0xFF);
170                         attr_list ++;
171
172                         cb->attr_valid_length = attr_list - cb->attr_list;
173                         cb->child_ID = rdata->cur_iter;
174                         udi_enumerate_ack(cb, UDI_ENUMERATE_OK, 1);
175                 }
176                 break;
177         }
178 }
179 void pci_devmgmt_req(udi_mgmt_cb_t *cb, udi_ubit8_t mgmt_op, udi_ubit8_t parent_ID)
180 {
181         UNIMPLEMENTED();
182 }
183 void pci_final_cleanup_req(udi_mgmt_cb_t *cb)
184 {
185         UNIMPLEMENTED();
186 }
187
188 void pci_bridge_ch_event_ind(udi_channel_event_cb_t *cb)
189 {
190         UNIMPLEMENTED();
191 }
192 void pci_bind_req(udi_bus_bind_cb_t *cb)
193 {
194         // TODO: "Lock" PCI device
195
196         // TODO: DMA constraints
197         udi_bus_bind_ack(cb, 0, UDI_DMA_LITTLE_ENDIAN, UDI_OK);
198 }
199 void pci_unbind_req(udi_bus_bind_cb_t *cb)
200 {
201         UNIMPLEMENTED();
202 }
203 void pci_intr_attach_req(udi_intr_attach_cb_t *cb)
204 {
205         pci_child_chan_context_t *context = UDI_GCB(cb)->context;
206
207         ASSERT(cb->interrupt_idx == 0); 
208
209         context->intr_preprocessing = cb->preprocessing_handle;
210         // Check if interrupt is already bound
211         if( !UDI_HANDLE_IS_NULL(context->interrupt_channel, udi_channel_t) )
212         {
213                 udi_intr_attach_ack(cb, UDI_OK);
214                 return ;
215         }
216         // Create a channel
217         udi_channel_spawn(pci_intr_attach_req__channel_spawned, UDI_GCB(cb),
218                 cb->gcb.channel, cb->interrupt_idx, PCI_OPS_IRQ, context);
219 }
220 void pci_intr_attach_req__channel_spawned(udi_cb_t *gcb, udi_channel_t new_channel)
221 {
222         udi_intr_attach_cb_t *cb = UDI_MCB(gcb, udi_intr_attach_cb_t);
223         pci_child_chan_context_t *context = UDI_GCB(cb)->context;
224
225         if( UDI_HANDLE_IS_NULL(new_channel, udi_channel_t) )
226         {
227                 // oops
228                 return ;
229         }       
230
231         context->interrupt_channel = new_channel;
232         
233         context->interrupt_handle = IRQ_AddHandler(
234                 PCI_GetIRQ(context->child_chan_context.child_ID),
235                 pci_intr_handler, context);
236
237         udi_intr_attach_ack(cb, UDI_OK);
238 }
239 void pci_intr_detach_req(udi_intr_detach_cb_t *cb)
240 {
241         UNIMPLEMENTED();
242 }
243
244 void pci_intr_ch_event_ind(udi_channel_event_cb_t *cb)
245 {
246         UNIMPLEMENTED();
247 }
248 void pci_intr_event_rdy(udi_intr_event_cb_t *cb)
249 {
250         pci_child_chan_context_t        *context = UDI_GCB(cb)->context;
251
252         ASSERTC(context->event_cb_rd_ofs, <, PCI_MAX_EVENT_CBS);
253         ASSERTC(context->event_cb_wr_ofs, <, PCI_MAX_EVENT_CBS);
254
255         LOG("Rd %i, Wr %i [WR %p{%p}]", context->event_cb_rd_ofs, context->event_cb_wr_ofs, cb, cb->event_buf);
256         if( context->event_cbs[context->event_cb_wr_ofs] )
257         {
258                 // oops, overrun.
259                 return ;
260         }
261         context->event_cbs[context->event_cb_wr_ofs++] = cb;
262         if( context->event_cb_wr_ofs == PCI_MAX_EVENT_CBS )
263                 context->event_cb_wr_ofs = 0;
264         
265         // TODO: Fire once >= min_event_pend CBs are recieved
266         if( !context->bIntrEnabled )
267         {
268                 context->bIntrEnabled = 1;
269                 udi_pio_trans(pci_intr_event_rdy__irqs_enabled, NULL, context->intr_preprocessing, 0, NULL, NULL);
270         }
271 }
272 void pci_intr_event_rdy__irqs_enabled(udi_cb_t *gcb, udi_buf_t *newbuf, udi_status_t status, udi_ubit16_t result)
273 {
274         // Do nothing
275 }
276
277 void pci_intr_handler(int irq, void *void_context)
278 {
279         pci_child_chan_context_t *context = void_context;
280
281         LOG("irq=%i, context=%p", irq, context);
282
283         if( context->event_cb_rd_ofs == context->event_cb_wr_ofs ) {
284                 // Dropped
285                 return ;
286         }
287
288         ASSERTC(context->event_cb_rd_ofs, <, PCI_MAX_EVENT_CBS);
289         ASSERTC(context->event_cb_wr_ofs, <, PCI_MAX_EVENT_CBS);
290
291         udi_intr_event_cb_t *cb = context->event_cbs[context->event_cb_rd_ofs];
292         LOG("Rd %i, Wr %i [RD %p]", context->event_cb_rd_ofs, context->event_cb_wr_ofs, cb);
293         context->event_cbs[context->event_cb_rd_ofs] = NULL;
294         context->event_cb_rd_ofs ++;
295         if( context->event_cb_rd_ofs == PCI_MAX_EVENT_CBS )
296                 context->event_cb_rd_ofs = 0;
297         ASSERT(cb);
298         ASSERT(cb->gcb.scratch);
299         
300         if( UDI_HANDLE_IS_NULL(context->intr_preprocessing, udi_pio_handle_t) )
301         {
302                 // TODO: Ensure region is an interrupt region
303                 udi_intr_event_ind(cb, 0);
304         }
305         else
306         {
307                 // Processing
308                 *(udi_ubit8_t*)(cb->gcb.scratch) = 0;
309                 // - no event info, so mem_ptr=NULL
310                 udi_pio_trans(pci_intr_handle__trans_done, UDI_GCB(cb),
311                         context->intr_preprocessing, 1, cb->event_buf, NULL);
312                 // V V V
313         }
314 }
315
316 void pci_intr_handle__trans_done(udi_cb_t *gcb, udi_buf_t *new_buf, udi_status_t status, udi_ubit16_t result)
317 {
318         udi_intr_event_cb_t *cb = UDI_MCB(gcb, udi_intr_event_cb_t);
319         LOG("cb(%p)->event_buf=%p, new_buf=%p",
320                 cb, cb->event_buf, new_buf);
321         
322         // TODO: Buffers should not change
323         cb->event_buf = new_buf;
324         cb->intr_result = result;
325         
326         udi_ubit8_t     intr_status = *(udi_ubit8_t*)(gcb->scratch);
327         if( intr_status & UDI_INTR_UNCLAIMED )
328         {
329                 // Not claimed, next please.
330                 // NOTE: Same as no event in the acess model
331                 LOG("Unclaimed");
332                 pci_intr_event_rdy(cb);
333         }
334         else if( intr_status & UDI_INTR_NO_EVENT )
335         {
336                 // No event should be generated, return cb to pool
337                 // EVIL!
338                 pci_intr_event_rdy(cb);
339                 LOG("No event, return cb to pool");
340         }
341         else
342         {
343                 LOG("Inform driver");
344                 udi_intr_event_ind(cb, UDI_INTR_PREPROCESSED);  
345         }
346 }
347
348 // - physio hooks
349 udi_status_t pci_pio_do_io(uint32_t child_ID, udi_ubit32_t regset_idx, udi_ubit32_t ofs, udi_ubit8_t len,
350         void *data, bool isOutput)
351 {
352 //      LOG("child_ID=%i, regset_idx=%i,ofs=0x%x,len=%i,data=%p,isOutput=%b",
353 //              child_ID, regset_idx, ofs, len, data, isOutput);
354         tPCIDev pciid = child_ID;
355         // TODO: Cache child mappings   
356
357         switch(regset_idx)
358         {
359         case UDI_PCI_CONFIG_SPACE:
360                 // TODO:
361                 return UDI_STAT_NOT_SUPPORTED;
362         case UDI_PCI_BAR_0 ... UDI_PCI_BAR_5: {
363                 Uint32 bar = PCI_GetBAR(pciid, regset_idx);
364                 if(bar & 1)
365                 {
366                         // IO BAR
367                         bar &= ~3;
368                         #define _IO(fc, type) do {\
369                                 if( isOutput ) { \
370                                         /*LOG("out"#fc"(0x%x, 0x%x)",bar+ofs,*(type*)data);*/\
371                                         out##fc(bar+ofs, *(type*)data); \
372                                 } \
373                                 else { \
374                                         *(type*)data = in##fc(bar+ofs); \
375                                         /*LOG("in"#fc"(0x%x) = 0x%x",bar+ofs,*(type*)data);*/\
376                                 }\
377                                 } while(0)
378                         switch(len)
379                         {
380                         case UDI_PIO_1BYTE:     _IO(b, udi_ubit8_t);    return UDI_OK;
381                         case UDI_PIO_2BYTE:     _IO(w, udi_ubit16_t);   return UDI_OK;
382                         case UDI_PIO_4BYTE:     _IO(d, udi_ubit32_t);   return UDI_OK;
383                         //case UDI_PIO_8BYTE:   _IO(q, uint64_t);       return UDI_OK;
384                         default:
385                                 return UDI_STAT_NOT_SUPPORTED;
386                         }
387                         #undef _IO
388                 }
389                 else
390                 {
391                         // Memory BAR
392                         //Uint64 longbar = PCI_GetValidBAR(pciid, regset_idx, PCI_BARTYPE_MEM);
393                         return UDI_STAT_NOT_SUPPORTED;
394                 }
395                 break; }
396         default:
397                 return UDI_STAT_NOT_UNDERSTOOD;
398         }
399 }
400
401 // === UDI Functions ===
402 udi_mgmt_ops_t  pci_mgmt_ops = {
403         pci_usage_ind,
404         pci_enumerate_req,
405         pci_devmgmt_req,
406         pci_final_cleanup_req
407 };
408 udi_ubit8_t     pci_mgmt_op_flags[4] = {0,0,0,0};
409 udi_bus_bridge_ops_t    pci_bridge_ops = {
410         pci_bridge_ch_event_ind,
411         pci_bind_req,
412         pci_unbind_req,
413         pci_intr_attach_req,
414         pci_intr_detach_req
415 };
416 udi_ubit8_t     pci_bridge_op_flags[5] = {0,0,0,0,0};
417 udi_intr_dispatcher_ops_t       pci_irq_ops = {
418         pci_intr_ch_event_ind,
419         pci_intr_event_rdy
420 };
421 udi_ubit8_t     pci_irq_ops_flags[2] = {0,0};
422 udi_primary_init_t      pci_pri_init = {
423         .mgmt_ops = &pci_mgmt_ops,
424         .mgmt_op_flags = pci_mgmt_op_flags,
425         .mgmt_scratch_requirement = 0,
426         .enumeration_attr_list_length = 7,
427         .rdata_size = sizeof(pci_rdata_t),
428         .child_data_size = 0,
429         .per_parent_paths = 0
430 };
431 udi_ops_init_t  pci_ops_list[] = {
432         {
433                 PCI_OPS_BRIDGE, 1, UDI_BUS_BRIDGE_OPS_NUM,
434                 sizeof(pci_child_chan_context_t),
435                 (udi_ops_vector_t*)&pci_bridge_ops,
436                 pci_bridge_op_flags
437         },
438         {
439                 PCI_OPS_IRQ, 1, UDI_BUS_INTR_DISPATCH_OPS_NUM,
440                 0,
441                 (udi_ops_vector_t*)&pci_irq_ops,
442                 pci_irq_ops_flags
443         },
444         {0}
445 };
446 udi_init_t      pci_init = {
447         .primary_init_info = &pci_pri_init,
448         .ops_init_list = pci_ops_list
449 };
450 const char      pci_udiprops[] =
451         "properties_version 0x101\0"
452         "message 1 Acess2 Kernel\0"
453         "message 2 John Hodge ([email protected])\0"
454         "message 3 Acess2 PCI Bus\0"
455         "supplier 1\0"
456         "contact 2\0"
457         "name 3\0"
458         "module acess_pci\0"
459         "shortname acesspci\0"
460         "requires udi 0x101\0"
461         "provides udi_bridge 0x101\0"
462         "meta 1 udi_bridge\0"
463         "enumerates 4 0 100 1 bus_name string pci\0"
464         "region 0\0"
465         "child_bind_ops 1 0 1\0"
466         "";
467 size_t  pci_udiprops_size = sizeof(pci_udiprops);

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