Merge branch 'clk-parent-rewrite-1' into clk-next

- Rewrite how clk parents can be specified to be DT/clkdev based instead
   of just string based

* clk-parent-rewrite-1:
  clk: Cache core in clk_fetch_parent_index() without names
  clk: fixed-factor: Initialize clk_init_data on stack
  clk: fixed-factor: Let clk framework find parent
  clk: Allow parents to be specified via clkspec index
  clk: Look for parents with clkdev based clk_lookups
  clk: Allow parents to be specified without string names
  clk: Add of_clk_hw_register() API for early clk drivers
  driver core: Let dev_of_node() accept a NULL dev
  clk: Prepare for clk registration API that uses DT nodes
  clkdev: Move clk creation outside of 'clocks_mutex'
This commit is contained in:
Stephen Boyd 2019-05-07 11:46:13 -07:00
commit c1157f60d7
6 changed files with 348 additions and 117 deletions

View File

@ -64,12 +64,14 @@ const struct clk_ops clk_fixed_factor_ops = {
}; };
EXPORT_SYMBOL_GPL(clk_fixed_factor_ops); EXPORT_SYMBOL_GPL(clk_fixed_factor_ops);
struct clk_hw *clk_hw_register_fixed_factor(struct device *dev, static struct clk_hw *
const char *name, const char *parent_name, unsigned long flags, __clk_hw_register_fixed_factor(struct device *dev, struct device_node *np,
unsigned int mult, unsigned int div) const char *name, const char *parent_name, int index,
unsigned long flags, unsigned int mult, unsigned int div)
{ {
struct clk_fixed_factor *fix; struct clk_fixed_factor *fix;
struct clk_init_data init; struct clk_init_data init = { };
struct clk_parent_data pdata = { .index = index };
struct clk_hw *hw; struct clk_hw *hw;
int ret; int ret;
@ -85,11 +87,17 @@ struct clk_hw *clk_hw_register_fixed_factor(struct device *dev,
init.name = name; init.name = name;
init.ops = &clk_fixed_factor_ops; init.ops = &clk_fixed_factor_ops;
init.flags = flags; init.flags = flags;
if (parent_name)
init.parent_names = &parent_name; init.parent_names = &parent_name;
else
init.parent_data = &pdata;
init.num_parents = 1; init.num_parents = 1;
hw = &fix->hw; hw = &fix->hw;
if (dev)
ret = clk_hw_register(dev, hw); ret = clk_hw_register(dev, hw);
else
ret = of_clk_hw_register(np, hw);
if (ret) { if (ret) {
kfree(fix); kfree(fix);
hw = ERR_PTR(ret); hw = ERR_PTR(ret);
@ -97,6 +105,14 @@ struct clk_hw *clk_hw_register_fixed_factor(struct device *dev,
return hw; return hw;
} }
struct clk_hw *clk_hw_register_fixed_factor(struct device *dev,
const char *name, const char *parent_name, unsigned long flags,
unsigned int mult, unsigned int div)
{
return __clk_hw_register_fixed_factor(dev, NULL, name, parent_name, -1,
flags, mult, div);
}
EXPORT_SYMBOL_GPL(clk_hw_register_fixed_factor); EXPORT_SYMBOL_GPL(clk_hw_register_fixed_factor);
struct clk *clk_register_fixed_factor(struct device *dev, const char *name, struct clk *clk_register_fixed_factor(struct device *dev, const char *name,
@ -143,11 +159,10 @@ static const struct of_device_id set_rate_parent_matches[] = {
{ /* Sentinel */ }, { /* Sentinel */ },
}; };
static struct clk *_of_fixed_factor_clk_setup(struct device_node *node) static struct clk_hw *_of_fixed_factor_clk_setup(struct device_node *node)
{ {
struct clk *clk; struct clk_hw *hw;
const char *clk_name = node->name; const char *clk_name = node->name;
const char *parent_name;
unsigned long flags = 0; unsigned long flags = 0;
u32 div, mult; u32 div, mult;
int ret; int ret;
@ -165,30 +180,28 @@ static struct clk *_of_fixed_factor_clk_setup(struct device_node *node)
} }
of_property_read_string(node, "clock-output-names", &clk_name); of_property_read_string(node, "clock-output-names", &clk_name);
parent_name = of_clk_get_parent_name(node, 0);
if (of_match_node(set_rate_parent_matches, node)) if (of_match_node(set_rate_parent_matches, node))
flags |= CLK_SET_RATE_PARENT; flags |= CLK_SET_RATE_PARENT;
clk = clk_register_fixed_factor(NULL, clk_name, parent_name, flags, hw = __clk_hw_register_fixed_factor(NULL, node, clk_name, NULL, 0,
mult, div); flags, mult, div);
if (IS_ERR(clk)) { if (IS_ERR(hw)) {
/* /*
* If parent clock is not registered, registration would fail.
* Clear OF_POPULATED flag so that clock registration can be * Clear OF_POPULATED flag so that clock registration can be
* attempted again from probe function. * attempted again from probe function.
*/ */
of_node_clear_flag(node, OF_POPULATED); of_node_clear_flag(node, OF_POPULATED);
return clk; return ERR_CAST(hw);
} }
ret = of_clk_add_provider(node, of_clk_src_simple_get, clk); ret = of_clk_add_hw_provider(node, of_clk_hw_simple_get, hw);
if (ret) { if (ret) {
clk_unregister(clk); clk_hw_unregister_fixed_factor(hw);
return ERR_PTR(ret); return ERR_PTR(ret);
} }
return clk; return hw;
} }
/** /**
@ -203,17 +216,17 @@ CLK_OF_DECLARE(fixed_factor_clk, "fixed-factor-clock",
static int of_fixed_factor_clk_remove(struct platform_device *pdev) static int of_fixed_factor_clk_remove(struct platform_device *pdev)
{ {
struct clk *clk = platform_get_drvdata(pdev); struct clk_hw *clk = platform_get_drvdata(pdev);
of_clk_del_provider(pdev->dev.of_node); of_clk_del_provider(pdev->dev.of_node);
clk_unregister_fixed_factor(clk); clk_hw_unregister_fixed_factor(clk);
return 0; return 0;
} }
static int of_fixed_factor_clk_probe(struct platform_device *pdev) static int of_fixed_factor_clk_probe(struct platform_device *pdev)
{ {
struct clk *clk; struct clk_hw *clk;
/* /*
* This function is not executed when of_fixed_factor_clk_setup * This function is not executed when of_fixed_factor_clk_setup

View File

@ -39,15 +39,23 @@ static LIST_HEAD(clk_notifier_list);
/*** private data structures ***/ /*** private data structures ***/
struct clk_parent_map {
const struct clk_hw *hw;
struct clk_core *core;
const char *fw_name;
const char *name;
int index;
};
struct clk_core { struct clk_core {
const char *name; const char *name;
const struct clk_ops *ops; const struct clk_ops *ops;
struct clk_hw *hw; struct clk_hw *hw;
struct module *owner; struct module *owner;
struct device *dev; struct device *dev;
struct device_node *of_node;
struct clk_core *parent; struct clk_core *parent;
const char **parent_names; struct clk_parent_map *parents;
struct clk_core **parents;
u8 num_parents; u8 num_parents;
u8 new_parent_index; u8 new_parent_index;
unsigned long rate; unsigned long rate;
@ -316,17 +324,102 @@ static struct clk_core *clk_core_lookup(const char *name)
return NULL; return NULL;
} }
/**
* clk_core_get - Find the clk_core parent of a clk
* @core: clk to find parent of
* @p_index: parent index to search for
*
* This is the preferred method for clk providers to find the parent of a
* clk when that parent is external to the clk controller. The parent_names
* array is indexed and treated as a local name matching a string in the device
* node's 'clock-names' property or as the 'con_id' matching the device's
* dev_name() in a clk_lookup. This allows clk providers to use their own
* namespace instead of looking for a globally unique parent string.
*
* For example the following DT snippet would allow a clock registered by the
* clock-controller@c001 that has a clk_init_data::parent_data array
* with 'xtal' in the 'name' member to find the clock provided by the
* clock-controller@f00abcd without needing to get the globally unique name of
* the xtal clk.
*
* parent: clock-controller@f00abcd {
* reg = <0xf00abcd 0xabcd>;
* #clock-cells = <0>;
* };
*
* clock-controller@c001 {
* reg = <0xc001 0xf00d>;
* clocks = <&parent>;
* clock-names = "xtal";
* #clock-cells = <1>;
* };
*
* Returns: -ENOENT when the provider can't be found or the clk doesn't
* exist in the provider. -EINVAL when the name can't be found. NULL when the
* provider knows about the clk but it isn't provided on this system.
* A valid clk_core pointer when the clk can be found in the provider.
*/
static struct clk_core *clk_core_get(struct clk_core *core, u8 p_index)
{
const char *name = core->parents[p_index].fw_name;
int index = core->parents[p_index].index;
struct clk_hw *hw = ERR_PTR(-ENOENT);
struct device *dev = core->dev;
const char *dev_id = dev ? dev_name(dev) : NULL;
struct device_node *np = core->of_node;
if (np && index >= 0)
hw = of_clk_get_hw(np, index, name);
/*
* If the DT search above couldn't find the provider or the provider
* didn't know about this clk, fallback to looking up via clkdev based
* clk_lookups
*/
if (PTR_ERR(hw) == -ENOENT && name)
hw = clk_find_hw(dev_id, name);
if (IS_ERR(hw))
return ERR_CAST(hw);
return hw->core;
}
static void clk_core_fill_parent_index(struct clk_core *core, u8 index)
{
struct clk_parent_map *entry = &core->parents[index];
struct clk_core *parent = ERR_PTR(-ENOENT);
if (entry->hw) {
parent = entry->hw->core;
/*
* We have a direct reference but it isn't registered yet?
* Orphan it and let clk_reparent() update the orphan status
* when the parent is registered.
*/
if (!parent)
parent = ERR_PTR(-EPROBE_DEFER);
} else {
parent = clk_core_get(core, index);
if (IS_ERR(parent) && PTR_ERR(parent) == -ENOENT)
parent = clk_core_lookup(entry->name);
}
/* Only cache it if it's not an error */
if (!IS_ERR(parent))
entry->core = parent;
}
static struct clk_core *clk_core_get_parent_by_index(struct clk_core *core, static struct clk_core *clk_core_get_parent_by_index(struct clk_core *core,
u8 index) u8 index)
{ {
if (!core || index >= core->num_parents) if (!core || index >= core->num_parents || !core->parents)
return NULL; return NULL;
if (!core->parents[index]) if (!core->parents[index].core)
core->parents[index] = clk_core_fill_parent_index(core, index);
clk_core_lookup(core->parent_names[index]);
return core->parents[index]; return core->parents[index].core;
} }
struct clk_hw * struct clk_hw *
@ -1520,20 +1613,37 @@ static int clk_fetch_parent_index(struct clk_core *core,
return -EINVAL; return -EINVAL;
for (i = 0; i < core->num_parents; i++) { for (i = 0; i < core->num_parents; i++) {
if (core->parents[i] == parent) /* Found it first try! */
if (core->parents[i].core == parent)
return i; return i;
if (core->parents[i]) /* Something else is here, so keep looking */
if (core->parents[i].core)
continue; continue;
/* Fallback to comparing globally unique names */ /* Maybe core hasn't been cached but the hw is all we know? */
if (!strcmp(parent->name, core->parent_names[i])) { if (core->parents[i].hw) {
core->parents[i] = parent; if (core->parents[i].hw == parent->hw)
return i; break;
}
/* Didn't match, but we're expecting a clk_hw */
continue;
} }
/* Maybe it hasn't been cached (clk_set_parent() path) */
if (parent == clk_core_get(core, i))
break;
/* Fallback to comparing globally unique names */
if (!strcmp(parent->name, core->parents[i].name))
break;
}
if (i == core->num_parents)
return -EINVAL; return -EINVAL;
core->parents[i].core = parent;
return i;
} }
/* /*
@ -2294,6 +2404,7 @@ void clk_hw_reparent(struct clk_hw *hw, struct clk_hw *new_parent)
bool clk_has_parent(struct clk *clk, struct clk *parent) bool clk_has_parent(struct clk *clk, struct clk *parent)
{ {
struct clk_core *core, *parent_core; struct clk_core *core, *parent_core;
int i;
/* NULL clocks should be nops, so return success if either is NULL. */ /* NULL clocks should be nops, so return success if either is NULL. */
if (!clk || !parent) if (!clk || !parent)
@ -2306,8 +2417,11 @@ bool clk_has_parent(struct clk *clk, struct clk *parent)
if (core->parent == parent_core) if (core->parent == parent_core)
return true; return true;
return match_string(core->parent_names, core->num_parents, for (i = 0; i < core->num_parents; i++)
parent_core->name) >= 0; if (!strcmp(core->parents[i].name, parent_core->name))
return true;
return false;
} }
EXPORT_SYMBOL_GPL(clk_has_parent); EXPORT_SYMBOL_GPL(clk_has_parent);
@ -2889,9 +3003,9 @@ static int possible_parents_show(struct seq_file *s, void *data)
int i; int i;
for (i = 0; i < core->num_parents - 1; i++) for (i = 0; i < core->num_parents - 1; i++)
seq_printf(s, "%s ", core->parent_names[i]); seq_printf(s, "%s ", core->parents[i].name);
seq_printf(s, "%s\n", core->parent_names[i]); seq_printf(s, "%s\n", core->parents[i].name);
return 0; return 0;
} }
@ -3025,7 +3139,7 @@ static inline void clk_debug_unregister(struct clk_core *core)
*/ */
static int __clk_core_init(struct clk_core *core) static int __clk_core_init(struct clk_core *core)
{ {
int i, ret; int ret;
struct clk_core *orphan; struct clk_core *orphan;
struct hlist_node *tmp2; struct hlist_node *tmp2;
unsigned long rate; unsigned long rate;
@ -3079,12 +3193,6 @@ static int __clk_core_init(struct clk_core *core)
goto out; goto out;
} }
/* throw a WARN if any entries in parent_names are NULL */
for (i = 0; i < core->num_parents; i++)
WARN(!core->parent_names[i],
"%s: invalid NULL in %s's .parent_names\n",
__func__, core->name);
core->parent = __clk_init_parent(core); core->parent = __clk_init_parent(core);
/* /*
@ -3313,22 +3421,104 @@ struct clk *clk_hw_create_clk(struct device *dev, struct clk_hw *hw,
return clk; return clk;
} }
/** static int clk_cpy_name(const char **dst_p, const char *src, bool must_exist)
* clk_register - allocate a new clock, register it and return an opaque cookie
* @dev: device that is registering this clock
* @hw: link to hardware-specific clock data
*
* clk_register is the *deprecated* interface for populating the clock tree with
* new clock nodes. Use clk_hw_register() instead.
*
* Returns: a pointer to the newly allocated struct clk which
* cannot be dereferenced by driver code but may be used in conjunction with the
* rest of the clock API. In the event of an error clk_register will return an
* error code; drivers must test for an error code after calling clk_register.
*/
struct clk *clk_register(struct device *dev, struct clk_hw *hw)
{ {
int i, ret; const char *dst;
if (!src) {
if (must_exist)
return -EINVAL;
return 0;
}
*dst_p = dst = kstrdup_const(src, GFP_KERNEL);
if (!dst)
return -ENOMEM;
return 0;
}
static int clk_core_populate_parent_map(struct clk_core *core)
{
const struct clk_init_data *init = core->hw->init;
u8 num_parents = init->num_parents;
const char * const *parent_names = init->parent_names;
const struct clk_hw **parent_hws = init->parent_hws;
const struct clk_parent_data *parent_data = init->parent_data;
int i, ret = 0;
struct clk_parent_map *parents, *parent;
if (!num_parents)
return 0;
/*
* Avoid unnecessary string look-ups of clk_core's possible parents by
* having a cache of names/clk_hw pointers to clk_core pointers.
*/
parents = kcalloc(num_parents, sizeof(*parents), GFP_KERNEL);
core->parents = parents;
if (!parents)
return -ENOMEM;
/* Copy everything over because it might be __initdata */
for (i = 0, parent = parents; i < num_parents; i++, parent++) {
parent->index = -1;
if (parent_names) {
/* throw a WARN if any entries are NULL */
WARN(!parent_names[i],
"%s: invalid NULL in %s's .parent_names\n",
__func__, core->name);
ret = clk_cpy_name(&parent->name, parent_names[i],
true);
} else if (parent_data) {
parent->hw = parent_data[i].hw;
parent->index = parent_data[i].index;
ret = clk_cpy_name(&parent->fw_name,
parent_data[i].fw_name, false);
if (!ret)
ret = clk_cpy_name(&parent->name,
parent_data[i].name,
false);
} else if (parent_hws) {
parent->hw = parent_hws[i];
} else {
ret = -EINVAL;
WARN(1, "Must specify parents if num_parents > 0\n");
}
if (ret) {
do {
kfree_const(parents[i].name);
kfree_const(parents[i].fw_name);
} while (--i >= 0);
kfree(parents);
return ret;
}
}
return 0;
}
static void clk_core_free_parent_map(struct clk_core *core)
{
int i = core->num_parents;
if (!core->num_parents)
return;
while (--i >= 0) {
kfree_const(core->parents[i].name);
kfree_const(core->parents[i].fw_name);
}
kfree(core->parents);
}
static struct clk *
__clk_register(struct device *dev, struct device_node *np, struct clk_hw *hw)
{
int ret;
struct clk_core *core; struct clk_core *core;
core = kzalloc(sizeof(*core), GFP_KERNEL); core = kzalloc(sizeof(*core), GFP_KERNEL);
@ -3352,6 +3542,7 @@ struct clk *clk_register(struct device *dev, struct clk_hw *hw)
if (dev && pm_runtime_enabled(dev)) if (dev && pm_runtime_enabled(dev))
core->rpm_enabled = true; core->rpm_enabled = true;
core->dev = dev; core->dev = dev;
core->of_node = np;
if (dev && dev->driver) if (dev && dev->driver)
core->owner = dev->driver->owner; core->owner = dev->driver->owner;
core->hw = hw; core->hw = hw;
@ -3361,33 +3552,9 @@ struct clk *clk_register(struct device *dev, struct clk_hw *hw)
core->max_rate = ULONG_MAX; core->max_rate = ULONG_MAX;
hw->core = core; hw->core = core;
/* allocate local copy in case parent_names is __initdata */ ret = clk_core_populate_parent_map(core);
core->parent_names = kcalloc(core->num_parents, sizeof(char *), if (ret)
GFP_KERNEL);
if (!core->parent_names) {
ret = -ENOMEM;
goto fail_parent_names;
}
/* copy each string name in case parent_names is __initdata */
for (i = 0; i < core->num_parents; i++) {
core->parent_names[i] = kstrdup_const(hw->init->parent_names[i],
GFP_KERNEL);
if (!core->parent_names[i]) {
ret = -ENOMEM;
goto fail_parent_names_copy;
}
}
/* avoid unnecessary string look-ups of clk_core's possible parents. */
core->parents = kcalloc(core->num_parents, sizeof(*core->parents),
GFP_KERNEL);
if (!core->parents) {
ret = -ENOMEM;
goto fail_parents; goto fail_parents;
};
INIT_HLIST_HEAD(&core->clks); INIT_HLIST_HEAD(&core->clks);
@ -3398,7 +3565,7 @@ struct clk *clk_register(struct device *dev, struct clk_hw *hw)
hw->clk = alloc_clk(core, NULL, NULL); hw->clk = alloc_clk(core, NULL, NULL);
if (IS_ERR(hw->clk)) { if (IS_ERR(hw->clk)) {
ret = PTR_ERR(hw->clk); ret = PTR_ERR(hw->clk);
goto fail_parents; goto fail_create_clk;
} }
clk_core_link_consumer(hw->core, hw->clk); clk_core_link_consumer(hw->core, hw->clk);
@ -3414,13 +3581,9 @@ struct clk *clk_register(struct device *dev, struct clk_hw *hw)
free_clk(hw->clk); free_clk(hw->clk);
hw->clk = NULL; hw->clk = NULL;
fail_create_clk:
clk_core_free_parent_map(core);
fail_parents: fail_parents:
kfree(core->parents);
fail_parent_names_copy:
while (--i >= 0)
kfree_const(core->parent_names[i]);
kfree(core->parent_names);
fail_parent_names:
fail_ops: fail_ops:
kfree_const(core->name); kfree_const(core->name);
fail_name: fail_name:
@ -3428,6 +3591,24 @@ fail_name:
fail_out: fail_out:
return ERR_PTR(ret); return ERR_PTR(ret);
} }
/**
* clk_register - allocate a new clock, register it and return an opaque cookie
* @dev: device that is registering this clock
* @hw: link to hardware-specific clock data
*
* clk_register is the *deprecated* interface for populating the clock tree with
* new clock nodes. Use clk_hw_register() instead.
*
* Returns: a pointer to the newly allocated struct clk which
* cannot be dereferenced by driver code but may be used in conjunction with the
* rest of the clock API. In the event of an error clk_register will return an
* error code; drivers must test for an error code after calling clk_register.
*/
struct clk *clk_register(struct device *dev, struct clk_hw *hw)
{
return __clk_register(dev, dev_of_node(dev), hw);
}
EXPORT_SYMBOL_GPL(clk_register); EXPORT_SYMBOL_GPL(clk_register);
/** /**
@ -3442,23 +3623,35 @@ EXPORT_SYMBOL_GPL(clk_register);
*/ */
int clk_hw_register(struct device *dev, struct clk_hw *hw) int clk_hw_register(struct device *dev, struct clk_hw *hw)
{ {
return PTR_ERR_OR_ZERO(clk_register(dev, hw)); return PTR_ERR_OR_ZERO(__clk_register(dev, dev_of_node(dev), hw));
} }
EXPORT_SYMBOL_GPL(clk_hw_register); EXPORT_SYMBOL_GPL(clk_hw_register);
/*
* of_clk_hw_register - register a clk_hw and return an error code
* @node: device_node of device that is registering this clock
* @hw: link to hardware-specific clock data
*
* of_clk_hw_register() is the primary interface for populating the clock tree
* with new clock nodes when a struct device is not available, but a struct
* device_node is. It returns an integer equal to zero indicating success or
* less than zero indicating failure. Drivers must test for an error code after
* calling of_clk_hw_register().
*/
int of_clk_hw_register(struct device_node *node, struct clk_hw *hw)
{
return PTR_ERR_OR_ZERO(__clk_register(NULL, node, hw));
}
EXPORT_SYMBOL_GPL(of_clk_hw_register);
/* Free memory allocated for a clock. */ /* Free memory allocated for a clock. */
static void __clk_release(struct kref *ref) static void __clk_release(struct kref *ref)
{ {
struct clk_core *core = container_of(ref, struct clk_core, ref); struct clk_core *core = container_of(ref, struct clk_core, ref);
int i = core->num_parents;
lockdep_assert_held(&prepare_lock); lockdep_assert_held(&prepare_lock);
kfree(core->parents); clk_core_free_parent_map(core);
while (--i >= 0)
kfree_const(core->parent_names[i]);
kfree(core->parent_names);
kfree_const(core->name); kfree_const(core->name);
kfree(core); kfree(core);
} }

