Modules/UDI - GIO Metalanguage Binding, GIO UART support, init error handling
[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_sub_class", (class >> 8) & 0xFF);
163                         attr_list ++;
164                         DPT_SET_ATTR32(attr_list, "pci_prog_if", (class >> 0) & 0xFF);
165                         attr_list ++;
166
167                         cb->attr_valid_length = attr_list - cb->attr_list;
168                         cb->child_ID = rdata->cur_iter;
169                         udi_enumerate_ack(cb, UDI_ENUMERATE_OK, 1);
170                 }
171                 break;
172         }
173 }
174 void pci_devmgmt_req(udi_mgmt_cb_t *cb, udi_ubit8_t mgmt_op, udi_ubit8_t parent_ID)
175 {
176         UNIMPLEMENTED();
177 }
178 void pci_final_cleanup_req(udi_mgmt_cb_t *cb)
179 {
180         UNIMPLEMENTED();
181 }
182
183 void pci_bridge_ch_event_ind(udi_channel_event_cb_t *cb)
184 {
185         UNIMPLEMENTED();
186 }
187 void pci_bind_req(udi_bus_bind_cb_t *cb)
188 {
189         // TODO: "Lock" PCI device
190
191         // TODO: DMA constraints
192         udi_bus_bind_ack(cb, 0, UDI_DMA_LITTLE_ENDIAN, UDI_OK);
193 }
194 void pci_unbind_req(udi_bus_bind_cb_t *cb)
195 {
196         UNIMPLEMENTED();
197 }
198 void pci_intr_attach_req(udi_intr_attach_cb_t *cb)
199 {
200         pci_child_chan_context_t *context = UDI_GCB(cb)->context;
201
202         ASSERT(cb->interrupt_idx == 0); 
203
204         context->intr_preprocessing = cb->preprocessing_handle;
205         // Check if interrupt is already bound
206         if( !UDI_HANDLE_IS_NULL(context->interrupt_channel, udi_channel_t) )
207         {
208                 udi_intr_attach_ack(cb, UDI_OK);
209                 return ;
210         }
211         // Create a channel
212         udi_channel_spawn(pci_intr_attach_req__channel_spawned, UDI_GCB(cb),
213                 cb->gcb.channel, cb->interrupt_idx, PCI_OPS_IRQ, context);
214 }
215 void pci_intr_attach_req__channel_spawned(udi_cb_t *gcb, udi_channel_t new_channel)
216 {
217         udi_intr_attach_cb_t *cb = UDI_MCB(gcb, udi_intr_attach_cb_t);
218         pci_child_chan_context_t *context = UDI_GCB(cb)->context;
219
220         if( UDI_HANDLE_IS_NULL(new_channel, udi_channel_t) )
221         {
222                 // oops
223                 return ;
224         }       
225
226         context->interrupt_channel = new_channel;
227         
228         context->interrupt_handle = IRQ_AddHandler(
229                 PCI_GetIRQ(context->child_chan_context.child_ID),
230                 pci_intr_handler, context);
231
232         udi_intr_attach_ack(cb, UDI_OK);
233 }
234 void pci_intr_detach_req(udi_intr_detach_cb_t *cb)
235 {
236         UNIMPLEMENTED();
237 }
238
239 void pci_intr_ch_event_ind(udi_channel_event_cb_t *cb)
240 {
241         UNIMPLEMENTED();
242 }
243 void pci_intr_event_rdy(udi_intr_event_cb_t *cb)
244 {
245         pci_child_chan_context_t        *context = UDI_GCB(cb)->context;
246
247         ASSERTC(context->event_cb_rd_ofs, <, PCI_MAX_EVENT_CBS);
248         ASSERTC(context->event_cb_wr_ofs, <, PCI_MAX_EVENT_CBS);
249
250         LOG("Rd %i, Wr %i [WR %p]", context->event_cb_rd_ofs, context->event_cb_wr_ofs, cb);
251         if( context->event_cbs[context->event_cb_wr_ofs] )
252         {
253                 // oops, overrun.
254                 return ;
255         }
256         context->event_cbs[context->event_cb_wr_ofs++] = cb;
257         if( context->event_cb_wr_ofs == PCI_MAX_EVENT_CBS )
258                 context->event_cb_wr_ofs = 0;
259         
260         // TODO: Fire once >= min_event_pend CBs are recieved
261         if( !context->bIntrEnabled )
262         {
263                 context->bIntrEnabled = 1;
264                 udi_pio_trans(pci_intr_event_rdy__irqs_enabled, NULL, context->intr_preprocessing, 0, NULL, NULL);
265         }
266 }
267 void pci_intr_event_rdy__irqs_enabled(udi_cb_t *gcb, udi_buf_t *newbuf, udi_status_t status, udi_ubit16_t result)
268 {
269         // nothing
270 }
271
272 void pci_intr_handler(int irq, void *void_context)
273 {
274         pci_child_chan_context_t *context = void_context;
275
276         LOG("irq=%i, context=%p", irq, context);
277
278         if( context->event_cb_rd_ofs == context->event_cb_wr_ofs ) {
279                 // Dropped
280                 return ;
281         }
282
283         ASSERTC(context->event_cb_rd_ofs, <, PCI_MAX_EVENT_CBS);
284         ASSERTC(context->event_cb_wr_ofs, <, PCI_MAX_EVENT_CBS);
285
286         udi_intr_event_cb_t *cb = context->event_cbs[context->event_cb_rd_ofs];
287         LOG("Rd %i, Wr %i [RD %p]", context->event_cb_rd_ofs, context->event_cb_wr_ofs, cb);
288         context->event_cbs[context->event_cb_rd_ofs] = NULL;
289         context->event_cb_rd_ofs ++;
290         if( context->event_cb_rd_ofs == PCI_MAX_EVENT_CBS )
291                 context->event_cb_rd_ofs = 0;
292         ASSERT(cb);
293         
294         if( UDI_HANDLE_IS_NULL(context->intr_preprocessing, udi_pio_handle_t) )
295         {
296                 udi_intr_event_ind(cb, 0);
297         }
298         else
299         {
300                 // Processing
301                 // - no event info, so mem_ptr=NULL
302                 udi_pio_trans(pci_intr_handle__trans_done, UDI_GCB(cb),
303                         context->intr_preprocessing, 1, cb->event_buf, NULL);
304         }
305 }
306
307 void pci_intr_handle__trans_done(udi_cb_t *gcb, udi_buf_t *new_buf, udi_status_t status, udi_ubit16_t result)
308 {
309         udi_intr_event_cb_t *cb = UDI_MCB(gcb, udi_intr_event_cb_t);
310         
311         cb->intr_result = result;
312         
313         udi_intr_event_ind(cb, UDI_INTR_PREPROCESSED);  
314 }
315
316 // - physio hooks
317 udi_status_t pci_pio_do_io(uint32_t child_ID, udi_ubit32_t regset_idx, udi_ubit32_t ofs, udi_ubit8_t len,
318         void *data, bool isOutput)
319 {
320 //      LOG("child_ID=%i, regset_idx=%i,ofs=0x%x,len=%i,data=%p,isOutput=%b",
321 //              child_ID, regset_idx, ofs, len, data, isOutput);
322         tPCIDev pciid = child_ID;
323         // TODO: Cache child mappings   
324
325         switch(regset_idx)
326         {
327         case UDI_PCI_CONFIG_SPACE:
328                 // TODO:
329                 return UDI_STAT_NOT_SUPPORTED;
330         case UDI_PCI_BAR_0 ... UDI_PCI_BAR_5: {
331                 Uint32 bar = PCI_GetBAR(pciid, regset_idx);
332                 if(bar & 1)
333                 {
334                         // IO BAR
335                         bar &= ~3;
336                         #define _IO(fc, type) do {\
337                                 if( isOutput ) { \
338                                         /*LOG("out"#fc"(0x%x, 0x%x)",bar+ofs,*(type*)data);*/\
339                                         out##fc(bar+ofs, *(type*)data); \
340                                 } \
341                                 else { \
342                                         *(type*)data = in##fc(bar+ofs); \
343                                         /*LOG("in"#fc"(0x%x) = 0x%x",bar+ofs,*(type*)data);*/\
344                                 }\
345                                 } while(0)
346                         switch(len)
347                         {
348                         case UDI_PIO_1BYTE:     _IO(b, udi_ubit8_t);    return UDI_OK;
349                         case UDI_PIO_2BYTE:     _IO(w, udi_ubit16_t);   return UDI_OK;
350                         case UDI_PIO_4BYTE:     _IO(d, udi_ubit32_t);   return UDI_OK;
351                         //case UDI_PIO_8BYTE:   _IO(q, uint64_t);       return UDI_OK;
352                         default:
353                                 return UDI_STAT_NOT_SUPPORTED;
354                         }
355                         #undef _IO
356                 }
357                 else
358                 {
359                         // Memory BAR
360                         //Uint64 longbar = PCI_GetValidBAR(pciid, regset_idx, PCI_BARTYPE_MEM);
361                         return UDI_STAT_NOT_SUPPORTED;
362                 }
363                 break; }
364         default:
365                 return UDI_STAT_NOT_UNDERSTOOD;
366         }
367 }
368
369 // === UDI Functions ===
370 udi_mgmt_ops_t  pci_mgmt_ops = {
371         pci_usage_ind,
372         pci_enumerate_req,
373         pci_devmgmt_req,
374         pci_final_cleanup_req
375 };
376 udi_ubit8_t     pci_mgmt_op_flags[4] = {0,0,0,0};
377 udi_bus_bridge_ops_t    pci_bridge_ops = {
378         pci_bridge_ch_event_ind,
379         pci_bind_req,
380         pci_unbind_req,
381         pci_intr_attach_req,
382         pci_intr_detach_req
383 };
384 udi_ubit8_t     pci_bridge_op_flags[5] = {0,0,0,0,0};
385 udi_intr_dispatcher_ops_t       pci_irq_ops = {
386         pci_intr_ch_event_ind,
387         pci_intr_event_rdy
388 };
389 udi_ubit8_t     pci_irq_ops_flags[2] = {0,0};
390 udi_primary_init_t      pci_pri_init = {
391         .mgmt_ops = &pci_mgmt_ops,
392         .mgmt_op_flags = pci_mgmt_op_flags,
393         .mgmt_scratch_requirement = 0,
394         .enumeration_attr_list_length = 7,
395         .rdata_size = sizeof(pci_rdata_t),
396         .child_data_size = 0,
397         .per_parent_paths = 0
398 };
399 udi_ops_init_t  pci_ops_list[] = {
400         {
401                 PCI_OPS_BRIDGE, 1, UDI_BUS_BRIDGE_OPS_NUM,
402                 sizeof(pci_child_chan_context_t),
403                 (udi_ops_vector_t*)&pci_bridge_ops,
404                 pci_bridge_op_flags
405         },
406         {
407                 PCI_OPS_IRQ, 1, UDI_BUS_INTR_DISPATCH_OPS_NUM,
408                 0,
409                 (udi_ops_vector_t*)&pci_irq_ops,
410                 pci_irq_ops_flags
411         },
412         {0}
413 };
414 udi_init_t      pci_init = {
415         .primary_init_info = &pci_pri_init,
416         .ops_init_list = pci_ops_list
417 };
418 const char      pci_udiprops[] =
419         "properties_version 0x101\0"
420         "message 1 Acess2 Kernel\0"
421         "message 2 John Hodge ([email protected])\0"
422         "message 3 Acess2 PCI Bus\0"
423         "supplier 1\0"
424         "contact 2\0"
425         "name 3\0"
426         "module acess_pci\0"
427         "shortname acesspci\0"
428         "requires udi 0x101\0"
429         "provides udi_bridge 0x101\0"
430         "meta 1 udi_bridge\0"
431         "enumerates 4 0 100 1 bus_name string pci\0"
432         "region 0\0"
433         "child_bind_ops 1 0 1\0"
434         "";
435 size_t  pci_udiprops_size = sizeof(pci_udiprops);

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