| /*  This file is part of the program psim. | 
 |  | 
 |     Copyright (C) 1994-1996, Andrew Cagney <cagney@highland.com.au> | 
 |  | 
 |     This program is free software; you can redistribute it and/or modify | 
 |     it under the terms of the GNU General Public License as published by | 
 |     the Free Software Foundation; either version 3 of the License, or | 
 |     (at your option) any later version. | 
 |  | 
 |     This program is distributed in the hope that it will be useful, | 
 |     but WITHOUT ANY WARRANTY; without even the implied warranty of | 
 |     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
 |     GNU General Public License for more details. | 
 |   | 
 |     You should have received a copy of the GNU General Public License | 
 |     along with this program; if not, see <http://www.gnu.org/licenses/>. | 
 |   | 
 |     */ | 
 |  | 
 |  | 
 | #ifndef _DEVICE_TABLE_C_ | 
 | #define _DEVICE_TABLE_C_ | 
 |  | 
 | #include "device_table.h" | 
 |  | 
 | #include <stdlib.h> | 
 | #include <ctype.h> | 
 |  | 
 |  | 
 | /* Helper functions */ | 
 |  | 
 |  | 
 | /* Go through the devices various reg properties for those that | 
 |    specify attach addresses */ | 
 |  | 
 |  | 
 | void | 
 | generic_device_init_address(device *me) | 
 | { | 
 |   static const char *(reg_property_names[]) = { | 
 |     "attach-addresses", | 
 |     "assigned-addresses", | 
 |     "reg", | 
 |     "alternate-reg" , | 
 |     NULL | 
 |   }; | 
 |   const char **reg_property_name; | 
 |   int nr_valid_reg_properties = 0; | 
 |   for (reg_property_name = reg_property_names; | 
 |        *reg_property_name != NULL; | 
 |        reg_property_name++) { | 
 |     if (device_find_property(me, *reg_property_name) != NULL) { | 
 |       reg_property_spec reg; | 
 |       int reg_entry; | 
 |       for (reg_entry = 0; | 
 | 	   device_find_reg_array_property(me, *reg_property_name, reg_entry, | 
 | 					  ®); | 
 | 	   reg_entry++) { | 
 | 	unsigned_word attach_address; | 
 | 	int attach_space; | 
 | 	unsigned attach_size; | 
 | 	if (!device_address_to_attach_address(device_parent(me), | 
 | 					      ®.address, | 
 | 					      &attach_space, &attach_address, | 
 | 					      me)) | 
 | 	  continue; | 
 | 	if (!device_size_to_attach_size(device_parent(me), | 
 | 					®.size, | 
 | 					&attach_size, me)) | 
 | 	  continue; | 
 | 	device_attach_address(device_parent(me), | 
 | 			      attach_callback, | 
 | 			      attach_space, attach_address, attach_size, | 
 | 			      access_read_write_exec, | 
 | 			      me); | 
 | 	nr_valid_reg_properties++; | 
 |       } | 
 |       /* if first option matches don't try for any others */ | 
 |       if (reg_property_name == reg_property_names) | 
 | 	break; | 
 |     } | 
 |   } | 
 | } | 
 |  | 
 | int | 
 | generic_device_unit_decode(device *bus, | 
 | 			   const char *unit, | 
 | 			   device_unit *phys) | 
 | { | 
 |   memset(phys, 0, sizeof(device_unit)); | 
 |   if (unit == NULL) | 
 |     return 0; | 
 |   else { | 
 |     int nr_cells = 0; | 
 |     const int max_nr_cells = device_nr_address_cells(bus); | 
 |     while (1) { | 
 |       char *end = NULL; | 
 |       unsigned long val; | 
 |       val = strtoul(unit, &end, 0); | 
 |       /* parse error? */ | 
 |       if (unit == end) | 
 | 	return -1; | 
 |       /* two many cells? */ | 
 |       if (nr_cells >= max_nr_cells) | 
 | 	return -1; | 
 |       /* save it */ | 
 |       phys->cells[nr_cells] = val; | 
 |       nr_cells++; | 
 |       unit = end; | 
 |       /* more to follow? */ | 
 |       if (isspace(*unit) || *unit == '\0') | 
 | 	break; | 
 |       if (*unit != ',') | 
 | 	return -1; | 
 |       unit++; | 
 |     } | 
 |     if (nr_cells < max_nr_cells) { | 
 |       /* shift everything to correct position */ | 
 |       int i; | 
 |       for (i = 1; i <= nr_cells; i++) | 
 | 	phys->cells[max_nr_cells - i] = phys->cells[nr_cells - i]; | 
 |       for (i = 0; i < (max_nr_cells - nr_cells); i++) | 
 | 	phys->cells[i] = 0; | 
 |     } | 
 |     phys->nr_cells = max_nr_cells; | 
 |     return max_nr_cells; | 
 |   } | 
 | } | 
 |  | 
 | int | 
 | generic_device_unit_encode(device *bus, | 
 | 			   const device_unit *phys, | 
 | 			   char *buf, | 
 | 			   int sizeof_buf) | 
 | { | 
 |   int i; | 
 |   int len; | 
 |   char *pos = buf; | 
 |   /* skip leading zero's */ | 
 |   for (i = 0; i < phys->nr_cells; i++) { | 
 |     if (phys->cells[i] != 0) | 
 |       break; | 
 |   } | 
 |   /* don't output anything if empty */ | 
 |   if (phys->nr_cells == 0) { | 
 |     strcpy(pos, ""); | 
 |     len = 0; | 
 |   } | 
 |   else if (i == phys->nr_cells) { | 
 |     /* all zero */ | 
 |     strcpy(pos, "0"); | 
 |     len = 1; | 
 |   } | 
 |   else { | 
 |     for (; i < phys->nr_cells; i++) { | 
 |       if (pos != buf) { | 
 | 	strcat(pos, ","); | 
 | 	pos = strchr(pos, '\0'); | 
 |       } | 
 |       if (phys->cells[i] < 10) | 
 | 	sprintf(pos, "%ld", (unsigned long)phys->cells[i]); | 
 |       else | 
 | 	sprintf(pos, "0x%lx", (unsigned long)phys->cells[i]); | 
 |       pos = strchr(pos, '\0'); | 
 |     } | 
 |     len = pos - buf; | 
 |   } | 
 |   if (len >= sizeof_buf) | 
 |     error("generic_unit_encode - buffer overflow\n"); | 
 |   return len; | 
 | } | 
 |  | 
 | int | 
 | generic_device_address_to_attach_address(device *me, | 
 | 					 const device_unit *address, | 
 | 					 int *attach_space, | 
 | 					 unsigned_word *attach_address, | 
 | 					 device *client) | 
 | { | 
 |   int i; | 
 |   for (i = 0; i < address->nr_cells - 2; i++) { | 
 |     if (address->cells[i] != 0) | 
 |       device_error(me, "Only 32bit addresses supported"); | 
 |   } | 
 |   if (address->nr_cells >= 2) | 
 |     *attach_space = address->cells[address->nr_cells - 2]; | 
 |   else | 
 |     *attach_space = 0; | 
 |   *attach_address = address->cells[address->nr_cells - 1]; | 
 |   return 1; | 
 | } | 
 |  | 
 | int | 
 | generic_device_size_to_attach_size(device *me, | 
 | 				   const device_unit *size, | 
 | 				   unsigned *nr_bytes, | 
 | 				   device *client) | 
 | { | 
 |   int i; | 
 |   for (i = 0; i < size->nr_cells - 1; i++) { | 
 |     if (size->cells[i] != 0) | 
 |       device_error(me, "Only 32bit sizes supported"); | 
 |   } | 
 |   *nr_bytes = size->cells[0]; | 
 |   return *nr_bytes; | 
 | } | 
 |  | 
 |  | 
 | /* ignore/passthrough versions of each function */ | 
 |  | 
 | void | 
 | passthrough_device_address_attach(device *me, | 
 | 				  attach_type attach, | 
 | 				  int space, | 
 | 				  unsigned_word addr, | 
 | 				  unsigned nr_bytes, | 
 | 				  access_type access, | 
 | 				  device *client) /*callback/default*/ | 
 | { | 
 |   device_attach_address(device_parent(me), attach, | 
 | 			space, addr, nr_bytes, | 
 | 			access, | 
 | 			client); | 
 | } | 
 |  | 
 | void | 
 | passthrough_device_address_detach(device *me, | 
 | 				  attach_type attach, | 
 | 				  int space, | 
 | 				  unsigned_word addr, | 
 | 				  unsigned nr_bytes, | 
 | 				  access_type access, | 
 | 				  device *client) /*callback/default*/ | 
 | { | 
 |   device_detach_address(device_parent(me), attach, | 
 | 			space, addr, nr_bytes, access, | 
 | 			client); | 
 | } | 
 |  | 
 | unsigned | 
 | passthrough_device_dma_read_buffer(device *me, | 
 | 				   void *dest, | 
 | 				   int space, | 
 | 				   unsigned_word addr, | 
 | 				   unsigned nr_bytes) | 
 | { | 
 |   return device_dma_read_buffer(device_parent(me), dest, | 
 | 				space, addr, nr_bytes); | 
 | } | 
 |  | 
 | unsigned | 
 | passthrough_device_dma_write_buffer(device *me, | 
 | 			     const void *source, | 
 | 			     int space, | 
 | 			     unsigned_word addr, | 
 | 			     unsigned nr_bytes, | 
 | 			     int violate_read_only_section) | 
 | { | 
 |   return device_dma_write_buffer(device_parent(me), source, | 
 | 				 space, addr, | 
 | 				 nr_bytes, | 
 | 				 violate_read_only_section); | 
 | } | 
 |  | 
 | int | 
 | ignore_device_unit_decode(device *me, | 
 | 			  const char *unit, | 
 | 			  device_unit *phys) | 
 | { | 
 |   memset(phys, 0, sizeof(device_unit)); | 
 |   return 0; | 
 | } | 
 |  | 
 |  | 
 | static const device_callbacks passthrough_callbacks = { | 
 |   { NULL, }, /* init */ | 
 |   { passthrough_device_address_attach, | 
 |     passthrough_device_address_detach, }, | 
 |   { NULL, }, /* IO */ | 
 |   { passthrough_device_dma_read_buffer, passthrough_device_dma_write_buffer, }, | 
 |   { NULL, }, /* interrupt */ | 
 |   { generic_device_unit_decode, | 
 |     generic_device_unit_encode, }, | 
 | }; | 
 |  | 
 |  | 
 | static const device_descriptor ob_device_table[] = { | 
 |   /* standard OpenBoot devices */ | 
 |   { "aliases", NULL, &passthrough_callbacks }, | 
 |   { "options", NULL, &passthrough_callbacks }, | 
 |   { "chosen", NULL, &passthrough_callbacks }, | 
 |   { "packages", NULL, &passthrough_callbacks }, | 
 |   { "cpus", NULL, &passthrough_callbacks }, | 
 |   { "openprom", NULL, &passthrough_callbacks }, | 
 |   { "init", NULL, &passthrough_callbacks }, | 
 |   { NULL }, | 
 | }; | 
 |  | 
 | const device_descriptor *const device_table[] = { | 
 |   ob_device_table, | 
 | #include "hw.c" | 
 |   NULL, | 
 | }; | 
 |  | 
 |  | 
 | #endif /* _DEVICE_TABLE_C_ */ |