Linux 内核clk 硬件相关层

    xiaoxiao2021-03-25  141

    在Linux内核的clk处理框架中,平台需要实现针对具体clk操作的函数句柄,并且这些 被封装到struct clk_hw对象中。之后通过函数clk_register()向clk框架层注册。 框架层主要分配两个对象,一个struct clk对象,另外一个是struct clk_core对象 clk框架层主要通过这两个结构管理时钟。 我们看clk_register()实现:

    /**  * 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 primary interface for populating the clock tree with new  * clock nodes.  It 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;  struct clk_core *core;

     core = kzalloc(sizeof(*core), GFP_KERNEL);  if (!core) {   ret = -ENOMEM;   goto fail_out;  }

     core->name = kstrdup_const(hw->init->name, GFP_KERNEL);  if (!core->name) {   ret = -ENOMEM;   goto fail_name;  }  core->ops = hw->init->ops;  if (dev && dev->driver)   core->owner = dev->driver->owner;  core->hw = hw;  core->flags = hw->init->flags;  core->num_parents = hw->init->num_parents;  core->min_rate = 0;  core->max_rate = ULONG_MAX;  hw->core = core;

     /* allocate local copy in case parent_names is __initdata */  core->parent_names = kcalloc(core->num_parents, sizeof(char *),      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;  };

     INIT_HLIST_HEAD(&core->clks);

     hw->clk = __clk_create_clk(hw, NULL, NULL);CONFIG_COMMON_CLK需要配置  if (IS_ERR(hw->clk)) {   ret = PTR_ERR(hw->clk);   goto fail_parents;  }

     ret = __clk_core_init(core);  if (!ret)   return hw->clk;

     __clk_free_clk(hw->clk);  hw->clk = NULL;

    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:  kfree_const(core->name); fail_name:  kfree(core); fail_out:  return ERR_PTR(ret); }

    struct clk *__clk_create_clk(struct clk_hw *hw, const char *dev_id,         const char *con_id) {  struct clk *clk;

     /* This is to allow this function to be chained to others */  if (IS_ERR_OR_NULL(hw))   return ERR_CAST(hw);

     clk = kzalloc(sizeof(*clk), GFP_KERNEL);  if (!clk)   return ERR_PTR(-ENOMEM);

     clk->core = hw->core;  clk->dev_id = dev_id;  clk->con_id = con_id;  clk->max_rate = ULONG_MAX;

     clk_prepare_lock();  hlist_add_head(&clk->clks_node, &hw->core->clks);  clk_prepare_unlock();

     return clk; }

    /**  * __clk_core_init - initialize the data structures in a struct clk_core  * @core: clk_core being initialized  *  * Initializes the lists in struct clk_core, queries the hardware for the  * parent and rate and sets them both.  */ static int __clk_core_init(struct clk_core *core) {  int i, ret = 0;  struct clk_core *orphan;  struct hlist_node *tmp2;  unsigned long rate;

     if (!core)   return -EINVAL;

     clk_prepare_lock();

     /* check to see if a clock with this name is already registered */  if (clk_core_lookup(core->name)) {   pr_debug("%s: clk %s already initialized\n",     __func__, core->name);   ret = -EEXIST;   goto out;  }

     /* check that clk_ops are sane.  See Documentation/clk.txt */  if (core->ops->set_rate &&      !((core->ops->round_rate || core->ops->determine_rate) &&        core->ops->recalc_rate)) {   pr_err("%s: %s must implement .round_rate or .determine_rate in addition to .recalc_rate\n",          __func__, core->name);   ret = -EINVAL;   goto out;  }

     if (core->ops->set_parent && !core->ops->get_parent) {   pr_err("%s: %s must implement .get_parent & .set_parent\n",          __func__, core->name);   ret = -EINVAL;   goto out;  }

     if (core->num_parents > 1 && !core->ops->get_parent) {   pr_err("%s: %s must implement .get_parent as it has multi parents\n",          __func__, core->name);   ret = -EINVAL;   goto out;  }

     if (core->ops->set_rate_and_parent &&    !(core->ops->set_parent && core->ops->set_rate)) {   pr_err("%s: %s must implement .set_parent & .set_rate\n",     __func__, core->name);   ret = -EINVAL;   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);

     /*   * Populate core->parent if parent has already been clk_core_init'd. If   * parent has not yet been clk_core_init'd then place clk in the orphan   * list.  If clk doesn't have any parents then place it in the root   * clk list.   *   * Every time a new clk is clk_init'd then we walk the list of orphan   * clocks and re-parent any that are children of the clock currently   * being clk_init'd.   */  if (core->parent) {   hlist_add_head(&core->child_node,     &core->parent->children);   core->orphan = core->parent->orphan;  } else if (!core->num_parents) {   hlist_add_head(&core->child_node, &clk_root_list);   core->orphan = false;  } else {   hlist_add_head(&core->child_node, &clk_orphan_list);   core->orphan = true;  }

     /*   * Set clk's accuracy.  The preferred method is to use   * .recalc_accuracy. For simple clocks and lazy developers the default   * fallback is to use the parent's accuracy.  If a clock doesn't have a   * parent (or is orphaned) then accuracy is set to zero (perfect   * clock).   */  if (core->ops->recalc_accuracy)   core->accuracy = core->ops->recalc_accuracy(core->hw,      __clk_get_accuracy(core->parent));  else if (core->parent)   core->accuracy = core->parent->accuracy;  else   core->accuracy = 0;

     /*   * Set clk's phase.   * Since a phase is by definition relative to its parent, just   * query the current clock phase, or just assume it's in phase.   */  if (core->ops->get_phase)   core->phase = core->ops->get_phase(core->hw);  else   core->phase = 0;

     /*   * Set clk's rate.  The preferred method is to use .recalc_rate.  For   * simple clocks and lazy developers the default fallback is to use the   * parent's rate.  If a clock doesn't have a parent (or is orphaned)   * then rate is set to zero.   */  if (core->ops->recalc_rate)   rate = core->ops->recalc_rate(core->hw,     clk_core_get_rate_nolock(core->parent));  else if (core->parent)   rate = core->parent->rate;  else   rate = 0;  core->rate = core->req_rate = rate;

     /*   * walk the list of orphan clocks and reparent any that newly finds a   * parent.   */  hlist_for_each_entry_safe(orphan, tmp2, &clk_orphan_list, child_node) {   struct clk_core *parent = __clk_init_parent(orphan);

      /*    * we could call __clk_set_parent, but that would result in a    * redundant call to the .set_rate op, if it exists    */   if (parent) {    __clk_set_parent_before(orphan, parent);    __clk_set_parent_after(orphan, parent, NULL);    __clk_recalc_accuracies(orphan);    __clk_recalc_rates(orphan, 0);   }  }

     /*   * optional platform-specific magic   *   * The .init callback is not used by any of the basic clock types, but   * exists for weird hardware that must perform initialization magic.   * Please consider other ways of solving initialization problems before   * using this callback, as its use is discouraged.   */  if (core->ops->init)   core->ops->init(core->hw);

     if (core->flags & CLK_IS_CRITICAL) {   unsigned long flags;

      clk_core_prepare(core);

      flags = clk_enable_lock();   clk_core_enable(core);   clk_enable_unlock(flags);  }

     kref_init(&core->ref); out:  clk_prepare_unlock();

     if (!ret)   clk_debug_register(core);

     return ret; }

     

     

     
    转载请注明原文地址: https://ju.6miu.com/read-9653.html

    最新回复(0)