162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * drivers/clk/clkdev.c 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2008 Russell King. 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Helper for the clk API to assist looking up a struct clk. 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci#include <linux/module.h> 1062306a36Sopenharmony_ci#include <linux/kernel.h> 1162306a36Sopenharmony_ci#include <linux/device.h> 1262306a36Sopenharmony_ci#include <linux/list.h> 1362306a36Sopenharmony_ci#include <linux/errno.h> 1462306a36Sopenharmony_ci#include <linux/err.h> 1562306a36Sopenharmony_ci#include <linux/string.h> 1662306a36Sopenharmony_ci#include <linux/mutex.h> 1762306a36Sopenharmony_ci#include <linux/clk.h> 1862306a36Sopenharmony_ci#include <linux/clkdev.h> 1962306a36Sopenharmony_ci#include <linux/clk-provider.h> 2062306a36Sopenharmony_ci#include <linux/of.h> 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#include "clk.h" 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_cistatic LIST_HEAD(clocks); 2562306a36Sopenharmony_cistatic DEFINE_MUTEX(clocks_mutex); 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci/* 2862306a36Sopenharmony_ci * Find the correct struct clk for the device and connection ID. 2962306a36Sopenharmony_ci * We do slightly fuzzy matching here: 3062306a36Sopenharmony_ci * An entry with a NULL ID is assumed to be a wildcard. 3162306a36Sopenharmony_ci * If an entry has a device ID, it must match 3262306a36Sopenharmony_ci * If an entry has a connection ID, it must match 3362306a36Sopenharmony_ci * Then we take the most specific entry - with the following 3462306a36Sopenharmony_ci * order of precedence: dev+con > dev only > con only. 3562306a36Sopenharmony_ci */ 3662306a36Sopenharmony_cistatic struct clk_lookup *clk_find(const char *dev_id, const char *con_id) 3762306a36Sopenharmony_ci{ 3862306a36Sopenharmony_ci struct clk_lookup *p, *cl = NULL; 3962306a36Sopenharmony_ci int match, best_found = 0, best_possible = 0; 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci if (dev_id) 4262306a36Sopenharmony_ci best_possible += 2; 4362306a36Sopenharmony_ci if (con_id) 4462306a36Sopenharmony_ci best_possible += 1; 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci lockdep_assert_held(&clocks_mutex); 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci list_for_each_entry(p, &clocks, node) { 4962306a36Sopenharmony_ci match = 0; 5062306a36Sopenharmony_ci if (p->dev_id) { 5162306a36Sopenharmony_ci if (!dev_id || strcmp(p->dev_id, dev_id)) 5262306a36Sopenharmony_ci continue; 5362306a36Sopenharmony_ci match += 2; 5462306a36Sopenharmony_ci } 5562306a36Sopenharmony_ci if (p->con_id) { 5662306a36Sopenharmony_ci if (!con_id || strcmp(p->con_id, con_id)) 5762306a36Sopenharmony_ci continue; 5862306a36Sopenharmony_ci match += 1; 5962306a36Sopenharmony_ci } 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci if (match > best_found) { 6262306a36Sopenharmony_ci cl = p; 6362306a36Sopenharmony_ci if (match != best_possible) 6462306a36Sopenharmony_ci best_found = match; 6562306a36Sopenharmony_ci else 6662306a36Sopenharmony_ci break; 6762306a36Sopenharmony_ci } 6862306a36Sopenharmony_ci } 6962306a36Sopenharmony_ci return cl; 7062306a36Sopenharmony_ci} 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_cistruct clk_hw *clk_find_hw(const char *dev_id, const char *con_id) 7362306a36Sopenharmony_ci{ 7462306a36Sopenharmony_ci struct clk_lookup *cl; 7562306a36Sopenharmony_ci struct clk_hw *hw = ERR_PTR(-ENOENT); 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci mutex_lock(&clocks_mutex); 7862306a36Sopenharmony_ci cl = clk_find(dev_id, con_id); 7962306a36Sopenharmony_ci if (cl) 8062306a36Sopenharmony_ci hw = cl->clk_hw; 8162306a36Sopenharmony_ci mutex_unlock(&clocks_mutex); 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci return hw; 8462306a36Sopenharmony_ci} 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_cistatic struct clk *__clk_get_sys(struct device *dev, const char *dev_id, 8762306a36Sopenharmony_ci const char *con_id) 8862306a36Sopenharmony_ci{ 8962306a36Sopenharmony_ci struct clk_hw *hw = clk_find_hw(dev_id, con_id); 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci return clk_hw_create_clk(dev, hw, dev_id, con_id); 9262306a36Sopenharmony_ci} 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_cistruct clk *clk_get_sys(const char *dev_id, const char *con_id) 9562306a36Sopenharmony_ci{ 9662306a36Sopenharmony_ci return __clk_get_sys(NULL, dev_id, con_id); 9762306a36Sopenharmony_ci} 9862306a36Sopenharmony_ciEXPORT_SYMBOL(clk_get_sys); 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_cistruct clk *clk_get(struct device *dev, const char *con_id) 10162306a36Sopenharmony_ci{ 10262306a36Sopenharmony_ci const char *dev_id = dev ? dev_name(dev) : NULL; 10362306a36Sopenharmony_ci struct clk_hw *hw; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci if (dev && dev->of_node) { 10662306a36Sopenharmony_ci hw = of_clk_get_hw(dev->of_node, 0, con_id); 10762306a36Sopenharmony_ci if (!IS_ERR(hw) || PTR_ERR(hw) == -EPROBE_DEFER) 10862306a36Sopenharmony_ci return clk_hw_create_clk(dev, hw, dev_id, con_id); 10962306a36Sopenharmony_ci } 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci return __clk_get_sys(dev, dev_id, con_id); 11262306a36Sopenharmony_ci} 11362306a36Sopenharmony_ciEXPORT_SYMBOL(clk_get); 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_civoid clk_put(struct clk *clk) 11662306a36Sopenharmony_ci{ 11762306a36Sopenharmony_ci __clk_put(clk); 11862306a36Sopenharmony_ci} 11962306a36Sopenharmony_ciEXPORT_SYMBOL(clk_put); 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_cistatic void __clkdev_add(struct clk_lookup *cl) 12262306a36Sopenharmony_ci{ 12362306a36Sopenharmony_ci mutex_lock(&clocks_mutex); 12462306a36Sopenharmony_ci list_add_tail(&cl->node, &clocks); 12562306a36Sopenharmony_ci mutex_unlock(&clocks_mutex); 12662306a36Sopenharmony_ci} 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_civoid clkdev_add(struct clk_lookup *cl) 12962306a36Sopenharmony_ci{ 13062306a36Sopenharmony_ci if (!cl->clk_hw) 13162306a36Sopenharmony_ci cl->clk_hw = __clk_get_hw(cl->clk); 13262306a36Sopenharmony_ci __clkdev_add(cl); 13362306a36Sopenharmony_ci} 13462306a36Sopenharmony_ciEXPORT_SYMBOL(clkdev_add); 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_civoid clkdev_add_table(struct clk_lookup *cl, size_t num) 13762306a36Sopenharmony_ci{ 13862306a36Sopenharmony_ci mutex_lock(&clocks_mutex); 13962306a36Sopenharmony_ci while (num--) { 14062306a36Sopenharmony_ci cl->clk_hw = __clk_get_hw(cl->clk); 14162306a36Sopenharmony_ci list_add_tail(&cl->node, &clocks); 14262306a36Sopenharmony_ci cl++; 14362306a36Sopenharmony_ci } 14462306a36Sopenharmony_ci mutex_unlock(&clocks_mutex); 14562306a36Sopenharmony_ci} 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci#define MAX_DEV_ID 20 14862306a36Sopenharmony_ci#define MAX_CON_ID 16 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_cistruct clk_lookup_alloc { 15162306a36Sopenharmony_ci struct clk_lookup cl; 15262306a36Sopenharmony_ci char dev_id[MAX_DEV_ID]; 15362306a36Sopenharmony_ci char con_id[MAX_CON_ID]; 15462306a36Sopenharmony_ci}; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_cistatic struct clk_lookup * __ref 15762306a36Sopenharmony_civclkdev_alloc(struct clk_hw *hw, const char *con_id, const char *dev_fmt, 15862306a36Sopenharmony_ci va_list ap) 15962306a36Sopenharmony_ci{ 16062306a36Sopenharmony_ci struct clk_lookup_alloc *cla; 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci cla = kzalloc(sizeof(*cla), GFP_KERNEL); 16362306a36Sopenharmony_ci if (!cla) 16462306a36Sopenharmony_ci return NULL; 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci cla->cl.clk_hw = hw; 16762306a36Sopenharmony_ci if (con_id) { 16862306a36Sopenharmony_ci strscpy(cla->con_id, con_id, sizeof(cla->con_id)); 16962306a36Sopenharmony_ci cla->cl.con_id = cla->con_id; 17062306a36Sopenharmony_ci } 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci if (dev_fmt) { 17362306a36Sopenharmony_ci vscnprintf(cla->dev_id, sizeof(cla->dev_id), dev_fmt, ap); 17462306a36Sopenharmony_ci cla->cl.dev_id = cla->dev_id; 17562306a36Sopenharmony_ci } 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci return &cla->cl; 17862306a36Sopenharmony_ci} 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_cistatic struct clk_lookup * 18162306a36Sopenharmony_civclkdev_create(struct clk_hw *hw, const char *con_id, const char *dev_fmt, 18262306a36Sopenharmony_ci va_list ap) 18362306a36Sopenharmony_ci{ 18462306a36Sopenharmony_ci struct clk_lookup *cl; 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci cl = vclkdev_alloc(hw, con_id, dev_fmt, ap); 18762306a36Sopenharmony_ci if (cl) 18862306a36Sopenharmony_ci __clkdev_add(cl); 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci return cl; 19162306a36Sopenharmony_ci} 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci/** 19462306a36Sopenharmony_ci * clkdev_create - allocate and add a clkdev lookup structure 19562306a36Sopenharmony_ci * @clk: struct clk to associate with all clk_lookups 19662306a36Sopenharmony_ci * @con_id: connection ID string on device 19762306a36Sopenharmony_ci * @dev_fmt: format string describing device name 19862306a36Sopenharmony_ci * 19962306a36Sopenharmony_ci * Returns a clk_lookup structure, which can be later unregistered and 20062306a36Sopenharmony_ci * freed. 20162306a36Sopenharmony_ci */ 20262306a36Sopenharmony_cistruct clk_lookup *clkdev_create(struct clk *clk, const char *con_id, 20362306a36Sopenharmony_ci const char *dev_fmt, ...) 20462306a36Sopenharmony_ci{ 20562306a36Sopenharmony_ci struct clk_lookup *cl; 20662306a36Sopenharmony_ci va_list ap; 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci va_start(ap, dev_fmt); 20962306a36Sopenharmony_ci cl = vclkdev_create(__clk_get_hw(clk), con_id, dev_fmt, ap); 21062306a36Sopenharmony_ci va_end(ap); 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci return cl; 21362306a36Sopenharmony_ci} 21462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(clkdev_create); 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci/** 21762306a36Sopenharmony_ci * clkdev_hw_create - allocate and add a clkdev lookup structure 21862306a36Sopenharmony_ci * @hw: struct clk_hw to associate with all clk_lookups 21962306a36Sopenharmony_ci * @con_id: connection ID string on device 22062306a36Sopenharmony_ci * @dev_fmt: format string describing device name 22162306a36Sopenharmony_ci * 22262306a36Sopenharmony_ci * Returns a clk_lookup structure, which can be later unregistered and 22362306a36Sopenharmony_ci * freed. 22462306a36Sopenharmony_ci */ 22562306a36Sopenharmony_cistruct clk_lookup *clkdev_hw_create(struct clk_hw *hw, const char *con_id, 22662306a36Sopenharmony_ci const char *dev_fmt, ...) 22762306a36Sopenharmony_ci{ 22862306a36Sopenharmony_ci struct clk_lookup *cl; 22962306a36Sopenharmony_ci va_list ap; 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci va_start(ap, dev_fmt); 23262306a36Sopenharmony_ci cl = vclkdev_create(hw, con_id, dev_fmt, ap); 23362306a36Sopenharmony_ci va_end(ap); 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci return cl; 23662306a36Sopenharmony_ci} 23762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(clkdev_hw_create); 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ciint clk_add_alias(const char *alias, const char *alias_dev_name, 24062306a36Sopenharmony_ci const char *con_id, struct device *dev) 24162306a36Sopenharmony_ci{ 24262306a36Sopenharmony_ci struct clk *r = clk_get(dev, con_id); 24362306a36Sopenharmony_ci struct clk_lookup *l; 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci if (IS_ERR(r)) 24662306a36Sopenharmony_ci return PTR_ERR(r); 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci l = clkdev_create(r, alias, alias_dev_name ? "%s" : NULL, 24962306a36Sopenharmony_ci alias_dev_name); 25062306a36Sopenharmony_ci clk_put(r); 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci return l ? 0 : -ENODEV; 25362306a36Sopenharmony_ci} 25462306a36Sopenharmony_ciEXPORT_SYMBOL(clk_add_alias); 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci/* 25762306a36Sopenharmony_ci * clkdev_drop - remove a clock dynamically allocated 25862306a36Sopenharmony_ci */ 25962306a36Sopenharmony_civoid clkdev_drop(struct clk_lookup *cl) 26062306a36Sopenharmony_ci{ 26162306a36Sopenharmony_ci mutex_lock(&clocks_mutex); 26262306a36Sopenharmony_ci list_del(&cl->node); 26362306a36Sopenharmony_ci mutex_unlock(&clocks_mutex); 26462306a36Sopenharmony_ci kfree(cl); 26562306a36Sopenharmony_ci} 26662306a36Sopenharmony_ciEXPORT_SYMBOL(clkdev_drop); 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_cistatic struct clk_lookup *__clk_register_clkdev(struct clk_hw *hw, 26962306a36Sopenharmony_ci const char *con_id, 27062306a36Sopenharmony_ci const char *dev_id, ...) 27162306a36Sopenharmony_ci{ 27262306a36Sopenharmony_ci struct clk_lookup *cl; 27362306a36Sopenharmony_ci va_list ap; 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci va_start(ap, dev_id); 27662306a36Sopenharmony_ci cl = vclkdev_create(hw, con_id, dev_id, ap); 27762306a36Sopenharmony_ci va_end(ap); 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci return cl; 28062306a36Sopenharmony_ci} 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_cistatic int do_clk_register_clkdev(struct clk_hw *hw, 28362306a36Sopenharmony_ci struct clk_lookup **cl, const char *con_id, const char *dev_id) 28462306a36Sopenharmony_ci{ 28562306a36Sopenharmony_ci if (IS_ERR(hw)) 28662306a36Sopenharmony_ci return PTR_ERR(hw); 28762306a36Sopenharmony_ci /* 28862306a36Sopenharmony_ci * Since dev_id can be NULL, and NULL is handled specially, we must 28962306a36Sopenharmony_ci * pass it as either a NULL format string, or with "%s". 29062306a36Sopenharmony_ci */ 29162306a36Sopenharmony_ci if (dev_id) 29262306a36Sopenharmony_ci *cl = __clk_register_clkdev(hw, con_id, "%s", dev_id); 29362306a36Sopenharmony_ci else 29462306a36Sopenharmony_ci *cl = __clk_register_clkdev(hw, con_id, NULL); 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci return *cl ? 0 : -ENOMEM; 29762306a36Sopenharmony_ci} 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci/** 30062306a36Sopenharmony_ci * clk_register_clkdev - register one clock lookup for a struct clk 30162306a36Sopenharmony_ci * @clk: struct clk to associate with all clk_lookups 30262306a36Sopenharmony_ci * @con_id: connection ID string on device 30362306a36Sopenharmony_ci * @dev_id: string describing device name 30462306a36Sopenharmony_ci * 30562306a36Sopenharmony_ci * con_id or dev_id may be NULL as a wildcard, just as in the rest of 30662306a36Sopenharmony_ci * clkdev. 30762306a36Sopenharmony_ci * 30862306a36Sopenharmony_ci * To make things easier for mass registration, we detect error clks 30962306a36Sopenharmony_ci * from a previous clk_register() call, and return the error code for 31062306a36Sopenharmony_ci * those. This is to permit this function to be called immediately 31162306a36Sopenharmony_ci * after clk_register(). 31262306a36Sopenharmony_ci */ 31362306a36Sopenharmony_ciint clk_register_clkdev(struct clk *clk, const char *con_id, 31462306a36Sopenharmony_ci const char *dev_id) 31562306a36Sopenharmony_ci{ 31662306a36Sopenharmony_ci struct clk_lookup *cl; 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci if (IS_ERR(clk)) 31962306a36Sopenharmony_ci return PTR_ERR(clk); 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci return do_clk_register_clkdev(__clk_get_hw(clk), &cl, con_id, 32262306a36Sopenharmony_ci dev_id); 32362306a36Sopenharmony_ci} 32462306a36Sopenharmony_ciEXPORT_SYMBOL(clk_register_clkdev); 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci/** 32762306a36Sopenharmony_ci * clk_hw_register_clkdev - register one clock lookup for a struct clk_hw 32862306a36Sopenharmony_ci * @hw: struct clk_hw to associate with all clk_lookups 32962306a36Sopenharmony_ci * @con_id: connection ID string on device 33062306a36Sopenharmony_ci * @dev_id: format string describing device name 33162306a36Sopenharmony_ci * 33262306a36Sopenharmony_ci * con_id or dev_id may be NULL as a wildcard, just as in the rest of 33362306a36Sopenharmony_ci * clkdev. 33462306a36Sopenharmony_ci * 33562306a36Sopenharmony_ci * To make things easier for mass registration, we detect error clk_hws 33662306a36Sopenharmony_ci * from a previous clk_hw_register_*() call, and return the error code for 33762306a36Sopenharmony_ci * those. This is to permit this function to be called immediately 33862306a36Sopenharmony_ci * after clk_hw_register_*(). 33962306a36Sopenharmony_ci */ 34062306a36Sopenharmony_ciint clk_hw_register_clkdev(struct clk_hw *hw, const char *con_id, 34162306a36Sopenharmony_ci const char *dev_id) 34262306a36Sopenharmony_ci{ 34362306a36Sopenharmony_ci struct clk_lookup *cl; 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci return do_clk_register_clkdev(hw, &cl, con_id, dev_id); 34662306a36Sopenharmony_ci} 34762306a36Sopenharmony_ciEXPORT_SYMBOL(clk_hw_register_clkdev); 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_cistatic void devm_clkdev_release(void *res) 35062306a36Sopenharmony_ci{ 35162306a36Sopenharmony_ci clkdev_drop(res); 35262306a36Sopenharmony_ci} 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci/** 35562306a36Sopenharmony_ci * devm_clk_hw_register_clkdev - managed clk lookup registration for clk_hw 35662306a36Sopenharmony_ci * @dev: device this lookup is bound 35762306a36Sopenharmony_ci * @hw: struct clk_hw to associate with all clk_lookups 35862306a36Sopenharmony_ci * @con_id: connection ID string on device 35962306a36Sopenharmony_ci * @dev_id: format string describing device name 36062306a36Sopenharmony_ci * 36162306a36Sopenharmony_ci * con_id or dev_id may be NULL as a wildcard, just as in the rest of 36262306a36Sopenharmony_ci * clkdev. 36362306a36Sopenharmony_ci * 36462306a36Sopenharmony_ci * To make things easier for mass registration, we detect error clk_hws 36562306a36Sopenharmony_ci * from a previous clk_hw_register_*() call, and return the error code for 36662306a36Sopenharmony_ci * those. This is to permit this function to be called immediately 36762306a36Sopenharmony_ci * after clk_hw_register_*(). 36862306a36Sopenharmony_ci */ 36962306a36Sopenharmony_ciint devm_clk_hw_register_clkdev(struct device *dev, struct clk_hw *hw, 37062306a36Sopenharmony_ci const char *con_id, const char *dev_id) 37162306a36Sopenharmony_ci{ 37262306a36Sopenharmony_ci struct clk_lookup *cl; 37362306a36Sopenharmony_ci int rval; 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci rval = do_clk_register_clkdev(hw, &cl, con_id, dev_id); 37662306a36Sopenharmony_ci if (rval) 37762306a36Sopenharmony_ci return rval; 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci return devm_add_action_or_reset(dev, devm_clkdev_release, cl); 38062306a36Sopenharmony_ci} 38162306a36Sopenharmony_ciEXPORT_SYMBOL(devm_clk_hw_register_clkdev); 382