Split out hw-event code. Clean up interface. Update all users.
[external/binutils.git] / sim / common / hw-base.c
1 /*  This file is part of the program psim.
2
3     Copyright (C) 1994-1996, 1998, Andrew Cagney <cagney@highland.com.au>
4
5     This program is free software; you can redistribute it and/or modify
6     it under the terms of the GNU General Public License as published by
7     the Free Software Foundation; either version 2 of the License, or
8     (at your option) any later version.
9
10     This program is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13     GNU General Public License for more details.
14  
15     You should have received a copy of the GNU General Public License
16     along with this program; if not, write to the Free Software
17     Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18  
19     */
20
21
22 #include "sim-main.h"
23 #include "hw-base.h"
24
25
26 #ifdef HAVE_STRING_H
27 #include <string.h>
28 #else
29 #ifdef HAVE_STRINGS_H
30 #include <strings.h>
31 #endif
32 #endif
33
34 #if HAVE_STDLIB_H
35 #include <stdlib.h>
36 #endif
37
38 #include <ctype.h>
39
40 #include "hw-config.h"
41
42 struct hw_base_data {
43   int finished_p;
44   const struct hw_device_descriptor *descriptor;
45   hw_delete_callback *to_delete;
46 };
47
48 static int
49 generic_hw_unit_decode (struct hw *bus,
50                         const char *unit,
51                         hw_unit *phys)
52 {
53   memset (phys, 0, sizeof (*phys));
54   if (unit == NULL)
55     return 0;
56   else
57     {
58       int nr_cells = 0;
59       const int max_nr_cells = hw_unit_nr_address_cells (bus);
60       while (1)
61         {
62           char *end = NULL;
63           unsigned long val;
64           val = strtoul (unit, &end, 0);
65           /* parse error? */
66           if (unit == end)
67             return -1;
68           /* two many cells? */
69           if (nr_cells >= max_nr_cells)
70             return -1;
71           /* save it */
72           phys->cells[nr_cells] = val;
73           nr_cells++;
74           unit = end;
75           /* more to follow? */
76           if (isspace (*unit) || *unit == '\0')
77             break;
78           if (*unit != ',')
79             return -1;
80           unit++;
81         }
82       if (nr_cells < max_nr_cells) {
83         /* shift everything to correct position */
84         int i;
85         for (i = 1; i <= nr_cells; i++)
86           phys->cells[max_nr_cells - i] = phys->cells[nr_cells - i];
87         for (i = 0; i < (max_nr_cells - nr_cells); i++)
88           phys->cells[i] = 0;
89       }
90       phys->nr_cells = max_nr_cells;
91       return max_nr_cells;
92   }
93 }
94
95 static int
96 generic_hw_unit_encode (struct hw *bus,
97                         const hw_unit *phys,
98                         char *buf,
99                         int sizeof_buf)
100 {
101   int i;
102   int len;
103   char *pos = buf;
104   /* skip leading zero's */
105   for (i = 0; i < phys->nr_cells; i++)
106     {
107       if (phys->cells[i] != 0)
108         break;
109     }
110   /* don't output anything if empty */
111   if (phys->nr_cells == 0)
112     {
113       strcpy(pos, "");
114       len = 0;
115     }
116   else if (i == phys->nr_cells)
117     {
118       /* all zero */
119       strcpy(pos, "0");
120       len = 1;
121     }
122   else
123     {
124       for (; i < phys->nr_cells; i++)
125         {
126           if (pos != buf) {
127             strcat(pos, ",");
128             pos = strchr(pos, '\0');
129           }
130           if (phys->cells[i] < 10)
131             sprintf (pos, "%ld", (unsigned long)phys->cells[i]);
132           else
133             sprintf (pos, "0x%lx", (unsigned long)phys->cells[i]);
134           pos = strchr(pos, '\0');
135         }
136       len = pos - buf;
137     }
138   if (len >= sizeof_buf)
139     hw_abort (NULL, "generic_unit_encode - buffer overflow\n");
140   return len;
141 }
142
143 static int
144 generic_hw_unit_address_to_attach_address (struct hw *me,
145                                            const hw_unit *address,
146                                            int *attach_space,
147                                            unsigned_word *attach_address,
148                                            struct hw *client)
149 {
150   int i;
151   for (i = 0; i < address->nr_cells - 2; i++)
152     {
153       if (address->cells[i] != 0)
154         hw_abort (me, "Only 32bit addresses supported");
155     }
156   if (address->nr_cells >= 2)
157     *attach_space = address->cells[address->nr_cells - 2];
158   else
159     *attach_space = 0;
160   *attach_address = address->cells[address->nr_cells - 1];
161   return 1;
162 }
163
164 static int
165 generic_hw_unit_size_to_attach_size (struct hw *me,
166                                      const hw_unit *size,
167                                      unsigned *nr_bytes,
168                                      struct hw *client)
169 {
170   int i;
171   for (i = 0; i < size->nr_cells - 1; i++)
172     {
173       if (size->cells[i] != 0)
174         hw_abort (me, "Only 32bit sizes supported");
175     }
176   *nr_bytes = size->cells[0];
177   return *nr_bytes;
178 }
179
180
181 /* ignore/passthrough versions of each function */
182
183 static void
184 passthrough_hw_attach_address (struct hw *me,
185                                int level,
186                                int space,
187                                address_word addr,
188                                address_word nr_bytes,
189                                struct hw *client) /*callback/default*/
190 {
191   if (hw_parent (me) == NULL)
192     hw_abort (client, "hw_attach_address: no parent attach method");
193   hw_attach_address (hw_parent (me), level,
194                      space, addr, nr_bytes,
195                      client);
196 }
197
198 static void
199 passthrough_hw_detach_address (struct hw *me,
200                                int level,
201                                int space,
202                                address_word addr,
203                                address_word nr_bytes,
204                                struct hw *client) /*callback/default*/
205 {
206   if (hw_parent (me) == NULL)
207     hw_abort (client, "hw_attach_address: no parent attach method");
208   hw_detach_address (hw_parent (me), level,
209                      space, addr, nr_bytes,
210                      client);
211 }
212
213 static unsigned
214 panic_hw_io_read_buffer (struct hw *me,
215                          void *dest,
216                          int space,
217                          unsigned_word addr,
218                          unsigned nr_bytes)
219 {
220   hw_abort (me, "no io-read method");
221   return 0;
222 }
223
224 static unsigned
225 panic_hw_io_write_buffer (struct hw *me,
226                           const void *source,
227                           int space,
228                           unsigned_word addr,
229                           unsigned nr_bytes)
230 {
231   hw_abort (me, "no io-write method");
232   return 0;
233 }
234
235 static unsigned
236 passthrough_hw_dma_read_buffer (struct hw *me,
237                                 void *dest,
238                                 int space,
239                                 unsigned_word addr,
240                                 unsigned nr_bytes)
241 {
242   if (hw_parent (me) == NULL)
243     hw_abort (me, "no parent dma-read method");
244   return hw_dma_read_buffer (hw_parent (me), dest,
245                              space, addr, nr_bytes);
246 }
247
248 static unsigned
249 passthrough_hw_dma_write_buffer (struct hw *me,
250                                  const void *source,
251                                  int space,
252                                  unsigned_word addr,
253                                  unsigned nr_bytes,
254                                  int violate_read_only_section)
255 {
256   if (hw_parent (me) == NULL)
257     hw_abort (me, "no parent dma-write method");
258   return hw_dma_write_buffer (hw_parent (me), source,
259                               space, addr,
260                               nr_bytes,
261                               violate_read_only_section);
262 }
263
264 static void
265 ignore_hw_delete (struct hw *me)
266 {
267   /* NOP */
268 }
269
270
271
272
273 static const char *
274 full_name_of_hw (struct hw *leaf,
275                  char *buf,
276                  unsigned sizeof_buf)
277 {
278   /* get a buffer */
279   char full_name[1024];
280   if (buf == (char*)0)
281     {
282       buf = full_name;
283       sizeof_buf = sizeof (full_name);
284     }
285
286   /* use head recursion to construct the path */
287
288   if (hw_parent (leaf) == NULL)
289     /* root */
290     {
291       if (sizeof_buf < 1)
292         hw_abort (leaf, "buffer overflow");
293       *buf = '\0';
294     }
295   else
296     /* sub node */
297     {
298       char unit[1024];
299       full_name_of_hw (hw_parent (leaf), buf, sizeof_buf);
300       if (hw_unit_encode (hw_parent (leaf),
301                           hw_unit_address (leaf),
302                           unit + 1,
303                           sizeof (unit) - 1)
304           > 0)
305         unit[0] = '@';
306       else
307         unit[0] = '\0';
308       if (strlen (buf) + strlen ("/") + strlen (hw_name (leaf)) + strlen (unit)
309           >= sizeof_buf)
310         hw_abort (leaf, "buffer overflow");
311       strcat (buf, "/");
312       strcat (buf, hw_name (leaf));
313       strcat (buf, unit);
314     }
315   
316   /* return it usefully */
317   if (buf == full_name)
318     buf = hw_strdup (leaf, full_name);
319   return buf;
320 }
321
322 struct hw *
323 hw_create (struct sim_state *sd,
324            struct hw *parent,
325            const char *family,
326            const char *name,
327            const char *unit,
328            const char *args)
329 {
330  /* NOTE: HW must be allocated using ZALLOC, others use HW_ZALLOC */
331   struct hw *hw = ZALLOC (struct hw);
332
333   /* our identity */
334   hw->family_of_hw = hw_strdup (hw, family);
335   hw->name_of_hw = hw_strdup (hw, name);
336   hw->args_of_hw = hw_strdup (hw, args);
337
338   /* a hook into the system */
339   if (sd != NULL)
340     hw->system_of_hw = sd;
341   else if (parent != NULL)
342     hw->system_of_hw = hw_system (parent);
343   else
344     hw_abort (parent, "No system found");
345
346   /* in a tree */
347   if (parent != NULL)
348     {
349       struct hw **sibling = &parent->child_of_hw;
350       while ((*sibling) != NULL)
351         sibling = &(*sibling)->sibling_of_hw;
352       *sibling = hw;
353       hw->parent_of_hw = parent;
354     }
355
356   /* top of tree */
357   if (parent != NULL)
358     {
359       struct hw *root = parent;
360       while (root->parent_of_hw != NULL)
361         root = root->parent_of_hw;
362       hw->root_of_hw = root;
363     }
364   
365   /* a unique identifier for the device on the parents bus */
366   if (parent != NULL)
367     {
368       hw_unit_decode (parent, unit, &hw->unit_address_of_hw);
369     }
370
371   /* Determine our path */
372   if (parent != NULL)
373     hw->path_of_hw = full_name_of_hw (hw, NULL, 0);
374   else
375     hw->path_of_hw = "/";
376
377   /* create our base type */
378   hw->base_of_hw = HW_ZALLOC (hw, struct hw_base_data);
379   hw->base_of_hw->finished_p = 0;
380
381   /* our callbacks */
382   set_hw_io_read_buffer (hw, panic_hw_io_read_buffer);
383   set_hw_io_write_buffer (hw, panic_hw_io_write_buffer);
384   set_hw_dma_read_buffer (hw, passthrough_hw_dma_read_buffer);
385   set_hw_dma_write_buffer (hw, passthrough_hw_dma_write_buffer);
386   set_hw_unit_decode (hw, generic_hw_unit_decode);
387   set_hw_unit_encode (hw, generic_hw_unit_encode);
388   set_hw_unit_address_to_attach_address (hw, generic_hw_unit_address_to_attach_address);
389   set_hw_unit_size_to_attach_size (hw, generic_hw_unit_size_to_attach_size);
390   set_hw_attach_address (hw, passthrough_hw_attach_address);
391   set_hw_detach_address (hw, passthrough_hw_detach_address);
392   set_hw_delete (hw, ignore_hw_delete);
393
394   /* locate a descriptor */
395   {
396     const struct hw_device_descriptor **table;
397     for (table = hw_descriptors;
398          *table != NULL;
399          table++)
400       {
401         const struct hw_device_descriptor *entry;
402         for (entry = *table;
403              entry->family != NULL;
404              entry++)
405           {
406             if (strcmp (family, entry->family) == 0)
407               {
408                 hw->base_of_hw->descriptor = entry;
409                 break;
410               }
411           }
412       }
413     if (hw->base_of_hw->descriptor == NULL)
414       {
415         hw_abort (parent, "Unknown device `%s'", family);
416       }
417   }
418
419   /* Attach dummy ports */
420   create_hw_port_data (hw);
421   create_hw_event_data (hw);
422   
423   return hw;
424 }
425
426
427 int
428 hw_finished_p (struct hw *me)
429 {
430   return (me->base_of_hw->finished_p);
431 }
432
433 void
434 hw_finish (struct hw *me)
435 {
436   if (hw_finished_p (me))
437     hw_abort (me, "Attempt to finish finished device");
438
439   /* Fill in the (hopefully) defined address/size cells values */
440   if (hw_find_property (me, "#address-cells") != NULL)
441     me->nr_address_cells_of_hw_unit =
442       hw_find_integer_property (me, "#address-cells");
443   else
444     me->nr_address_cells_of_hw_unit = 2;
445   if (hw_find_property (me, "#size-cells") != NULL)
446     me->nr_size_cells_of_hw_unit =
447       hw_find_integer_property (me, "#size-cells");
448   else
449     me->nr_size_cells_of_hw_unit = 1;
450
451   /* Fill in the (hopefully) defined trace variable */
452   if (hw_find_property (me, "trace?") != NULL)
453     me->trace_of_hw_p = hw_find_boolean_property (me, "trace?");
454   /* allow global variable to define default tracing */
455   else if (! hw_trace_p (me)
456            && hw_find_property (hw_root (me), "global-trace?") != NULL
457            && hw_find_boolean_property (hw_root (me), "global-trace?"))
458     me->trace_of_hw_p = 1;
459     
460
461   /* Allow the real device to override any methods */
462   me->base_of_hw->descriptor->to_finish (me);
463   me->base_of_hw->finished_p = 1;
464 }
465
466
467 void
468 hw_delete (struct hw *me)
469 {
470   /* give the object a chance to tidy up */
471   me->base_of_hw->to_delete (me);
472
473   delete_hw_event_data (me);
474   delete_hw_port_data (me);
475
476   /* now unlink us from the tree */
477   if (hw_parent (me))
478     {
479       struct hw **sibling = &hw_parent (me)->child_of_hw;
480       while (*sibling != NULL)
481         {
482           if (*sibling == me)
483             {
484               *sibling = me->sibling_of_hw;
485               me->sibling_of_hw = NULL;
486               me->parent_of_hw = NULL;
487               break;
488             }
489         }
490     }
491
492   /* some sanity checks */
493   if (hw_child (me) != NULL)
494     {
495       hw_abort (me, "attempt to delete device with children");
496     }
497   if (hw_sibling (me) != NULL)
498     {
499       hw_abort (me, "attempt to delete device with siblings");
500     }
501
502   /* blow away all memory belonging to the device */
503   hw_free_all (me);
504
505   /* finally */
506   zfree (me->base_of_hw);
507   zfree (me);
508 }
509
510
511 /* Go through the devices various reg properties for those that
512    specify attach addresses */
513
514
515 void
516 do_hw_attach_regs (struct hw *hw)
517 {
518   static const char *(reg_property_names[]) = {
519     "attach-addresses",
520     "assigned-addresses",
521     "reg",
522     "alternate-reg" ,
523     NULL
524   };
525   const char **reg_property_name;
526   int nr_valid_reg_properties = 0;
527   for (reg_property_name = reg_property_names;
528        *reg_property_name != NULL;
529        reg_property_name++)
530     {
531       if (hw_find_property (hw, *reg_property_name) != NULL)
532         {
533           reg_property_spec reg;
534           int reg_entry;
535           for (reg_entry = 0;
536                hw_find_reg_array_property (hw, *reg_property_name, reg_entry,
537                                            &reg);
538                reg_entry++)
539             {
540               unsigned_word attach_address;
541               int attach_space;
542               unsigned attach_size;
543               if (!hw_unit_address_to_attach_address (hw_parent (hw),
544                                                       &reg.address,
545                                                       &attach_space,
546                                                       &attach_address,
547                                                       hw))
548                 continue;
549               if (!hw_unit_size_to_attach_size (hw_parent (hw),
550                                                 &reg.size,
551                                                 &attach_size, hw))
552                 continue;
553               hw_attach_address (hw_parent (hw),
554                                  0,
555                                  attach_space, attach_address, attach_size,
556                                  hw);
557               nr_valid_reg_properties++;
558             }
559           /* if first option matches don't try for any others */
560           if (reg_property_name == reg_property_names)
561             break;
562         }
563     }
564 }