294 lines · c
1// SPDX-License-Identifier: GPL-2.02#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 *devm_clk_get_optional_enabled_with_rate(struct device *dev,103 const char *id,104 unsigned long rate)105{106 struct clk *clk;107 int ret;108 109 clk = __devm_clk_get(dev, id, clk_get_optional, NULL,110 clk_disable_unprepare);111 if (IS_ERR(clk))112 return ERR_CAST(clk);113 114 ret = clk_set_rate(clk, rate);115 if (ret)116 goto out_put_clk;117 118 ret = clk_prepare_enable(clk);119 if (ret)120 goto out_put_clk;121 122 return clk;123 124out_put_clk:125 devm_clk_put(dev, clk);126 return ERR_PTR(ret);127}128EXPORT_SYMBOL_GPL(devm_clk_get_optional_enabled_with_rate);129 130struct clk_bulk_devres {131 struct clk_bulk_data *clks;132 int num_clks;133};134 135static void devm_clk_bulk_release(struct device *dev, void *res)136{137 struct clk_bulk_devres *devres = res;138 139 clk_bulk_put(devres->num_clks, devres->clks);140}141 142static int __devm_clk_bulk_get(struct device *dev, int num_clks,143 struct clk_bulk_data *clks, bool optional)144{145 struct clk_bulk_devres *devres;146 int ret;147 148 devres = devres_alloc(devm_clk_bulk_release,149 sizeof(*devres), GFP_KERNEL);150 if (!devres)151 return -ENOMEM;152 153 if (optional)154 ret = clk_bulk_get_optional(dev, num_clks, clks);155 else156 ret = clk_bulk_get(dev, num_clks, clks);157 if (!ret) {158 devres->clks = clks;159 devres->num_clks = num_clks;160 devres_add(dev, devres);161 } else {162 devres_free(devres);163 }164 165 return ret;166}167 168int __must_check devm_clk_bulk_get(struct device *dev, int num_clks,169 struct clk_bulk_data *clks)170{171 return __devm_clk_bulk_get(dev, num_clks, clks, false);172}173EXPORT_SYMBOL_GPL(devm_clk_bulk_get);174 175int __must_check devm_clk_bulk_get_optional(struct device *dev, int num_clks,176 struct clk_bulk_data *clks)177{178 return __devm_clk_bulk_get(dev, num_clks, clks, true);179}180EXPORT_SYMBOL_GPL(devm_clk_bulk_get_optional);181 182static void devm_clk_bulk_release_all(struct device *dev, void *res)183{184 struct clk_bulk_devres *devres = res;185 186 clk_bulk_put_all(devres->num_clks, devres->clks);187}188 189int __must_check devm_clk_bulk_get_all(struct device *dev,190 struct clk_bulk_data **clks)191{192 struct clk_bulk_devres *devres;193 int ret;194 195 devres = devres_alloc(devm_clk_bulk_release_all,196 sizeof(*devres), GFP_KERNEL);197 if (!devres)198 return -ENOMEM;199 200 ret = clk_bulk_get_all(dev, &devres->clks);201 if (ret > 0) {202 *clks = devres->clks;203 devres->num_clks = ret;204 devres_add(dev, devres);205 } else {206 devres_free(devres);207 }208 209 return ret;210}211EXPORT_SYMBOL_GPL(devm_clk_bulk_get_all);212 213static void devm_clk_bulk_release_all_enable(struct device *dev, void *res)214{215 struct clk_bulk_devres *devres = res;216 217 clk_bulk_disable_unprepare(devres->num_clks, devres->clks);218 clk_bulk_put_all(devres->num_clks, devres->clks);219}220 221int __must_check devm_clk_bulk_get_all_enable(struct device *dev,222 struct clk_bulk_data **clks)223{224 struct clk_bulk_devres *devres;225 int ret;226 227 devres = devres_alloc(devm_clk_bulk_release_all_enable,228 sizeof(*devres), GFP_KERNEL);229 if (!devres)230 return -ENOMEM;231 232 ret = clk_bulk_get_all(dev, &devres->clks);233 if (ret > 0) {234 *clks = devres->clks;235 devres->num_clks = ret;236 } else {237 devres_free(devres);238 return ret;239 }240 241 ret = clk_bulk_prepare_enable(devres->num_clks, *clks);242 if (!ret) {243 devres_add(dev, devres);244 } else {245 clk_bulk_put_all(devres->num_clks, devres->clks);246 devres_free(devres);247 }248 249 return ret;250}251EXPORT_SYMBOL_GPL(devm_clk_bulk_get_all_enable);252 253static int devm_clk_match(struct device *dev, void *res, void *data)254{255 struct clk **c = res;256 if (!c || !*c) {257 WARN_ON(!c || !*c);258 return 0;259 }260 return *c == data;261}262 263void devm_clk_put(struct device *dev, struct clk *clk)264{265 int ret;266 267 ret = devres_release(dev, devm_clk_release, devm_clk_match, clk);268 269 WARN_ON(ret);270}271EXPORT_SYMBOL(devm_clk_put);272 273struct clk *devm_get_clk_from_child(struct device *dev,274 struct device_node *np, const char *con_id)275{276 struct devm_clk_state *state;277 struct clk *clk;278 279 state = devres_alloc(devm_clk_release, sizeof(*state), GFP_KERNEL);280 if (!state)281 return ERR_PTR(-ENOMEM);282 283 clk = of_clk_get_by_name(np, con_id);284 if (!IS_ERR(clk)) {285 state->clk = clk;286 devres_add(dev, state);287 } else {288 devres_free(state);289 }290 291 return clk;292}293EXPORT_SYMBOL(devm_get_clk_from_child);294