Sunday, July 22, 2018

Linux Clock - clk_hw_register_mux_table

note for myself:

For mux, I still dont understand most of its.
The frame:
/**
 * struct clk_mux - multiplexer clock
 *
 * @hw:  handle between common and hardware-specific interfaces
 * @reg: register controlling multiplexer
 * @table: array of register values corresponding to the parent index
 * @shift: shift to multiplexer bit field
 * @mask: mask of mutliplexer bit field
 * @flags: hardware-specific flags
 * @lock: register lock
 *
 * Clock with multiple selectable parents.  Implements .get_parent, .set_parent
 * and .recalc_rate
 *
 * Flags:
 * CLK_MUX_INDEX_ONE - register index starts at 1, not 0
 * CLK_MUX_INDEX_BIT - register index is a single bit (power of two)
 * CLK_MUX_HIWORD_MASK - The mux settings are only in lower 16-bit of this
 * register, and mask of mux bits are in higher 16-bit of this register.
 * While setting the mux bits, higher 16-bit should also be updated to
 * indicate changing mux bits.
 * CLK_MUX_ROUND_CLOSEST - Use the parent rate that is closest to the desired
 * frequency.
 */
struct clk_mux {
 struct clk_hw hw;
 void __iomem *reg;
 u32  *table;
 u32  mask;
 u8  shift;
 u8  flags;
 spinlock_t *lock;
};

#define to_clk_mux(_hw) container_of(_hw, struct clk_mux, hw)

#define CLK_MUX_INDEX_ONE  BIT(0)
#define CLK_MUX_INDEX_BIT  BIT(1)
#define CLK_MUX_HIWORD_MASK  BIT(2)
#define CLK_MUX_READ_ONLY  BIT(3) /* mux can't be changed */
#define CLK_MUX_ROUND_CLOSEST  BIT(4)

extern const struct clk_ops clk_mux_ops;
extern const struct clk_ops clk_mux_ro_ops;

struct clk *clk_register_mux(struct device *dev, const char *name,
  const char * const *parent_names, u8 num_parents,
  unsigned long flags,
  void __iomem *reg, u8 shift, u8 width,
  u8 clk_mux_flags, spinlock_t *lock);
struct clk_hw *clk_hw_register_mux(struct device *dev, const char *name,
  const char * const *parent_names, u8 num_parents,
  unsigned long flags,
  void __iomem *reg, u8 shift, u8 width,
  u8 clk_mux_flags, spinlock_t *lock);

struct clk *clk_register_mux_table(struct device *dev, const char *name,
  const char * const *parent_names, u8 num_parents,
  unsigned long flags,
  void __iomem *reg, u8 shift, u32 mask,
  u8 clk_mux_flags, u32 *table, spinlock_t *lock);
struct clk_hw *clk_hw_register_mux_table(struct device *dev, const char *name,
  const char * const *parent_names, u8 num_parents,
  unsigned long flags,
  void __iomem *reg, u8 shift, u32 mask,
  u8 clk_mux_flags, u32 *table, spinlock_t *lock);

int clk_mux_val_to_index(struct clk_hw *hw, u32 *table, unsigned int flags,
    unsigned int val);
unsigned int clk_mux_index_to_val(u32 *table, unsigned int flags, u8 index);

void clk_unregister_mux(struct clk *clk);
void clk_hw_unregister_mux(struct clk_hw *hw);

And from "drivers/clk/clk-mux.c"
clk_register_mux -> clk_register_mux_table -> clk_hw_register_mux_table
clk_hw_register_mux ->  clk_hw_register_mux_table

Let see the differences:
struct clk *clk_register_mux_table(struct device *dev, const char *name,
  const char * const *parent_names, u8 num_parents,
  unsigned long flags,
  void __iomem *reg, u8 shift, u32 mask,
  u8 clk_mux_flags, u32 *table, spinlock_t *lock)
{
 struct clk_hw *hw;

 hw = clk_hw_register_mux_table(dev, name, parent_names, num_parents,
           flags, reg, shift, mask, clk_mux_flags,
           table, lock);
 if (IS_ERR(hw))
  return ERR_CAST(hw);
 return hw->clk;
}
EXPORT_SYMBOL_GPL(clk_register_mux_table);

struct clk *clk_register_mux(struct device *dev, const char *name,
  const char * const *parent_names, u8 num_parents,
  unsigned long flags,
  void __iomem *reg, u8 shift, u8 width,
  u8 clk_mux_flags, spinlock_t *lock)
{
 u32 mask = BIT(width) - 1;

 return clk_register_mux_table(dev, name, parent_names, num_parents,
          flags, reg, shift, mask, clk_mux_flags,
          NULL, lock);
}
EXPORT_SYMBOL_GPL(clk_register_mux);

struct clk_hw *clk_hw_register_mux(struct device *dev, const char *name,
  const char * const *parent_names, u8 num_parents,
  unsigned long flags,
  void __iomem *reg, u8 shift, u8 width,
  u8 clk_mux_flags, spinlock_t *lock)
{
 u32 mask = BIT(width) - 1;

 return clk_hw_register_mux_table(dev, name, parent_names, num_parents,
          flags, reg, shift, mask, clk_mux_flags,
          NULL, lock);
}
EXPORT_SYMBOL_GPL(clk_hw_register_mux);

