1// SPDX-License-Identifier: GPL-2.0 2#include <linux/clk.h> 3#include <linux/device.h> 4#include <linux/export.h> 5#include <linux/gfp.h> 6 7struct devm_clk_state { 8 struct clk *clk; 9 void (*exit)(struct clk *clk); 10}; 11 12static void devm_clk_release(struct device *dev, void *res) 13{ 14 struct devm_clk_state *state = res; 15 16 if (state->exit) 17 state->exit(state->clk); 18 19 clk_put(state->clk); 20} 21 22static struct clk *__devm_clk_get(struct device *dev, const char *id, 23 struct clk *(*get)(struct device *dev, const char *id), 24 int (*init)(struct clk *clk), 25 void (*exit)(struct clk *clk)) 26{ 27 struct devm_clk_state *state; 28 struct clk *clk; 29 int ret; 30 31 state = devres_alloc(devm_clk_release, sizeof(*state), GFP_KERNEL); 32 if (!state) 33 return ERR_PTR(-ENOMEM); 34 35 clk = get(dev, id); 36 if (IS_ERR(clk)) { 37 ret = PTR_ERR(clk); 38 goto err_clk_get; 39 } 40 41 if (init) { 42 ret = init(clk); 43 if (ret) 44 goto err_clk_init; 45 } 46 47 state->clk = clk; 48 state->exit = exit; 49 50 devres_add(dev, state); 51 52 return clk; 53 54err_clk_init: 55 56 clk_put(clk); 57err_clk_get: 58 59 devres_free(state); 60 return ERR_PTR(ret); 61} 62 63struct clk *devm_clk_get(struct device *dev, const char *id) 64{ 65 return __devm_clk_get(dev, id, clk_get, NULL, NULL); 66} 67EXPORT_SYMBOL(devm_clk_get); 68 69struct clk *devm_clk_get_prepared(struct device *dev, const char *id) 70{ 71 return __devm_clk_get(dev, id, clk_get, clk_prepare, clk_unprepare); 72} 73EXPORT_SYMBOL_GPL(devm_clk_get_prepared); 74 75struct clk *devm_clk_get_enabled(struct device *dev, const char *id) 76{ 77 return __devm_clk_get(dev, id, clk_get, 78 clk_prepare_enable, clk_disable_unprepare); 79} 80EXPORT_SYMBOL_GPL(devm_clk_get_enabled); 81 82struct clk *devm_clk_get_optional(struct device *dev, const char *id) 83{ 84 return __devm_clk_get(dev, id, clk_get_optional, NULL, NULL); 85} 86EXPORT_SYMBOL(devm_clk_get_optional); 87 88struct clk *devm_clk_get_optional_prepared(struct device *dev, const char *id) 89{ 90 return __devm_clk_get(dev, id, clk_get_optional, 91 clk_prepare, clk_unprepare); 92} 93EXPORT_SYMBOL_GPL(devm_clk_get_optional_prepared); 94 95struct clk *devm_clk_get_optional_enabled(struct device *dev, const char *id) 96{ 97 return __devm_clk_get(dev, id, clk_get_optional, 98 clk_prepare_enable, clk_disable_unprepare); 99} 100EXPORT_SYMBOL_GPL(devm_clk_get_optional_enabled); 101 102struct clk_bulk_devres { 103 struct clk_bulk_data *clks; 104 int num_clks; 105}; 106 107static void devm_clk_bulk_release(struct device *dev, void *res) 108{ 109 struct clk_bulk_devres *devres = res; 110 111 clk_bulk_put(devres->num_clks, devres->clks); 112} 113 114static int __devm_clk_bulk_get(struct device *dev, int num_clks, 115 struct clk_bulk_data *clks, bool optional) 116{ 117 struct clk_bulk_devres *devres; 118 int ret; 119 120 devres = devres_alloc(devm_clk_bulk_release, 121 sizeof(*devres), GFP_KERNEL); 122 if (!devres) 123 return -ENOMEM; 124 125 if (optional) 126 ret = clk_bulk_get_optional(dev, num_clks, clks); 127 else 128 ret = clk_bulk_get(dev, num_clks, clks); 129 if (!ret) { 130 devres->clks = clks; 131 devres->num_clks = num_clks; 132 devres_add(dev, devres); 133 } else { 134 devres_free(devres); 135 } 136 137 return ret; 138} 139 140int __must_check devm_clk_bulk_get(struct device *dev, int num_clks, 141 struct clk_bulk_data *clks) 142{ 143 return __devm_clk_bulk_get(dev, num_clks, clks, false); 144} 145EXPORT_SYMBOL_GPL(devm_clk_bulk_get); 146 147int __must_check devm_clk_bulk_get_optional(struct device *dev, int num_clks, 148 struct clk_bulk_data *clks) 149{ 150 return __devm_clk_bulk_get(dev, num_clks, clks, true); 151} 152EXPORT_SYMBOL_GPL(devm_clk_bulk_get_optional); 153 154static void devm_clk_bulk_release_all(struct device *dev, void *res) 155{ 156 struct clk_bulk_devres *devres = res; 157 158 clk_bulk_put_all(devres->num_clks, devres->clks); 159} 160 161int __must_check devm_clk_bulk_get_all(struct device *dev, 162 struct clk_bulk_data **clks) 163{ 164 struct clk_bulk_devres *devres; 165 int ret; 166 167 devres = devres_alloc(devm_clk_bulk_release_all, 168 sizeof(*devres), GFP_KERNEL); 169 if (!devres) 170 return -ENOMEM; 171 172 ret = clk_bulk_get_all(dev, &devres->clks); 173 if (ret > 0) { 174 *clks = devres->clks; 175 devres->num_clks = ret; 176 devres_add(dev, devres); 177 } else { 178 devres_free(devres); 179 } 180 181 return ret; 182} 183EXPORT_SYMBOL_GPL(devm_clk_bulk_get_all); 184 185static int devm_clk_match(struct device *dev, void *res, void *data) 186{ 187 struct clk **c = res; 188 if (!c || !*c) { 189 WARN_ON(!c || !*c); 190 return 0; 191 } 192 return *c == data; 193} 194 195void devm_clk_put(struct device *dev, struct clk *clk) 196{ 197 int ret; 198 199 ret = devres_release(dev, devm_clk_release, devm_clk_match, clk); 200 201 WARN_ON(ret); 202} 203EXPORT_SYMBOL(devm_clk_put); 204 205struct clk *devm_get_clk_from_child(struct device *dev, 206 struct device_node *np, const char *con_id) 207{ 208 struct devm_clk_state *state; 209 struct clk *clk; 210 211 state = devres_alloc(devm_clk_release, sizeof(*state), GFP_KERNEL); 212 if (!state) 213 return ERR_PTR(-ENOMEM); 214 215 clk = of_clk_get_by_name(np, con_id); 216 if (!IS_ERR(clk)) { 217 state->clk = clk; 218 devres_add(dev, state); 219 } else { 220 devres_free(state); 221 } 222 223 return clk; 224} 225EXPORT_SYMBOL(devm_get_clk_from_child); 226