Sunday, July 22, 2018

Linux Clock - clk_hw_register_gate

Note for myself:
clk_hw_register_gate 

Always start with source code:
/**
 * clk_hw_register_gate - register a gate clock with the clock framework
 * @dev: device that is registering this clock
 * @name: name of this clock
 * @parent_name: name of this clock's parent
 * @flags: framework-specific flags for this clock
 * @reg: register address to control gating of this clock
 * @bit_idx: which bit in the register controls gating of this clock
 * @clk_gate_flags: gate-specific flags for this clock
 * @lock: shared register lock for this clock
 */
struct clk_hw *clk_hw_register_gate(struct device *dev, const char *name,
  const char *parent_name, unsigned long flags,
  void __iomem *reg, u8 bit_idx,
  u8 clk_gate_flags, spinlock_t *lock)
{
 struct clk_gate *gate;
 struct clk_hw *hw;
 struct clk_init_data init;
 int ret;

 if (clk_gate_flags & CLK_GATE_HIWORD_MASK) {
  if (bit_idx > 15) {
   pr_err("gate bit exceeds LOWORD field\n");
   return ERR_PTR(-EINVAL);
  }
 }

 /* allocate the gate */
 gate = kzalloc(sizeof(*gate), GFP_KERNEL);
 if (!gate)
  return ERR_PTR(-ENOMEM);

 init.name = name;
 init.ops = &clk_gate_ops;
 init.flags = flags | CLK_IS_BASIC;
 init.parent_names = parent_name ? &parent_name : NULL;
 init.num_parents = parent_name ? 1 : 0;

 /* struct clk_gate assignments */
 gate->reg = reg;
 gate->bit_idx = bit_idx;
 gate->flags = clk_gate_flags;
 gate->lock = lock;
 gate->hw.init = &init;

 hw = &gate->hw;
 ret = clk_hw_register(dev, hw);
 if (ret) {
  kfree(gate);
  hw = ERR_PTR(ret);
 }

 return hw;
}
EXPORT_SYMBOL_GPL(clk_hw_register_gate);

What does it meant by CLK_IS_BASIC, the definition is all come from clk-provider.h

/*
 * flags used across common struct clk.  these flags should only affect the
 * top-level framework.  custom flags for dealing with hardware specifics
 * belong in struct clk_foo
 *
 * Please update clk_flags[] in drivers/clk/clk.c when making changes here!
 */
#define CLK_SET_RATE_GATE BIT(0) /* must be gated across rate change */
#define CLK_SET_PARENT_GATE BIT(1) /* must be gated across re-parent */
#define CLK_SET_RATE_PARENT BIT(2) /* propagate rate change up one level */
#define CLK_IGNORE_UNUSED BIT(3) /* do not gate even if unused */
    /* unused */
#define CLK_IS_BASIC  BIT(5) /* Basic clk, can't do a to_clk_foo() */
#define CLK_GET_RATE_NOCACHE BIT(6) /* do not use the cached clk rate */
#define CLK_SET_RATE_NO_REPARENT BIT(7) /* don't re-parent on rate change */
#define CLK_GET_ACCURACY_NOCACHE BIT(8) /* do not use the cached clk accuracy */
#define CLK_RECALC_NEW_RATES BIT(9) /* recalc rates after notifications */
#define CLK_SET_RATE_UNGATE BIT(10) /* clock needs to run to set rate */
#define CLK_IS_CRITICAL  BIT(11) /* do not gate, ever */
/* parents need enable during gate/ungate, set rate and re-parent */
#define CLK_OPS_PARENT_ENABLE BIT(12)
all these flags are used in difference driver files, seem like the common clock framework had cater for most cases, also it is making the framework getting complicated. Where is details documentation and reference code. Guess more to code readings and tracking.

This line init.ops = &clk_gate_ops; linked to following code

const struct clk_ops clk_gate_ops = {
 .enable = clk_gate_enable,
 .disable = clk_gate_disable,
 .is_enabled = clk_gate_is_enabled,
};
EXPORT_SYMBOL_GPL(clk_gate_ops);

