Tuesday, July 31, 2018

Linux Clock - clk_hw_register_composite

note for myself:
clk_register_composite -> clk_hw_register_composite

struct clk *clk_register_composite(struct device *dev, const char *name,
   const char * const *parent_names, int num_parents,
   struct clk_hw *mux_hw, const struct clk_ops *mux_ops,
   struct clk_hw *rate_hw, const struct clk_ops *rate_ops,
   struct clk_hw *gate_hw, const struct clk_ops *gate_ops,
   unsigned long flags)
{
 struct clk_hw *hw;

 hw = clk_hw_register_composite(dev, name, parent_names, num_parents,
   mux_hw, mux_ops, rate_hw, rate_ops, gate_hw, gate_ops,
   flags);
 if (IS_ERR(hw))
  return ERR_CAST(hw);
 return hw->clk;
}

Key functions:
struct clk_hw *clk_hw_register_composite(struct device *dev, const char *name,
   const char * const *parent_names, int num_parents,
   struct clk_hw *mux_hw, const struct clk_ops *mux_ops,
   struct clk_hw *rate_hw, const struct clk_ops *rate_ops,
   struct clk_hw *gate_hw, const struct clk_ops *gate_ops,
   unsigned long flags)
{
 struct clk_hw *hw;
 struct clk_init_data init;
 struct clk_composite *composite;
 struct clk_ops *clk_composite_ops;
 int ret;

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

 init.name = name;
 init.flags = flags | CLK_IS_BASIC;
 init.parent_names = parent_names;
 init.num_parents = num_parents;
 hw = &composite->hw;

 clk_composite_ops = &composite->ops;

 if (mux_hw && mux_ops) {
  if (!mux_ops->get_parent) {
   hw = ERR_PTR(-EINVAL);
   goto err;
  }

  composite->mux_hw = mux_hw;
  composite->mux_ops = mux_ops;
  clk_composite_ops->get_parent = clk_composite_get_parent;
  if (mux_ops->set_parent)
   clk_composite_ops->set_parent = clk_composite_set_parent;
  if (mux_ops->determine_rate)
   clk_composite_ops->determine_rate = clk_composite_determine_rate;
 }

 if (rate_hw && rate_ops) {
  if (!rate_ops->recalc_rate) {
   hw = ERR_PTR(-EINVAL);
   goto err;
  }
  clk_composite_ops->recalc_rate = clk_composite_recalc_rate;

  if (rate_ops->determine_rate)
   clk_composite_ops->determine_rate =
    clk_composite_determine_rate;
  else if (rate_ops->round_rate)
   clk_composite_ops->round_rate =
    clk_composite_round_rate;

  /* .set_rate requires either .round_rate or .determine_rate */
  if (rate_ops->set_rate) {
   if (rate_ops->determine_rate || rate_ops->round_rate)
    clk_composite_ops->set_rate =
      clk_composite_set_rate;
   else
    WARN(1, "%s: missing round_rate op is required\n",
      __func__);
  }

  composite->rate_hw = rate_hw;
  composite->rate_ops = rate_ops;
 }

 if (mux_hw && mux_ops && rate_hw && rate_ops) {
  if (mux_ops->set_parent && rate_ops->set_rate)
   clk_composite_ops->set_rate_and_parent =
   clk_composite_set_rate_and_parent;
 }

 if (gate_hw && gate_ops) {
  if (!gate_ops->is_enabled || !gate_ops->enable ||
      !gate_ops->disable) {
   hw = ERR_PTR(-EINVAL);
   goto err;
  }

  composite->gate_hw = gate_hw;
  composite->gate_ops = gate_ops;
  clk_composite_ops->is_enabled = clk_composite_is_enabled;
  clk_composite_ops->enable = clk_composite_enable;
  clk_composite_ops->disable = clk_composite_disable;
 }

 init.ops = clk_composite_ops;
 composite->hw.init = &init;

 ret = clk_hw_register(dev, hw);
 if (ret) {
  hw = ERR_PTR(ret);
  goto err;
 }

 if (composite->mux_hw)
  composite->mux_hw->clk = hw->clk;

 if (composite->rate_hw)
  composite->rate_hw->clk = hw->clk;

 if (composite->gate_hw)
  composite->gate_hw->clk = hw->clk;

 return hw;

err:
 kfree(composite);
 return hw;
}
Composite is rather a large piece, decode part by part.
Composite is combined of mux, rate and gate.
All these: mux, rate and gate are not to be worried as these structs are passed from outside to inside.

