1/*  This file is part of the program psim.
2
3    Copyright (C) 1994-1996, 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 3 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, see <http://www.gnu.org/licenses/>.
17
18    */
19
20
21#ifndef _DEVICE_TABLE_C_
22#define _DEVICE_TABLE_C_
23
24#include "device_table.h"
25
26#if HAVE_STDLIB_H
27#include <stdlib.h>
28#endif
29
30#include <ctype.h>
31
32
33/* Helper functions */
34
35
36/* Go through the devices various reg properties for those that
37   specify attach addresses */
38
39
40void
41generic_device_init_address(device *me)
42{
43  static const char *(reg_property_names[]) = {
44    "attach-addresses",
45    "assigned-addresses",
46    "reg",
47    "alternate-reg" ,
48    NULL
49  };
50  const char **reg_property_name;
51  int nr_valid_reg_properties = 0;
52  for (reg_property_name = reg_property_names;
53       *reg_property_name != NULL;
54       reg_property_name++) {
55    if (device_find_property(me, *reg_property_name) != NULL) {
56      reg_property_spec reg;
57      int reg_entry;
58      for (reg_entry = 0;
59	   device_find_reg_array_property(me, *reg_property_name, reg_entry,
60					  &reg);
61	   reg_entry++) {
62	unsigned_word attach_address;
63	int attach_space;
64	unsigned attach_size;
65	if (!device_address_to_attach_address(device_parent(me),
66					      &reg.address,
67					      &attach_space, &attach_address,
68					      me))
69	  continue;
70	if (!device_size_to_attach_size(device_parent(me),
71					&reg.size,
72					&attach_size, me))
73	  continue;
74	device_attach_address(device_parent(me),
75			      attach_callback,
76			      attach_space, attach_address, attach_size,
77			      access_read_write_exec,
78			      me);
79	nr_valid_reg_properties++;
80      }
81      /* if first option matches don't try for any others */
82      if (reg_property_name == reg_property_names)
83	break;
84    }
85  }
86}
87
88int
89generic_device_unit_decode(device *bus,
90			   const char *unit,
91			   device_unit *phys)
92{
93  memset(phys, 0, sizeof(device_unit));
94  if (unit == NULL)
95    return 0;
96  else {
97    int nr_cells = 0;
98    const int max_nr_cells = device_nr_address_cells(bus);
99    while (1) {
100      char *end = NULL;
101      unsigned long val;
102      val = strtoul(unit, &end, 0);
103      /* parse error? */
104      if (unit == end)
105	return -1;
106      /* two many cells? */
107      if (nr_cells >= max_nr_cells)
108	return -1;
109      /* save it */
110      phys->cells[nr_cells] = val;
111      nr_cells++;
112      unit = end;
113      /* more to follow? */
114      if (isspace(*unit) || *unit == '\0')
115	break;
116      if (*unit != ',')
117	return -1;
118      unit++;
119    }
120    if (nr_cells < max_nr_cells) {
121      /* shift everything to correct position */
122      int i;
123      for (i = 1; i <= nr_cells; i++)
124	phys->cells[max_nr_cells - i] = phys->cells[nr_cells - i];
125      for (i = 0; i < (max_nr_cells - nr_cells); i++)
126	phys->cells[i] = 0;
127    }
128    phys->nr_cells = max_nr_cells;
129    return max_nr_cells;
130  }
131}
132
133int
134generic_device_unit_encode(device *bus,
135			   const device_unit *phys,
136			   char *buf,
137			   int sizeof_buf)
138{
139  int i;
140  int len;
141  char *pos = buf;
142  /* skip leading zero's */
143  for (i = 0; i < phys->nr_cells; i++) {
144    if (phys->cells[i] != 0)
145      break;
146  }
147  /* don't output anything if empty */
148  if (phys->nr_cells == 0) {
149    strcpy(pos, "");
150    len = 0;
151  }
152  else if (i == phys->nr_cells) {
153    /* all zero */
154    strcpy(pos, "0");
155    len = 1;
156  }
157  else {
158    for (; i < phys->nr_cells; i++) {
159      if (pos != buf) {
160	strcat(pos, ",");
161	pos = strchr(pos, '\0');
162      }
163      if (phys->cells[i] < 10)
164	sprintf(pos, "%ld", (unsigned long)phys->cells[i]);
165      else
166	sprintf(pos, "0x%lx", (unsigned long)phys->cells[i]);
167      pos = strchr(pos, '\0');
168    }
169    len = pos - buf;
170  }
171  if (len >= sizeof_buf)
172    error("generic_unit_encode - buffer overflow\n");
173  return len;
174}
175
176int
177generic_device_address_to_attach_address(device *me,
178					 const device_unit *address,
179					 int *attach_space,
180					 unsigned_word *attach_address,
181					 device *client)
182{
183  int i;
184  for (i = 0; i < address->nr_cells - 2; i++) {
185    if (address->cells[i] != 0)
186      device_error(me, "Only 32bit addresses supported");
187  }
188  if (address->nr_cells >= 2)
189    *attach_space = address->cells[address->nr_cells - 2];
190  else
191    *attach_space = 0;
192  *attach_address = address->cells[address->nr_cells - 1];
193  return 1;
194}
195
196int
197generic_device_size_to_attach_size(device *me,
198				   const device_unit *size,
199				   unsigned *nr_bytes,
200				   device *client)
201{
202  int i;
203  for (i = 0; i < size->nr_cells - 1; i++) {
204    if (size->cells[i] != 0)
205      device_error(me, "Only 32bit sizes supported");
206  }
207  *nr_bytes = size->cells[0];
208  return *nr_bytes;
209}
210
211
212/* ignore/passthrough versions of each function */
213
214void
215passthrough_device_address_attach(device *me,
216				  attach_type attach,
217				  int space,
218				  unsigned_word addr,
219				  unsigned nr_bytes,
220				  access_type access,
221				  device *client) /*callback/default*/
222{
223  device_attach_address(device_parent(me), attach,
224			space, addr, nr_bytes,
225			access,
226			client);
227}
228
229void
230passthrough_device_address_detach(device *me,
231				  attach_type attach,
232				  int space,
233				  unsigned_word addr,
234				  unsigned nr_bytes,
235				  access_type access,
236				  device *client) /*callback/default*/
237{
238  device_detach_address(device_parent(me), attach,
239			space, addr, nr_bytes, access,
240			client);
241}
242
243unsigned
244passthrough_device_dma_read_buffer(device *me,
245				   void *dest,
246				   int space,
247				   unsigned_word addr,
248				   unsigned nr_bytes)
249{
250  return device_dma_read_buffer(device_parent(me), dest,
251				space, addr, nr_bytes);
252}
253
254unsigned
255passthrough_device_dma_write_buffer(device *me,
256			     const void *source,
257			     int space,
258			     unsigned_word addr,
259			     unsigned nr_bytes,
260			     int violate_read_only_section)
261{
262  return device_dma_write_buffer(device_parent(me), source,
263				 space, addr,
264				 nr_bytes,
265				 violate_read_only_section);
266}
267
268int
269ignore_device_unit_decode(device *me,
270			  const char *unit,
271			  device_unit *phys)
272{
273  memset(phys, 0, sizeof(device_unit));
274  return 0;
275}
276
277
278static const device_callbacks passthrough_callbacks = {
279  { NULL, }, /* init */
280  { passthrough_device_address_attach,
281    passthrough_device_address_detach, },
282  { NULL, }, /* IO */
283  { passthrough_device_dma_read_buffer, passthrough_device_dma_write_buffer, },
284  { NULL, }, /* interrupt */
285  { generic_device_unit_decode,
286    generic_device_unit_encode, },
287};
288
289
290static const device_descriptor ob_device_table[] = {
291  /* standard OpenBoot devices */
292  { "aliases", NULL, &passthrough_callbacks },
293  { "options", NULL, &passthrough_callbacks },
294  { "chosen", NULL, &passthrough_callbacks },
295  { "packages", NULL, &passthrough_callbacks },
296  { "cpus", NULL, &passthrough_callbacks },
297  { "openprom", NULL, &passthrough_callbacks },
298  { "init", NULL, &passthrough_callbacks },
299  { NULL },
300};
301
302const device_descriptor *const device_table[] = {
303  ob_device_table,
304#include "hw.c"
305  NULL,
306};
307
308
309#endif /* _DEVICE_TABLE_C_ */
310