View File

@ -19,6 +19,8 @@ static inline struct clk_hw *of_clk_get_hw(struct device_node *np,
} }
#endif #endif
struct clk_hw *clk_find_hw(const char *dev_id, const char *con_id);
#ifdef CONFIG_COMMON_CLK #ifdef CONFIG_COMMON_CLK
struct clk *clk_hw_create_clk(struct device *dev, struct clk_hw *hw, struct clk *clk_hw_create_clk(struct device *dev, struct clk_hw *hw,
const char *dev_id, const char *con_id); const char *dev_id, const char *con_id);

View File

@ -72,25 +72,26 @@ static struct clk_lookup *clk_find(const char *dev_id, const char *con_id)
return cl; return cl;
} }
struct clk_hw *clk_find_hw(const char *dev_id, const char *con_id)
{
struct clk_lookup *cl;
struct clk_hw *hw = ERR_PTR(-ENOENT);
mutex_lock(&clocks_mutex);
cl = clk_find(dev_id, con_id);
if (cl)
hw = cl->clk_hw;
mutex_unlock(&clocks_mutex);
return hw;
}
static struct clk *__clk_get_sys(struct device *dev, const char *dev_id, static struct clk *__clk_get_sys(struct device *dev, const char *dev_id,
const char *con_id) const char *con_id)
{ {
struct clk_lookup *cl; struct clk_hw *hw = clk_find_hw(dev_id, con_id);
struct clk *clk = NULL;
mutex_lock(&clocks_mutex); return clk_hw_create_clk(dev, hw, dev_id, con_id);
cl = clk_find(dev_id, con_id);
if (!cl)
goto out;
clk = clk_hw_create_clk(dev, cl->clk_hw, dev_id, con_id);
if (IS_ERR(clk))
cl = NULL;
out:
mutex_unlock(&clocks_mutex);
return cl ? clk : ERR_PTR(-ENOENT);
} }
struct clk *clk_get_sys(const char *dev_id, const char *con_id) struct clk *clk_get_sys(const char *dev_id, const char *con_id)

