1608f26c5SMasahiro Yamada /* 2608f26c5SMasahiro Yamada * Copyright (C) 2015 Masahiro Yamada <yamada.masahiro@socionext.com> 3608f26c5SMasahiro Yamada * 4608f26c5SMasahiro Yamada * Based on the original work in Linux by 5608f26c5SMasahiro Yamada * Copyright (c) 2006 SUSE Linux Products GmbH 6608f26c5SMasahiro Yamada * Copyright (c) 2006 Tejun Heo <teheo@suse.de> 7608f26c5SMasahiro Yamada * 8608f26c5SMasahiro Yamada * SPDX-License-Identifier: GPL-2.0+ 9608f26c5SMasahiro Yamada */ 10608f26c5SMasahiro Yamada 11608f26c5SMasahiro Yamada #include <common.h> 12608f26c5SMasahiro Yamada #include <linux/compat.h> 13608f26c5SMasahiro Yamada #include <linux/kernel.h> 14608f26c5SMasahiro Yamada #include <linux/list.h> 15608f26c5SMasahiro Yamada #include <dm/device.h> 16*40b6f2d0SMasahiro Yamada #include <dm/root.h> 17*40b6f2d0SMasahiro Yamada #include <dm/util.h> 18608f26c5SMasahiro Yamada 19608f26c5SMasahiro Yamada /** 20608f26c5SMasahiro Yamada * struct devres - Bookkeeping info for managed device resource 21608f26c5SMasahiro Yamada * @entry: List to associate this structure with a device 22608f26c5SMasahiro Yamada * @release: Callback invoked when this resource is released 23608f26c5SMasahiro Yamada * @probe: Flag to show when this resource was allocated 24608f26c5SMasahiro Yamada (true = probe, false = bind) 25608f26c5SMasahiro Yamada * @name: Name of release function 26608f26c5SMasahiro Yamada * @size: Size of resource data 27608f26c5SMasahiro Yamada * @data: Resource data 28608f26c5SMasahiro Yamada */ 29608f26c5SMasahiro Yamada struct devres { 30608f26c5SMasahiro Yamada struct list_head entry; 31608f26c5SMasahiro Yamada dr_release_t release; 32608f26c5SMasahiro Yamada bool probe; 33608f26c5SMasahiro Yamada #ifdef CONFIG_DEBUG_DEVRES 34608f26c5SMasahiro Yamada const char *name; 35608f26c5SMasahiro Yamada size_t size; 36608f26c5SMasahiro Yamada #endif 37608f26c5SMasahiro Yamada unsigned long long data[]; 38608f26c5SMasahiro Yamada }; 39608f26c5SMasahiro Yamada 40608f26c5SMasahiro Yamada #ifdef CONFIG_DEBUG_DEVRES 41608f26c5SMasahiro Yamada static void set_node_dbginfo(struct devres *dr, const char *name, size_t size) 42608f26c5SMasahiro Yamada { 43608f26c5SMasahiro Yamada dr->name = name; 44608f26c5SMasahiro Yamada dr->size = size; 45608f26c5SMasahiro Yamada } 46608f26c5SMasahiro Yamada 47608f26c5SMasahiro Yamada static void devres_log(struct udevice *dev, struct devres *dr, 48608f26c5SMasahiro Yamada const char *op) 49608f26c5SMasahiro Yamada { 50608f26c5SMasahiro Yamada printf("%s: DEVRES %3s %p %s (%lu bytes)\n", 51608f26c5SMasahiro Yamada dev->name, op, dr, dr->name, (unsigned long)dr->size); 52608f26c5SMasahiro Yamada } 53608f26c5SMasahiro Yamada #else /* CONFIG_DEBUG_DEVRES */ 54608f26c5SMasahiro Yamada #define set_node_dbginfo(dr, n, s) do {} while (0) 55608f26c5SMasahiro Yamada #define devres_log(dev, dr, op) do {} while (0) 56608f26c5SMasahiro Yamada #endif 57608f26c5SMasahiro Yamada 58608f26c5SMasahiro Yamada #if CONFIG_DEBUG_DEVRES 59608f26c5SMasahiro Yamada void *__devres_alloc(dr_release_t release, size_t size, gfp_t gfp, 60608f26c5SMasahiro Yamada const char *name) 61608f26c5SMasahiro Yamada #else 62608f26c5SMasahiro Yamada void *_devres_alloc(dr_release_t release, size_t size, gfp_t gfp) 63608f26c5SMasahiro Yamada #endif 64608f26c5SMasahiro Yamada { 65608f26c5SMasahiro Yamada size_t tot_size = sizeof(struct devres) + size; 66608f26c5SMasahiro Yamada struct devres *dr; 67608f26c5SMasahiro Yamada 68608f26c5SMasahiro Yamada dr = kmalloc(tot_size, gfp); 69608f26c5SMasahiro Yamada if (unlikely(!dr)) 70608f26c5SMasahiro Yamada return NULL; 71608f26c5SMasahiro Yamada 72608f26c5SMasahiro Yamada INIT_LIST_HEAD(&dr->entry); 73608f26c5SMasahiro Yamada dr->release = release; 74608f26c5SMasahiro Yamada set_node_dbginfo(dr, name, size); 75608f26c5SMasahiro Yamada 76608f26c5SMasahiro Yamada return dr->data; 77608f26c5SMasahiro Yamada } 78608f26c5SMasahiro Yamada 79608f26c5SMasahiro Yamada void devres_free(void *res) 80608f26c5SMasahiro Yamada { 81608f26c5SMasahiro Yamada if (res) { 82608f26c5SMasahiro Yamada struct devres *dr = container_of(res, struct devres, data); 83608f26c5SMasahiro Yamada 84608f26c5SMasahiro Yamada BUG_ON(!list_empty(&dr->entry)); 85608f26c5SMasahiro Yamada kfree(dr); 86608f26c5SMasahiro Yamada } 87608f26c5SMasahiro Yamada } 88608f26c5SMasahiro Yamada 89608f26c5SMasahiro Yamada void devres_add(struct udevice *dev, void *res) 90608f26c5SMasahiro Yamada { 91608f26c5SMasahiro Yamada struct devres *dr = container_of(res, struct devres, data); 92608f26c5SMasahiro Yamada 93608f26c5SMasahiro Yamada devres_log(dev, dr, "ADD"); 94608f26c5SMasahiro Yamada BUG_ON(!list_empty(&dr->entry)); 95608f26c5SMasahiro Yamada dr->probe = dev->flags & DM_FLAG_BOUND ? true : false; 96608f26c5SMasahiro Yamada list_add_tail(&dr->entry, &dev->devres_head); 97608f26c5SMasahiro Yamada } 98608f26c5SMasahiro Yamada 99608f26c5SMasahiro Yamada void *devres_find(struct udevice *dev, dr_release_t release, 100608f26c5SMasahiro Yamada dr_match_t match, void *match_data) 101608f26c5SMasahiro Yamada { 102608f26c5SMasahiro Yamada struct devres *dr; 103608f26c5SMasahiro Yamada 104608f26c5SMasahiro Yamada list_for_each_entry_reverse(dr, &dev->devres_head, entry) { 105608f26c5SMasahiro Yamada if (dr->release != release) 106608f26c5SMasahiro Yamada continue; 107608f26c5SMasahiro Yamada if (match && !match(dev, dr->data, match_data)) 108608f26c5SMasahiro Yamada continue; 109608f26c5SMasahiro Yamada return dr->data; 110608f26c5SMasahiro Yamada } 111608f26c5SMasahiro Yamada 112608f26c5SMasahiro Yamada return NULL; 113608f26c5SMasahiro Yamada } 114608f26c5SMasahiro Yamada 115608f26c5SMasahiro Yamada void *devres_get(struct udevice *dev, void *new_res, 116608f26c5SMasahiro Yamada dr_match_t match, void *match_data) 117608f26c5SMasahiro Yamada { 118608f26c5SMasahiro Yamada struct devres *new_dr = container_of(new_res, struct devres, data); 119608f26c5SMasahiro Yamada void *res; 120608f26c5SMasahiro Yamada 121608f26c5SMasahiro Yamada res = devres_find(dev, new_dr->release, match, match_data); 122608f26c5SMasahiro Yamada if (!res) { 123608f26c5SMasahiro Yamada devres_add(dev, new_res); 124608f26c5SMasahiro Yamada res = new_res; 125608f26c5SMasahiro Yamada new_res = NULL; 126608f26c5SMasahiro Yamada } 127608f26c5SMasahiro Yamada devres_free(new_res); 128608f26c5SMasahiro Yamada 129608f26c5SMasahiro Yamada return res; 130608f26c5SMasahiro Yamada } 131608f26c5SMasahiro Yamada 132608f26c5SMasahiro Yamada void *devres_remove(struct udevice *dev, dr_release_t release, 133608f26c5SMasahiro Yamada dr_match_t match, void *match_data) 134608f26c5SMasahiro Yamada { 135608f26c5SMasahiro Yamada void *res; 136608f26c5SMasahiro Yamada 137608f26c5SMasahiro Yamada res = devres_find(dev, release, match, match_data); 138608f26c5SMasahiro Yamada if (res) { 139608f26c5SMasahiro Yamada struct devres *dr = container_of(res, struct devres, data); 140608f26c5SMasahiro Yamada 141608f26c5SMasahiro Yamada list_del_init(&dr->entry); 142608f26c5SMasahiro Yamada devres_log(dev, dr, "REM"); 143608f26c5SMasahiro Yamada } 144608f26c5SMasahiro Yamada 145608f26c5SMasahiro Yamada return res; 146608f26c5SMasahiro Yamada } 147608f26c5SMasahiro Yamada 148608f26c5SMasahiro Yamada int devres_destroy(struct udevice *dev, dr_release_t release, 149608f26c5SMasahiro Yamada dr_match_t match, void *match_data) 150608f26c5SMasahiro Yamada { 151608f26c5SMasahiro Yamada void *res; 152608f26c5SMasahiro Yamada 153608f26c5SMasahiro Yamada res = devres_remove(dev, release, match, match_data); 154608f26c5SMasahiro Yamada if (unlikely(!res)) 155608f26c5SMasahiro Yamada return -ENOENT; 156608f26c5SMasahiro Yamada 157608f26c5SMasahiro Yamada devres_free(res); 158608f26c5SMasahiro Yamada return 0; 159608f26c5SMasahiro Yamada } 160608f26c5SMasahiro Yamada 161608f26c5SMasahiro Yamada int devres_release(struct udevice *dev, dr_release_t release, 162608f26c5SMasahiro Yamada dr_match_t match, void *match_data) 163608f26c5SMasahiro Yamada { 164608f26c5SMasahiro Yamada void *res; 165608f26c5SMasahiro Yamada 166608f26c5SMasahiro Yamada res = devres_remove(dev, release, match, match_data); 167608f26c5SMasahiro Yamada if (unlikely(!res)) 168608f26c5SMasahiro Yamada return -ENOENT; 169608f26c5SMasahiro Yamada 170608f26c5SMasahiro Yamada (*release)(dev, res); 171608f26c5SMasahiro Yamada devres_free(res); 172608f26c5SMasahiro Yamada return 0; 173608f26c5SMasahiro Yamada } 174608f26c5SMasahiro Yamada 175608f26c5SMasahiro Yamada static void release_nodes(struct udevice *dev, struct list_head *head, 176608f26c5SMasahiro Yamada bool probe_only) 177608f26c5SMasahiro Yamada { 178608f26c5SMasahiro Yamada struct devres *dr, *tmp; 179608f26c5SMasahiro Yamada 180608f26c5SMasahiro Yamada list_for_each_entry_safe_reverse(dr, tmp, head, entry) { 181608f26c5SMasahiro Yamada if (probe_only && !dr->probe) 182608f26c5SMasahiro Yamada break; 183608f26c5SMasahiro Yamada devres_log(dev, dr, "REL"); 184608f26c5SMasahiro Yamada dr->release(dev, dr->data); 185608f26c5SMasahiro Yamada list_del(&dr->entry); 186608f26c5SMasahiro Yamada kfree(dr); 187608f26c5SMasahiro Yamada } 188608f26c5SMasahiro Yamada } 189608f26c5SMasahiro Yamada 190608f26c5SMasahiro Yamada void devres_release_probe(struct udevice *dev) 191608f26c5SMasahiro Yamada { 192608f26c5SMasahiro Yamada release_nodes(dev, &dev->devres_head, true); 193608f26c5SMasahiro Yamada } 194608f26c5SMasahiro Yamada 195608f26c5SMasahiro Yamada void devres_release_all(struct udevice *dev) 196608f26c5SMasahiro Yamada { 197608f26c5SMasahiro Yamada release_nodes(dev, &dev->devres_head, false); 198608f26c5SMasahiro Yamada } 1992b07f685SMasahiro Yamada 200*40b6f2d0SMasahiro Yamada #ifdef CONFIG_DEBUG_DEVRES 201*40b6f2d0SMasahiro Yamada static void dump_resources(struct udevice *dev, int depth) 202*40b6f2d0SMasahiro Yamada { 203*40b6f2d0SMasahiro Yamada struct devres *dr; 204*40b6f2d0SMasahiro Yamada struct udevice *child; 205*40b6f2d0SMasahiro Yamada 206*40b6f2d0SMasahiro Yamada printf("- %s\n", dev->name); 207*40b6f2d0SMasahiro Yamada 208*40b6f2d0SMasahiro Yamada list_for_each_entry(dr, &dev->devres_head, entry) 209*40b6f2d0SMasahiro Yamada printf(" %p (%lu byte) %s %s\n", dr, 210*40b6f2d0SMasahiro Yamada (unsigned long)dr->size, dr->name, 211*40b6f2d0SMasahiro Yamada dr->probe ? "PROBE" : "BIND"); 212*40b6f2d0SMasahiro Yamada 213*40b6f2d0SMasahiro Yamada list_for_each_entry(child, &dev->child_head, sibling_node) 214*40b6f2d0SMasahiro Yamada dump_resources(child, depth + 1); 215*40b6f2d0SMasahiro Yamada } 216*40b6f2d0SMasahiro Yamada 217*40b6f2d0SMasahiro Yamada void dm_dump_devres(void) 218*40b6f2d0SMasahiro Yamada { 219*40b6f2d0SMasahiro Yamada struct udevice *root; 220*40b6f2d0SMasahiro Yamada 221*40b6f2d0SMasahiro Yamada root = dm_root(); 222*40b6f2d0SMasahiro Yamada if (root) 223*40b6f2d0SMasahiro Yamada dump_resources(root, 0); 224*40b6f2d0SMasahiro Yamada } 225*40b6f2d0SMasahiro Yamada #endif 226*40b6f2d0SMasahiro Yamada 2272b07f685SMasahiro Yamada /* 2282b07f685SMasahiro Yamada * Managed kmalloc/kfree 2292b07f685SMasahiro Yamada */ 2302b07f685SMasahiro Yamada static void devm_kmalloc_release(struct udevice *dev, void *res) 2312b07f685SMasahiro Yamada { 2322b07f685SMasahiro Yamada /* noop */ 2332b07f685SMasahiro Yamada } 2342b07f685SMasahiro Yamada 2352b07f685SMasahiro Yamada static int devm_kmalloc_match(struct udevice *dev, void *res, void *data) 2362b07f685SMasahiro Yamada { 2372b07f685SMasahiro Yamada return res == data; 2382b07f685SMasahiro Yamada } 2392b07f685SMasahiro Yamada 2402b07f685SMasahiro Yamada void *devm_kmalloc(struct udevice *dev, size_t size, gfp_t gfp) 2412b07f685SMasahiro Yamada { 2422b07f685SMasahiro Yamada void *data; 2432b07f685SMasahiro Yamada 2442b07f685SMasahiro Yamada data = _devres_alloc(devm_kmalloc_release, size, gfp); 2452b07f685SMasahiro Yamada if (unlikely(!data)) 2462b07f685SMasahiro Yamada return NULL; 2472b07f685SMasahiro Yamada 2482b07f685SMasahiro Yamada devres_add(dev, data); 2492b07f685SMasahiro Yamada 2502b07f685SMasahiro Yamada return data; 2512b07f685SMasahiro Yamada } 2522b07f685SMasahiro Yamada 2532b07f685SMasahiro Yamada void devm_kfree(struct udevice *dev, void *p) 2542b07f685SMasahiro Yamada { 2552b07f685SMasahiro Yamada int rc; 2562b07f685SMasahiro Yamada 2572b07f685SMasahiro Yamada rc = devres_destroy(dev, devm_kmalloc_release, devm_kmalloc_match, p); 2582b07f685SMasahiro Yamada WARN_ON(rc); 2592b07f685SMasahiro Yamada } 260