clk_register_mux  and clk_hw_register_mux:
do "u32 mask = BIT(width) - 1;"
and passing NULL to xxx_register_mux_table

clk_register_mux_table and clk_hw_register_mux_table:
almost the same, but clk_register_mux_table do error checking.

I should study "clk_hw_register_mux_table" is the function doing all the work
struct clk_hw *clk_hw_register_mux_table(struct device *dev, const char *name,
  const char * const *parent_names, u8 num_parents,
  unsigned long flags,
  void __iomem *reg, u8 shift, u32 mask,
  u8 clk_mux_flags, u32 *table, spinlock_t *lock)
{
 struct clk_mux *mux;
 struct clk_hw *hw;
 struct clk_init_data init;
 u8 width = 0;
 int ret;

 if (clk_mux_flags & CLK_MUX_HIWORD_MASK) {
  width = fls(mask) - ffs(mask) + 1;
  if (width + shift > 16) {
   pr_err("mux value exceeds LOWORD field\n");
   return ERR_PTR(-EINVAL);
  }
 }

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

 init.name = name;
 if (clk_mux_flags & CLK_MUX_READ_ONLY)
  init.ops = &clk_mux_ro_ops;
 else
  init.ops = &clk_mux_ops;
 init.flags = flags | CLK_IS_BASIC;
 init.parent_names = parent_names;
 init.num_parents = num_parents;

 /* struct clk_mux assignments */
 mux->reg = reg;
 mux->shift = shift;
 mux->mask = mask;
 mux->flags = clk_mux_flags;
 mux->lock = lock;
 mux->table = table;
 mux->hw.init = &init;

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

 return hw;
}
EXPORT_SYMBOL_GPL(clk_hw_register_mux_table);
Copy everything here for better viewing.
there are two ops, this code is self-explainable.
if (clk_mux_flags & CLK_MUX_READ_ONLY)
init.ops = &clk_mux_ro_ops;
else
init.ops = &clk_mux_ops;


const struct clk_ops clk_mux_ops = {
 .get_parent = clk_mux_get_parent,
 .set_parent = clk_mux_set_parent,
 .determine_rate = clk_mux_determine_rate,
};
EXPORT_SYMBOL_GPL(clk_mux_ops);

const struct clk_ops clk_mux_ro_ops = {
 .get_parent = clk_mux_get_parent,
};
EXPORT_SYMBOL_GPL(clk_mux_ro_ops);

clk_mux_set_parent:
static int clk_mux_set_parent(struct clk_hw *hw, u8 index)
{
 struct clk_mux *mux = to_clk_mux(hw);
 u32 val = clk_mux_index_to_val(mux->table, mux->flags, index);
 unsigned long flags = 0;
 u32 reg;

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

 if (mux->flags & CLK_MUX_HIWORD_MASK) {
  reg = mux->mask << (mux->shift + 16);
 } else {
  reg = clk_readl(mux->reg);
  reg &= ~(mux->mask << mux->shift);
 }
 val = val << mux->shift;
 reg |= val;
 clk_writel(reg, mux->reg);

 if (mux->lock)
  spin_unlock_irqrestore(mux->lock, flags);
 else
  __release(mux->lock);

 return 0;
}

clk_mux_index_to_val:

unsigned int clk_mux_index_to_val(u32 *table, unsigned int flags, u8 index)
{
 unsigned int val = index;

 if (table) {
  val = table[index];
 } else {
  if (flags & CLK_MUX_INDEX_BIT)
   val = 1 << index;

  if (flags & CLK_MUX_INDEX_ONE)
   val++;
 }

 return val;
}
EXPORT_SYMBOL_GPL(clk_mux_index_to_val);
4 ways to obtain the val:
1. from predefined table val = table[index];
2. one bit mask is corresponding to one source
3. value + 1 corresponded to one clock source
4. value directly read out from register

After the val is obtained, it is normal write to particular register.
val = val << mux->shift;
reg |= val;
clk_writel(reg, mux->reg);

clk_mux_get_parent:
now is how to get the parent clock.

static u8 clk_mux_get_parent(struct clk_hw *hw)
{
 struct clk_mux *mux = to_clk_mux(hw);
 u32 val;

 val = clk_readl(mux->reg) >> mux->shift;
 val &= mux->mask;

 return clk_mux_val_to_index(hw, mux->table, mux->flags, val);
}

// all information is at clk_mux_val_to_index

int clk_mux_val_to_index(struct clk_hw *hw, u32 *table, unsigned int flags,
    unsigned int val)
{
 int num_parents = clk_hw_get_num_parents(hw);

 if (table) {
  int i;

  for (i = 0; i < num_parents; i++)
   if (table[i] == val)
    return i;
  return -EINVAL;
 }

 if (val && (flags & CLK_MUX_INDEX_BIT))
  val = ffs(val) - 1;

 if (val && (flags & CLK_MUX_INDEX_ONE))
  val--;

 if (val >= num_parents)
  return -EINVAL;

 return val;
}
EXPORT_SYMBOL_GPL(clk_mux_val_to_index);
still 3 ways to do the mapping:
1. get the val from table - easiest to understand
2. return the least significant index bit value, for example, bit4 is passed to ffs, ffs will return 5. "val = ffs(val) - 1" = 4.
3. register index starts at 1, not 0.
4. register index starts at 0.

I need to read reference code to understand how to use this mux register.

No comments:

Post a Comment