1 /* 2 * Copyright (C) 2015 Masahiro Yamada <yamada.masahiro@socionext.com> 3 * 4 * Based on the original work in Linux by 5 * Copyright (c) 2006 SUSE Linux Products GmbH 6 * Copyright (c) 2006 Tejun Heo <teheo@suse.de> 7 * 8 * SPDX-License-Identifier: GPL-2.0+ 9 */ 10 11 #include <common.h> 12 #include <linux/compat.h> 13 #include <linux/kernel.h> 14 #include <linux/list.h> 15 #include <dm/device.h> 16 17 /** 18 * struct devres - Bookkeeping info for managed device resource 19 * @entry: List to associate this structure with a device 20 * @release: Callback invoked when this resource is released 21 * @probe: Flag to show when this resource was allocated 22 (true = probe, false = bind) 23 * @name: Name of release function 24 * @size: Size of resource data 25 * @data: Resource data 26 */ 27 struct devres { 28 struct list_head entry; 29 dr_release_t release; 30 bool probe; 31 #ifdef CONFIG_DEBUG_DEVRES 32 const char *name; 33 size_t size; 34 #endif 35 unsigned long long data[]; 36 }; 37 38 #ifdef CONFIG_DEBUG_DEVRES 39 static void set_node_dbginfo(struct devres *dr, const char *name, size_t size) 40 { 41 dr->name = name; 42 dr->size = size; 43 } 44 45 static void devres_log(struct udevice *dev, struct devres *dr, 46 const char *op) 47 { 48 printf("%s: DEVRES %3s %p %s (%lu bytes)\n", 49 dev->name, op, dr, dr->name, (unsigned long)dr->size); 50 } 51 #else /* CONFIG_DEBUG_DEVRES */ 52 #define set_node_dbginfo(dr, n, s) do {} while (0) 53 #define devres_log(dev, dr, op) do {} while (0) 54 #endif 55 56 #if CONFIG_DEBUG_DEVRES 57 void *__devres_alloc(dr_release_t release, size_t size, gfp_t gfp, 58 const char *name) 59 #else 60 void *_devres_alloc(dr_release_t release, size_t size, gfp_t gfp) 61 #endif 62 { 63 size_t tot_size = sizeof(struct devres) + size; 64 struct devres *dr; 65 66 dr = kmalloc(tot_size, gfp); 67 if (unlikely(!dr)) 68 return NULL; 69 70 INIT_LIST_HEAD(&dr->entry); 71 dr->release = release; 72 set_node_dbginfo(dr, name, size); 73 74 return dr->data; 75 } 76 77 void devres_free(void *res) 78 { 79 if (res) { 80 struct devres *dr = container_of(res, struct devres, data); 81 82 BUG_ON(!list_empty(&dr->entry)); 83 kfree(dr); 84 } 85 } 86 87 void devres_add(struct udevice *dev, void *res) 88 { 89 struct devres *dr = container_of(res, struct devres, data); 90 91 devres_log(dev, dr, "ADD"); 92 BUG_ON(!list_empty(&dr->entry)); 93 dr->probe = dev->flags & DM_FLAG_BOUND ? true : false; 94 list_add_tail(&dr->entry, &dev->devres_head); 95 } 96 97 void *devres_find(struct udevice *dev, dr_release_t release, 98 dr_match_t match, void *match_data) 99 { 100 struct devres *dr; 101 102 list_for_each_entry_reverse(dr, &dev->devres_head, entry) { 103 if (dr->release != release) 104 continue; 105 if (match && !match(dev, dr->data, match_data)) 106 continue; 107 return dr->data; 108 } 109 110 return NULL; 111 } 112 113 void *devres_get(struct udevice *dev, void *new_res, 114 dr_match_t match, void *match_data) 115 { 116 struct devres *new_dr = container_of(new_res, struct devres, data); 117 void *res; 118 119 res = devres_find(dev, new_dr->release, match, match_data); 120 if (!res) { 121 devres_add(dev, new_res); 122 res = new_res; 123 new_res = NULL; 124 } 125 devres_free(new_res); 126 127 return res; 128 } 129 130 void *devres_remove(struct udevice *dev, dr_release_t release, 131 dr_match_t match, void *match_data) 132 { 133 void *res; 134 135 res = devres_find(dev, release, match, match_data); 136 if (res) { 137 struct devres *dr = container_of(res, struct devres, data); 138 139 list_del_init(&dr->entry); 140 devres_log(dev, dr, "REM"); 141 } 142 143 return res; 144 } 145 146 int devres_destroy(struct udevice *dev, dr_release_t release, 147 dr_match_t match, void *match_data) 148 { 149 void *res; 150 151 res = devres_remove(dev, release, match, match_data); 152 if (unlikely(!res)) 153 return -ENOENT; 154 155 devres_free(res); 156 return 0; 157 } 158 159 int devres_release(struct udevice *dev, dr_release_t release, 160 dr_match_t match, void *match_data) 161 { 162 void *res; 163 164 res = devres_remove(dev, release, match, match_data); 165 if (unlikely(!res)) 166 return -ENOENT; 167 168 (*release)(dev, res); 169 devres_free(res); 170 return 0; 171 } 172 173 static void release_nodes(struct udevice *dev, struct list_head *head, 174 bool probe_only) 175 { 176 struct devres *dr, *tmp; 177 178 list_for_each_entry_safe_reverse(dr, tmp, head, entry) { 179 if (probe_only && !dr->probe) 180 break; 181 devres_log(dev, dr, "REL"); 182 dr->release(dev, dr->data); 183 list_del(&dr->entry); 184 kfree(dr); 185 } 186 } 187 188 void devres_release_probe(struct udevice *dev) 189 { 190 release_nodes(dev, &dev->devres_head, true); 191 } 192 193 void devres_release_all(struct udevice *dev) 194 { 195 release_nodes(dev, &dev->devres_head, false); 196 } 197