1 /* 2 * Copyright (c) 2013 Google, Inc 3 * 4 * (C) Copyright 2012 5 * Pavel Herrmann <morpheus.ibis@gmail.com> 6 * 7 * SPDX-License-Identifier: GPL-2.0+ 8 */ 9 10 #include <common.h> 11 #include <errno.h> 12 #include <malloc.h> 13 #include <dm/device.h> 14 #include <dm/device-internal.h> 15 #include <dm/lists.h> 16 #include <dm/uclass.h> 17 #include <dm/uclass-internal.h> 18 #include <dm/util.h> 19 20 DECLARE_GLOBAL_DATA_PTR; 21 22 struct uclass *uclass_find(enum uclass_id key) 23 { 24 struct uclass *uc; 25 26 /* 27 * TODO(sjg@chromium.org): Optimise this, perhaps moving the found 28 * node to the start of the list, or creating a linear array mapping 29 * id to node. 30 */ 31 list_for_each_entry(uc, &gd->uclass_root, sibling_node) { 32 if (uc->uc_drv->id == key) 33 return uc; 34 } 35 36 return NULL; 37 } 38 39 /** 40 * uclass_add() - Create new uclass in list 41 * @id: Id number to create 42 * @ucp: Returns pointer to uclass, or NULL on error 43 * @return 0 on success, -ve on error 44 * 45 * The new uclass is added to the list. There must be only one uclass for 46 * each id. 47 */ 48 static int uclass_add(enum uclass_id id, struct uclass **ucp) 49 { 50 struct uclass_driver *uc_drv; 51 struct uclass *uc; 52 int ret; 53 54 *ucp = NULL; 55 uc_drv = lists_uclass_lookup(id); 56 if (!uc_drv) { 57 dm_warn("Cannot find uclass for id %d: please add the UCLASS_DRIVER() declaration for this UCLASS_... id\n", 58 id); 59 return -ENOENT; 60 } 61 if (uc_drv->ops) { 62 dm_warn("No ops for uclass id %d\n", id); 63 return -EINVAL; 64 } 65 uc = calloc(1, sizeof(*uc)); 66 if (!uc) 67 return -ENOMEM; 68 if (uc_drv->priv_auto_alloc_size) { 69 uc->priv = calloc(1, uc_drv->priv_auto_alloc_size); 70 if (!uc->priv) { 71 ret = -ENOMEM; 72 goto fail_mem; 73 } 74 } 75 uc->uc_drv = uc_drv; 76 INIT_LIST_HEAD(&uc->sibling_node); 77 INIT_LIST_HEAD(&uc->dev_head); 78 list_add(&uc->sibling_node, &DM_UCLASS_ROOT_NON_CONST); 79 80 if (uc_drv->init) { 81 ret = uc_drv->init(uc); 82 if (ret) 83 goto fail; 84 } 85 86 *ucp = uc; 87 88 return 0; 89 fail: 90 if (uc_drv->priv_auto_alloc_size) { 91 free(uc->priv); 92 uc->priv = NULL; 93 } 94 list_del(&uc->sibling_node); 95 fail_mem: 96 free(uc); 97 98 return ret; 99 } 100 101 int uclass_destroy(struct uclass *uc) 102 { 103 struct uclass_driver *uc_drv; 104 struct udevice *dev, *tmp; 105 int ret; 106 107 list_for_each_entry_safe(dev, tmp, &uc->dev_head, uclass_node) { 108 ret = device_remove(dev); 109 if (ret) 110 return ret; 111 ret = device_unbind(dev); 112 if (ret) 113 return ret; 114 } 115 116 uc_drv = uc->uc_drv; 117 if (uc_drv->destroy) 118 uc_drv->destroy(uc); 119 list_del(&uc->sibling_node); 120 if (uc_drv->priv_auto_alloc_size) 121 free(uc->priv); 122 free(uc); 123 124 return 0; 125 } 126 127 int uclass_get(enum uclass_id id, struct uclass **ucp) 128 { 129 struct uclass *uc; 130 131 *ucp = NULL; 132 uc = uclass_find(id); 133 if (!uc) 134 return uclass_add(id, ucp); 135 *ucp = uc; 136 137 return 0; 138 } 139 140 int uclass_find_device(enum uclass_id id, int index, struct udevice **devp) 141 { 142 struct uclass *uc; 143 struct udevice *dev; 144 int ret; 145 146 *devp = NULL; 147 ret = uclass_get(id, &uc); 148 if (ret) 149 return ret; 150 151 list_for_each_entry(dev, &uc->dev_head, uclass_node) { 152 if (!index--) { 153 *devp = dev; 154 return 0; 155 } 156 } 157 158 return -ENODEV; 159 } 160 161 /** 162 * uclass_get_device_tail() - handle the end of a get_device call 163 * 164 * This handles returning an error or probing a device as needed. 165 * 166 * @dev: Device that needs to be probed 167 * @ret: Error to return. If non-zero then the device is not probed 168 * @devp: Returns the value of 'dev' if there is no error 169 * @return ret, if non-zero, else the result of the device_probe() call 170 */ 171 static int uclass_get_device_tail(struct udevice *dev, int ret, 172 struct udevice **devp) 173 { 174 if (ret) 175 return ret; 176 177 ret = device_probe(dev); 178 if (ret) 179 return ret; 180 181 *devp = dev; 182 183 return 0; 184 } 185 186 int uclass_get_device(enum uclass_id id, int index, struct udevice **devp) 187 { 188 struct udevice *dev; 189 int ret; 190 191 *devp = NULL; 192 ret = uclass_find_device(id, index, &dev); 193 return uclass_get_device_tail(dev, ret, devp); 194 } 195 196 int uclass_first_device(enum uclass_id id, struct udevice **devp) 197 { 198 struct uclass *uc; 199 struct udevice *dev; 200 int ret; 201 202 *devp = NULL; 203 ret = uclass_get(id, &uc); 204 if (ret) 205 return ret; 206 if (list_empty(&uc->dev_head)) 207 return 0; 208 209 dev = list_first_entry(&uc->dev_head, struct udevice, uclass_node); 210 ret = device_probe(dev); 211 if (ret) 212 return ret; 213 *devp = dev; 214 215 return 0; 216 } 217 218 int uclass_next_device(struct udevice **devp) 219 { 220 struct udevice *dev = *devp; 221 int ret; 222 223 *devp = NULL; 224 if (list_is_last(&dev->uclass_node, &dev->uclass->dev_head)) 225 return 0; 226 227 dev = list_entry(dev->uclass_node.next, struct udevice, 228 uclass_node); 229 ret = device_probe(dev); 230 if (ret) 231 return ret; 232 *devp = dev; 233 234 return 0; 235 } 236 237 int uclass_bind_device(struct udevice *dev) 238 { 239 struct uclass *uc; 240 int ret; 241 242 uc = dev->uclass; 243 244 list_add_tail(&dev->uclass_node, &uc->dev_head); 245 246 if (uc->uc_drv->post_bind) { 247 ret = uc->uc_drv->post_bind(dev); 248 if (ret) { 249 list_del(&dev->uclass_node); 250 return ret; 251 } 252 } 253 254 return 0; 255 } 256 257 int uclass_unbind_device(struct udevice *dev) 258 { 259 struct uclass *uc; 260 int ret; 261 262 uc = dev->uclass; 263 if (uc->uc_drv->pre_unbind) { 264 ret = uc->uc_drv->pre_unbind(dev); 265 if (ret) 266 return ret; 267 } 268 269 list_del(&dev->uclass_node); 270 return 0; 271 } 272 273 int uclass_post_probe_device(struct udevice *dev) 274 { 275 struct uclass_driver *uc_drv = dev->uclass->uc_drv; 276 277 if (uc_drv->post_probe) 278 return uc_drv->post_probe(dev); 279 280 return 0; 281 } 282 283 int uclass_pre_remove_device(struct udevice *dev) 284 { 285 struct uclass_driver *uc_drv; 286 struct uclass *uc; 287 int ret; 288 289 uc = dev->uclass; 290 uc_drv = uc->uc_drv; 291 if (uc->uc_drv->pre_remove) { 292 ret = uc->uc_drv->pre_remove(dev); 293 if (ret) 294 return ret; 295 } 296 if (uc_drv->per_device_auto_alloc_size) { 297 free(dev->uclass_priv); 298 dev->uclass_priv = NULL; 299 } 300 301 return 0; 302 } 303