View File

@ -250,6 +250,20 @@ struct clk_ops {
void (*debug_init)(struct clk_hw *hw, struct dentry *dentry); void (*debug_init)(struct clk_hw *hw, struct dentry *dentry);
}; };
/**
* struct clk_parent_data - clk parent information
* @hw: parent clk_hw pointer (used for clk providers with internal clks)
* @fw_name: parent name local to provider registering clk
* @name: globally unique parent name (used as a fallback)
* @index: parent index local to provider registering clk (if @fw_name absent)
*/
struct clk_parent_data {
const struct clk_hw *hw;
const char *fw_name;
const char *name;
int index;
};
/** /**
* struct clk_init_data - holds init data that's common to all clocks and is * struct clk_init_data - holds init data that's common to all clocks and is
* shared between the clock provider and the common clock framework. * shared between the clock provider and the common clock framework.
@ -257,13 +271,20 @@ struct clk_ops {
* @name: clock name * @name: clock name
* @ops: operations this clock supports * @ops: operations this clock supports
* @parent_names: array of string names for all possible parents * @parent_names: array of string names for all possible parents
* @parent_data: array of parent data for all possible parents (when some
* parents are external to the clk controller)
* @parent_hws: array of pointers to all possible parents (when all parents
* are internal to the clk controller)
* @num_parents: number of possible parents * @num_parents: number of possible parents
* @flags: framework-level hints and quirks * @flags: framework-level hints and quirks
*/ */
struct clk_init_data { struct clk_init_data {
const char *name; const char *name;
const struct clk_ops *ops; const struct clk_ops *ops;
/* Only one of the following three should be assigned */
const char * const *parent_names; const char * const *parent_names;
const struct clk_parent_data *parent_data;
const struct clk_hw **parent_hws;
u8 num_parents; u8 num_parents;
unsigned long flags; unsigned long flags;
}; };
@ -776,6 +797,7 @@ struct clk *devm_clk_register(struct device *dev, struct clk_hw *hw);
int __must_check clk_hw_register(struct device *dev, struct clk_hw *hw); int __must_check clk_hw_register(struct device *dev, struct clk_hw *hw);
int __must_check devm_clk_hw_register(struct device *dev, struct clk_hw *hw); int __must_check devm_clk_hw_register(struct device *dev, struct clk_hw *hw);
int __must_check of_clk_hw_register(struct device_node *node, struct clk_hw *hw);
void clk_unregister(struct clk *clk); void clk_unregister(struct clk *clk);
void devm_clk_unregister(struct device *dev, struct clk *clk); void devm_clk_unregister(struct device *dev, struct clk *clk);

View File

@ -1229,7 +1229,7 @@ static inline void device_lock_assert(struct device *dev)
static inline struct device_node *dev_of_node(struct device *dev) static inline struct device_node *dev_of_node(struct device *dev)
{ {
if (!IS_ENABLED(CONFIG_OF)) if (!IS_ENABLED(CONFIG_OF) || !dev)
return NULL; return NULL;
return dev->of_node; return dev->of_node;
} }