1 /* 2 * Copyright (c) 2013 Google, Inc 3 * 4 * (C) Copyright 2012 5 * Marek Vasut <marex@denx.de> 6 * 7 * SPDX-License-Identifier: GPL-2.0+ 8 */ 9 10 #include <common.h> 11 #include <errno.h> 12 #include <dm/device.h> 13 #include <dm/device-internal.h> 14 #include <dm/lists.h> 15 #include <dm/platdata.h> 16 #include <dm/uclass.h> 17 #include <dm/util.h> 18 #include <fdtdec.h> 19 #include <linux/compiler.h> 20 21 struct driver *lists_driver_lookup_name(const char *name) 22 { 23 struct driver *drv = 24 ll_entry_start(struct driver, driver); 25 const int n_ents = ll_entry_count(struct driver, driver); 26 struct driver *entry; 27 int len; 28 29 if (!drv || !n_ents) 30 return NULL; 31 32 len = strlen(name); 33 34 for (entry = drv; entry != drv + n_ents; entry++) { 35 if (strncmp(name, entry->name, len)) 36 continue; 37 38 /* Full match */ 39 if (len == strlen(entry->name)) 40 return entry; 41 } 42 43 /* Not found */ 44 return NULL; 45 } 46 47 struct uclass_driver *lists_uclass_lookup(enum uclass_id id) 48 { 49 struct uclass_driver *uclass = 50 ll_entry_start(struct uclass_driver, uclass); 51 const int n_ents = ll_entry_count(struct uclass_driver, uclass); 52 struct uclass_driver *entry; 53 54 if ((id == UCLASS_INVALID) || !uclass) 55 return NULL; 56 57 for (entry = uclass; entry != uclass + n_ents; entry++) { 58 if (entry->id == id) 59 return entry; 60 } 61 62 return NULL; 63 } 64 65 int lists_bind_drivers(struct udevice *parent, bool pre_reloc_only) 66 { 67 struct driver_info *info = 68 ll_entry_start(struct driver_info, driver_info); 69 const int n_ents = ll_entry_count(struct driver_info, driver_info); 70 struct driver_info *entry; 71 struct udevice *dev; 72 int result = 0; 73 int ret; 74 75 for (entry = info; entry != info + n_ents; entry++) { 76 ret = device_bind_by_name(parent, pre_reloc_only, entry, &dev); 77 if (ret && ret != -EPERM) { 78 dm_warn("No match for driver '%s'\n", entry->name); 79 if (!result || ret != -ENOENT) 80 result = ret; 81 } 82 } 83 84 return result; 85 } 86 87 #ifdef CONFIG_OF_CONTROL 88 /** 89 * driver_check_compatible() - Check if a driver is compatible with this node 90 * 91 * @param blob: Device tree pointer 92 * @param offset: Offset of node in device tree 93 * @param of_matchL List of compatible strings to match 94 * @return 0 if there is a match, -ENOENT if no match, -ENODEV if the node 95 * does not have a compatible string, other error <0 if there is a device 96 * tree error 97 */ 98 static int driver_check_compatible(const void *blob, int offset, 99 const struct udevice_id *of_match) 100 { 101 int ret; 102 103 if (!of_match) 104 return -ENOENT; 105 106 while (of_match->compatible) { 107 ret = fdt_node_check_compatible(blob, offset, 108 of_match->compatible); 109 if (!ret) 110 return 0; 111 else if (ret == -FDT_ERR_NOTFOUND) 112 return -ENODEV; 113 else if (ret < 0) 114 return -EINVAL; 115 of_match++; 116 } 117 118 return -ENOENT; 119 } 120 121 int lists_bind_fdt(struct udevice *parent, const void *blob, int offset) 122 { 123 struct driver *driver = ll_entry_start(struct driver, driver); 124 const int n_ents = ll_entry_count(struct driver, driver); 125 struct driver *entry; 126 struct udevice *dev; 127 bool found = false; 128 const char *name; 129 int result = 0; 130 int ret = 0; 131 132 dm_dbg("bind node %s\n", fdt_get_name(blob, offset, NULL)); 133 for (entry = driver; entry != driver + n_ents; entry++) { 134 ret = driver_check_compatible(blob, offset, entry->of_match); 135 name = fdt_get_name(blob, offset, NULL); 136 if (ret == -ENOENT) { 137 continue; 138 } else if (ret == -ENODEV) { 139 dm_dbg("Device '%s' has no compatible string\n", name); 140 break; 141 } else if (ret) { 142 dm_warn("Device tree error at offset %d\n", offset); 143 if (!result || ret != -ENOENT) 144 result = ret; 145 break; 146 } 147 148 dm_dbg(" - found match at '%s'\n", entry->name); 149 ret = device_bind(parent, entry, name, NULL, offset, &dev); 150 if (ret) { 151 dm_warn("Error binding driver '%s'\n", entry->name); 152 if (!result || ret != -ENOENT) 153 result = ret; 154 } else { 155 found = true; 156 } 157 break; 158 } 159 160 if (!found && !result && ret != -ENODEV) { 161 dm_dbg("No match for node '%s'\n", 162 fdt_get_name(blob, offset, NULL)); 163 } 164 165 return result; 166 } 167 #endif 168