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, &gd->uclass_root); 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 device *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 device **devp) 141 { 142 struct uclass *uc; 143 struct device *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 int uclass_get_device(enum uclass_id id, int index, struct device **devp) 162 { 163 struct device *dev; 164 int ret; 165 166 *devp = NULL; 167 ret = uclass_find_device(id, index, &dev); 168 if (ret) 169 return ret; 170 171 ret = device_probe(dev); 172 if (ret) 173 return ret; 174 175 *devp = dev; 176 177 return 0; 178 } 179 180 int uclass_first_device(enum uclass_id id, struct device **devp) 181 { 182 struct uclass *uc; 183 struct device *dev; 184 int ret; 185 186 *devp = NULL; 187 ret = uclass_get(id, &uc); 188 if (ret) 189 return ret; 190 if (list_empty(&uc->dev_head)) 191 return 0; 192 193 dev = list_first_entry(&uc->dev_head, struct device, uclass_node); 194 ret = device_probe(dev); 195 if (ret) 196 return ret; 197 *devp = dev; 198 199 return 0; 200 } 201 202 int uclass_next_device(struct device **devp) 203 { 204 struct device *dev = *devp; 205 int ret; 206 207 *devp = NULL; 208 if (list_is_last(&dev->uclass_node, &dev->uclass->dev_head)) 209 return 0; 210 211 dev = list_entry(dev->uclass_node.next, struct device, uclass_node); 212 ret = device_probe(dev); 213 if (ret) 214 return ret; 215 *devp = dev; 216 217 return 0; 218 } 219 220 int uclass_bind_device(struct device *dev) 221 { 222 struct uclass *uc; 223 int ret; 224 225 uc = dev->uclass; 226 227 list_add_tail(&dev->uclass_node, &uc->dev_head); 228 229 if (uc->uc_drv->post_bind) { 230 ret = uc->uc_drv->post_bind(dev); 231 if (ret) { 232 list_del(&dev->uclass_node); 233 return ret; 234 } 235 } 236 237 return 0; 238 } 239 240 int uclass_unbind_device(struct device *dev) 241 { 242 struct uclass *uc; 243 int ret; 244 245 uc = dev->uclass; 246 if (uc->uc_drv->pre_unbind) { 247 ret = uc->uc_drv->pre_unbind(dev); 248 if (ret) 249 return ret; 250 } 251 252 list_del(&dev->uclass_node); 253 return 0; 254 } 255 256 int uclass_post_probe_device(struct device *dev) 257 { 258 struct uclass_driver *uc_drv = dev->uclass->uc_drv; 259 260 if (uc_drv->post_probe) 261 return uc_drv->post_probe(dev); 262 263 return 0; 264 } 265 266 int uclass_pre_remove_device(struct device *dev) 267 { 268 struct uclass_driver *uc_drv; 269 struct uclass *uc; 270 int ret; 271 272 uc = dev->uclass; 273 uc_drv = uc->uc_drv; 274 if (uc->uc_drv->pre_remove) { 275 ret = uc->uc_drv->pre_remove(dev); 276 if (ret) 277 return ret; 278 } 279 if (uc_drv->per_device_auto_alloc_size) { 280 free(dev->uclass_priv); 281 dev->uclass_priv = NULL; 282 } 283 284 return 0; 285 } 286