1 /* 2 * Copyright (c) 2011 The Chromium OS Authors. 3 * See file CREDITS for list of people who contributed to this 4 * project. 5 * 6 * This program is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU General Public License as 8 * published by the Free Software Foundation; either version 2 of 9 * the License, or (at your option) any later version. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with this program; if not, write to the Free Software 18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, 19 * MA 02111-1307 USA 20 */ 21 22 #include <common.h> 23 #include <serial.h> 24 #include <libfdt.h> 25 #include <fdtdec.h> 26 27 DECLARE_GLOBAL_DATA_PTR; 28 29 /* 30 * Here are the type we know about. One day we might allow drivers to 31 * register. For now we just put them here. The COMPAT macro allows us to 32 * turn this into a sparse list later, and keeps the ID with the name. 33 */ 34 #define COMPAT(id, name) name 35 static const char * const compat_names[COMPAT_COUNT] = { 36 }; 37 38 const char *fdtdec_get_compatible(enum fdt_compat_id id) 39 { 40 /* We allow reading of the 'unknown' ID for testing purposes */ 41 assert(id >= 0 && id < COMPAT_COUNT); 42 return compat_names[id]; 43 } 44 45 /** 46 * Look in the FDT for an alias with the given name and return its node. 47 * 48 * @param blob FDT blob 49 * @param name alias name to look up 50 * @return node offset if found, or an error code < 0 otherwise 51 */ 52 static int find_alias_node(const void *blob, const char *name) 53 { 54 const char *path; 55 int alias_node; 56 57 debug("find_alias_node: %s\n", name); 58 alias_node = fdt_path_offset(blob, "/aliases"); 59 if (alias_node < 0) 60 return alias_node; 61 path = fdt_getprop(blob, alias_node, name, NULL); 62 if (!path) 63 return -FDT_ERR_NOTFOUND; 64 return fdt_path_offset(blob, path); 65 } 66 67 fdt_addr_t fdtdec_get_addr(const void *blob, int node, 68 const char *prop_name) 69 { 70 const fdt_addr_t *cell; 71 int len; 72 73 debug("get_addr: %s\n", prop_name); 74 cell = fdt_getprop(blob, node, prop_name, &len); 75 if (cell && (len == sizeof(fdt_addr_t) || 76 len == sizeof(fdt_addr_t) * 2)) 77 return fdt_addr_to_cpu(*cell); 78 return FDT_ADDR_T_NONE; 79 } 80 81 s32 fdtdec_get_int(const void *blob, int node, const char *prop_name, 82 s32 default_val) 83 { 84 const s32 *cell; 85 int len; 86 87 debug("get_size: %s\n", prop_name); 88 cell = fdt_getprop(blob, node, prop_name, &len); 89 if (cell && len >= sizeof(s32)) 90 return fdt32_to_cpu(cell[0]); 91 return default_val; 92 } 93 94 int fdtdec_get_is_enabled(const void *blob, int node, int default_val) 95 { 96 const char *cell; 97 98 cell = fdt_getprop(blob, node, "status", NULL); 99 if (cell) 100 return 0 == strcmp(cell, "ok"); 101 return default_val; 102 } 103 104 enum fdt_compat_id fd_dec_lookup(const void *blob, int node) 105 { 106 enum fdt_compat_id id; 107 108 /* Search our drivers */ 109 for (id = COMPAT_UNKNOWN; id < COMPAT_COUNT; id++) 110 if (0 == fdt_node_check_compatible(blob, node, 111 compat_names[id])) 112 return id; 113 return COMPAT_UNKNOWN; 114 } 115 116 int fdtdec_next_compatible(const void *blob, int node, 117 enum fdt_compat_id id) 118 { 119 return fdt_node_offset_by_compatible(blob, node, compat_names[id]); 120 } 121 122 int fdtdec_next_alias(const void *blob, const char *name, 123 enum fdt_compat_id id, int *upto) 124 { 125 #define MAX_STR_LEN 20 126 char str[MAX_STR_LEN + 20]; 127 int node, err; 128 129 /* snprintf() is not available */ 130 assert(strlen(name) < MAX_STR_LEN); 131 sprintf(str, "%.*s%d", MAX_STR_LEN, name, *upto); 132 (*upto)++; 133 node = find_alias_node(blob, str); 134 if (node < 0) 135 return node; 136 err = fdt_node_check_compatible(blob, node, compat_names[id]); 137 if (err < 0) 138 return err; 139 return err ? -FDT_ERR_NOTFOUND : node; 140 } 141 142 /* TODO: Can we tighten this code up a little? */ 143 int fdtdec_find_aliases_for_id(const void *blob, const char *name, 144 enum fdt_compat_id id, int *node_list, int maxcount) 145 { 146 int name_len = strlen(name); 147 int nodes[maxcount]; 148 int num_found = 0; 149 int offset, node; 150 int alias_node; 151 int count; 152 int i, j; 153 154 /* find the alias node if present */ 155 alias_node = fdt_path_offset(blob, "/aliases"); 156 157 /* 158 * start with nothing, and we can assume that the root node can't 159 * match 160 */ 161 memset(nodes, '\0', sizeof(nodes)); 162 163 /* First find all the compatible nodes */ 164 for (node = count = 0; node >= 0 && count < maxcount;) { 165 node = fdtdec_next_compatible(blob, node, id); 166 if (node >= 0) 167 nodes[count++] = node; 168 } 169 if (node >= 0) 170 debug("%s: warning: maxcount exceeded with alias '%s'\n", 171 __func__, name); 172 173 /* Now find all the aliases */ 174 memset(node_list, '\0', sizeof(*node_list) * maxcount); 175 176 for (offset = fdt_first_property_offset(blob, alias_node); 177 offset > 0; 178 offset = fdt_next_property_offset(blob, offset)) { 179 const struct fdt_property *prop; 180 const char *path; 181 int number; 182 int found; 183 184 node = 0; 185 prop = fdt_get_property_by_offset(blob, offset, NULL); 186 path = fdt_string(blob, fdt32_to_cpu(prop->nameoff)); 187 if (prop->len && 0 == strncmp(path, name, name_len)) 188 node = fdt_path_offset(blob, prop->data); 189 if (node <= 0) 190 continue; 191 192 /* Get the alias number */ 193 number = simple_strtoul(path + name_len, NULL, 10); 194 if (number < 0 || number >= maxcount) { 195 debug("%s: warning: alias '%s' is out of range\n", 196 __func__, path); 197 continue; 198 } 199 200 /* Make sure the node we found is actually in our list! */ 201 found = -1; 202 for (j = 0; j < count; j++) 203 if (nodes[j] == node) { 204 found = j; 205 break; 206 } 207 208 if (found == -1) { 209 debug("%s: warning: alias '%s' points to a node " 210 "'%s' that is missing or is not compatible " 211 " with '%s'\n", __func__, path, 212 fdt_get_name(blob, node, NULL), 213 compat_names[id]); 214 continue; 215 } 216 217 /* 218 * Add this node to our list in the right place, and mark 219 * it as done. 220 */ 221 if (fdtdec_get_is_enabled(blob, node)) { 222 node_list[number] = node; 223 if (number >= num_found) 224 num_found = number + 1; 225 } 226 nodes[j] = 0; 227 } 228 229 /* Add any nodes not mentioned by an alias */ 230 for (i = j = 0; i < maxcount; i++) { 231 if (!node_list[i]) { 232 for (; j < maxcount; j++) 233 if (nodes[j] && 234 fdtdec_get_is_enabled(blob, nodes[j])) 235 break; 236 237 /* Have we run out of nodes to add? */ 238 if (j == maxcount) 239 break; 240 241 assert(!node_list[i]); 242 node_list[i] = nodes[j++]; 243 if (i >= num_found) 244 num_found = i + 1; 245 } 246 } 247 248 return num_found; 249 } 250 251 /* 252 * This function is a little odd in that it accesses global data. At some 253 * point if the architecture board.c files merge this will make more sense. 254 * Even now, it is common code. 255 */ 256 int fdtdec_check_fdt(void) 257 { 258 /* We must have an fdt */ 259 if (fdt_check_header(gd->fdt_blob)) 260 panic("No valid fdt found - please append one to U-Boot\n" 261 "binary or define CONFIG_OF_EMBED\n"); 262 return 0; 263 } 264