what these do:
clk_composite_ops->is_enabled = clk_composite_is_enabled;
clk_composite_ops->enable = clk_composite_enable;
clk_composite_ops->disable = clk_composite_disable;
A rough look at clk_composite, it is doing the clock gating.

example usage at drivers/clk/clk-stm32h7.c#L1323
  /* Register the 3 output dividers */
  for (odf = 0; odf < 3; odf++) {
   int idx = n * 3 + odf;

   get_cfg_composite_div(&odf_clk_gcfg, &stm32_odf[n][odf],
     &c_cfg, &stm32rcc_lock);

   hws[ODF_BANK + idx] = clk_hw_register_composite(NULL,
     stm32_odf[n][odf].name,
     stm32_odf[n][odf].parent_name,
     stm32_odf[n][odf].num_parents,
     c_cfg.mux_hw, c_cfg.mux_ops,
     c_cfg.div_hw, c_cfg.div_ops,
     c_cfg.gate_hw, c_cfg.gate_ops,
     stm32_odf[n][odf].flags);
  }

All the c_cfg information come from "stm32_odf", which is passed through "get_cfg_composite_div", while ops is assigned at "odf_clk_gcfg".
1.
static struct composite_clk_gcfg odf_clk_gcfg = {
 M_CFG_DIV(&odf_divider_ops, 0),
 M_CFG_GATE(&odf_gate_ops, 0),
};
2.
#define M_CFG_DIV(_rate_ops, _rate_flags)\ .div = &(struct composite_clk_gcfg_t) {_rate_flags, _rate_ops} #define M_CFG_GATE(_gate_ops, _gate_flags)\ .gate = &(struct composite_clk_gcfg_t) { _gate_flags, _gate_ops}
3.
/* * General config definition of a composite clock (only clock diviser for rate) */ struct composite_clk_gcfg { struct composite_clk_gcfg_t *mux; struct composite_clk_gcfg_t *div; struct composite_clk_gcfg_t *gate; };

"stm32_odf"

#define M_ODF_F(_name, _parent, _gate_offset,  _bit_idx, _rate_offset,\
  _rate_shift, _rate_width, _flags)\
{\
 .mux = NULL,\
 .div = &(struct muxdiv_cfg) {_rate_offset, _rate_shift, _rate_width},\
 .gate = &(struct gate_cfg) {_gate_offset, _bit_idx },\
 .name = _name,\
 .parent_name = &(const char *) {_parent},\
 .num_parents = 1,\
 .flags = _flags,\
}

static const struct composite_clk_cfg stm32_odf[3][3] = {
 {
  M_ODF_F("pll1_p", "vco1", RCC_PLLCFGR, 16, RCC_PLL1DIVR,  9, 7,
    CLK_IGNORE_UNUSED),
  M_ODF_F("pll1_q", "vco1", RCC_PLLCFGR, 17, RCC_PLL1DIVR, 16, 7,
    CLK_IGNORE_UNUSED),
  M_ODF_F("pll1_r", "vco1", RCC_PLLCFGR, 18, RCC_PLL1DIVR, 24, 7,
    CLK_IGNORE_UNUSED),
 },
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
_gate_offset,  _bit_idx == RCC_PLLCFGR, 16:
Bit 16 DIVP1EN: PLL1 DIVP divider output enable
Set and reset by software to enable the pll1_p_ck output of the PLL1.
0: pll1_p_ck output is disabled
1: pll1_p_ck output is enabled (default after reset)
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
_rate_offset, _rate_shift, _rate_width == RCC_PLL1DIVR,  9, 7:
Bits 15:9 DIVP1[6:0]: PLL1 DIVP division factor
Set and reset by software to control the frequency of the pll1_p_ck clock.
0000000: Not allowed
0000001: pll1_p_ck = vco1_ck / 2 (default after reset)
0000010: Not allowed
0000011: pll1_p_ck = vco1_ck / 4
...
1111111: pll1_p_ck = vco1_ck / 128
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#define CLK_IGNORE_UNUSED BIT(3) /* do not gate even if unused */
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

Just the particular function's register and bit information are passed inside. Cannot find the settings.

I found one  PLLs Initialization Flowchart


Seem like it is registering the PLL clock, which is consisted of gate and div. Still not sure where the actual settings are done. Maybe in uboot side - i guess.


No comments:

Post a Comment