tracing a bit: clk_gate_enable -> clk_gate_endisable(hw, 1);

/*
 * It works on following logic:
 *
 * For enabling clock, enable = 1
 * set2dis = 1 -> clear bit -> set = 0
 * set2dis = 0 -> set bit -> set = 1
 *
 * For disabling clock, enable = 0
 * set2dis = 1 -> set bit -> set = 1
 * set2dis = 0 -> clear bit -> set = 0
 *
 * So, result is always: enable xor set2dis.
 */
static void clk_gate_endisable(struct clk_hw *hw, int enable)
{
 struct clk_gate *gate = to_clk_gate(hw);
 int set = gate->flags & CLK_GATE_SET_TO_DISABLE ? 1 : 0;
 unsigned long uninitialized_var(flags);
 u32 reg;

 set ^= enable;

 if (gate->lock)
  spin_lock_irqsave(gate->lock, flags);
 else
  __acquire(gate->lock);

 if (gate->flags & CLK_GATE_HIWORD_MASK) {
  reg = BIT(gate->bit_idx + 16);
  if (set)
   reg |= BIT(gate->bit_idx);
 } else {
  reg = clk_readl(gate->reg);

  if (set)
   reg |= BIT(gate->bit_idx);
  else
   reg &= ~BIT(gate->bit_idx);
 }

 clk_writel(reg, gate->reg);

 if (gate->lock)
  spin_unlock_irqrestore(gate->lock, flags);
 else
  __release(gate->lock);
}
Above is the framework working behind on how to do clock gating.

Below is the usage this function in driver clock "clk-stm32h7.c":

/* PERIF CLOCKS */
struct pclk_t {
 u32 gate_offset;
 u8 bit_idx;
 const char *name;
 const char *parent;
 u32 flags;
};

#define PER_CLK(_gate_offset, _bit_idx, _name, _parent)\
 PER_CLKF(_gate_offset, _bit_idx, _name, _parent, 0)

static const struct pclk_t pclk[] = {
 PER_CLK(RCC_AHB3ENR, 31, "d1sram1", "hclk"),
 PER_CLK(RCC_AHB3ENR, 30, "itcm", "hclk"),
 PER_CLK(RCC_AHB3ENR, 29, "dtcm2", "hclk"),
 PER_CLK(RCC_AHB3ENR, 28, "dtcm1", "hclk"),
 PER_CLK(RCC_AHB3ENR, 8, "flitf", "hclk"),
 PER_CLK(RCC_AHB3ENR, 5, "jpgdec", "hclk"),
 PER_CLK(RCC_AHB3ENR, 4, "dma2d", "hclk"),
 PER_CLK(RCC_AHB3ENR, 0, "mdma", "hclk"),
all the clocks information is passed to this struct pclk[];
_gate_offset = RCC_AHB3ENR = 0xD4
_bit_idx = 31
_name = d1sram1
_parent = hclk

I looked back "RM0433: STM32H743/753 and STM32H750 advanced ARM®-based 32-bit MCUs", page 428.
Strangely, I cannot match back the register.
Bits 31:17 Reserved
And Bits 11:6 also Reserved

only bit 0, 4 and 5 able to be matched.
Bit 5 JPGDECEN: JPGDEC Peripheral Clock Enable
Bit 4 DMA2DEN: DMA2D Peripheral Clock Enable
Bit 0 MDMAEN: MDMA Peripheral Clock Enable
and it is matching the description name.

oh, well. As long as something matched.

Back to driver code again:

 /* Peripheral clocks */
 for (n = 0; n < ARRAY_SIZE(pclk); n++)
  hws[PERIF_BANK + n] = clk_hw_register_gate(NULL, pclk[n].name,
    pclk[n].parent,
    pclk[n].flags, base + pclk[n].gate_offset,
    pclk[n].bit_idx, pclk[n].flags, &stm32rcc_lock);

This is where the struct pclk[] is registered to clock framework.


The more I read, the more I need to read.

No comments:

Post a Comment