/** * struct div6_clock - CPG 6 bit divider clock * @hw: handle between common and hardware-specific interfaces * @reg: IO-remapped register * @div: divisor value (1-64) * @src_mask: Bitmask covering the register bits to select the parent clock * @nb: Notifier block to save/restore clock state for system resume * @parents: Array to map from valid parent clocks indices to hardware indices
*/ struct div6_clock { struct clk_hw hw; void __iomem *reg; unsignedint div;
u32 src_mask; struct notifier_block nb;
u8 parents[];
};
val = readl(clock->reg);
val |= CPG_DIV6_CKSTP; /* * DIV6 clocks require the divisor field to be non-zero when stopping * the clock. However, some clocks (e.g. ZB on sh73a0) fail to be * re-enabled later if the divisor field is changed when stopping the * clock
*/ if (!(val & CPG_DIV6_DIV_MASK))
val |= CPG_DIV6_DIV_MASK;
writel(val, clock->reg);
}
val = readl(clock->reg) & ~CPG_DIV6_DIV_MASK; /* Only program the new divisor if the clock isn't stopped. */ if (!(val & CPG_DIV6_CKSTP))
writel(val | CPG_DIV6_DIV(clock->div - 1), clock->reg);
switch (action) { case PM_EVENT_RESUME: /* * TODO: This does not yet support DIV6 clocks with multiple * parents, as the parent selection bits are not restored. * Fortunately so far such DIV6 clocks are found only on * R/SH-Mobile SoCs, while the resume functionality is only * needed on R-Car Gen3.
*/ if (__clk_get_enable_count(clock->hw.clk))
cpg_div6_clock_enable(&clock->hw); else
cpg_div6_clock_disable(&clock->hw); return NOTIFY_OK;
}
return NOTIFY_DONE;
}
/** * cpg_div6_register - Register a DIV6 clock * @name: Name of the DIV6 clock * @num_parents: Number of parent clocks of the DIV6 clock (1, 4, or 8) * @parent_names: Array containing the names of the parent clocks * @reg: Mapped register used to control the DIV6 clock * @notifiers: Optional notifier chain to save/restore state for system resume
*/ struct clk * __init cpg_div6_register(constchar *name, unsignedint num_parents, constchar **parent_names, void __iomem *reg, struct raw_notifier_head *notifiers)
{ unsignedint valid_parents; struct clk_init_data init = {}; struct div6_clock *clock; struct clk *clk; unsignedint i;
clock = kzalloc(struct_size(clock, parents, num_parents), GFP_KERNEL); if (!clock) return ERR_PTR(-ENOMEM);
clock->reg = reg;
/* * Read the divisor. Disabling the clock overwrites the divisor, so we * need to cache its value for the enable operation.
*/
clock->div = (readl(clock->reg) & CPG_DIV6_DIV_MASK) + 1;
switch (num_parents) { case 1: /* fixed parent clock */
clock->src_mask = 0; break; case 4: /* clock with EXSRC bits 6-7 */
clock->src_mask = GENMASK(7, 6); break; case 8: /* VCLK with EXSRC bits 12-14 */
clock->src_mask = GENMASK(14, 12); break; default:
pr_err("%s: invalid number of parents for DIV6 clock %s\n",
__func__, name);
clk = ERR_PTR(-EINVAL); goto free_clock;
}
/* Filter out invalid parents */ for (i = 0, valid_parents = 0; i < num_parents; i++) { if (parent_names[i]) {
parent_names[valid_parents] = parent_names[i];
clock->parents[valid_parents] = i;
valid_parents++;
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.