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

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