/* * According to the programming manual, a timer channel's registers can * only be accessed when the channel's stop bit is clear.
*/
enabled = !!ingenic_tcu_is_enabled(hw);
regmap_write(tcu->map, TCU_REG_TSCR, BIT(info->gate_bit));
map = device_node_to_regmap(np); if (IS_ERR(map)) return PTR_ERR(map);
tcu = kzalloc(sizeof(*tcu), GFP_KERNEL); if (!tcu) return -ENOMEM;
tcu->map = map;
tcu->soc_info = id->data;
if (tcu->soc_info->has_tcu_clk) {
tcu->clk = of_clk_get_by_name(np, "tcu"); if (IS_ERR(tcu->clk)) {
ret = PTR_ERR(tcu->clk);
/* * Old device trees for some SoCs did not include the * TCU clock because this driver (incorrectly) didn't * use it. In this case we complain loudly and attempt * to continue without the clock, which might work if * booting with workarounds like "clk_ignore_unused".
*/ if (tcu->soc_info->allow_missing_tcu_clk && ret == -EINVAL) {
pr_warn("TCU clock missing from device tree, please update your device tree\n");
tcu->clk = NULL;
} else {
pr_crit("Cannot get TCU clock from device tree\n"); goto err_free_tcu;
}
} else {
ret = clk_prepare_enable(tcu->clk); if (ret) {
pr_crit("Unable to enable TCU clock\n"); goto err_put_clk;
}
}
}
tcu->clocks = kzalloc(struct_size(tcu->clocks, hws, TCU_CLK_COUNT),
GFP_KERNEL); if (!tcu->clocks) {
ret = -ENOMEM; goto err_clk_disable;
}
tcu->clocks->num = TCU_CLK_COUNT;
for (i = 0; i < tcu->soc_info->num_channels; i++) {
ret = ingenic_tcu_register_clock(tcu, i, TCU_PARENT_EXT,
&ingenic_tcu_clk_info[i],
tcu->clocks); if (ret) {
pr_crit("cannot register clock %d\n", i); goto err_unregister_timer_clocks;
}
}
/* * We set EXT as the default parent clock for all the TCU clocks * except for the watchdog one, where we set the RTC clock as the * parent. Since the EXT and PCLK are much faster than the RTC clock, * the watchdog would kick after a maximum time of 5s, and we might * want a slower kicking time.
*/
ret = ingenic_tcu_register_clock(tcu, TCU_CLK_WDT, TCU_PARENT_RTC,
&ingenic_tcu_watchdog_clk_info,
tcu->clocks); if (ret) {
pr_crit("cannot register watchdog clock\n"); goto err_unregister_timer_clocks;
}
if (tcu->soc_info->has_ost) {
ret = ingenic_tcu_register_clock(tcu, TCU_CLK_OST,
TCU_PARENT_EXT,
&ingenic_tcu_ost_clk_info,
tcu->clocks); if (ret) {
pr_crit("cannot register ost clock\n"); goto err_unregister_watchdog_clock;
}
}
ret = of_clk_add_hw_provider(np, of_clk_hw_onecell_get, tcu->clocks); if (ret) {
pr_crit("cannot add OF clock provider\n"); goto err_unregister_ost_clock;
}
ingenic_tcu = tcu;
return 0;
err_unregister_ost_clock: if (tcu->soc_info->has_ost)
clk_hw_unregister(tcu->clocks->hws[i + 1]);
err_unregister_watchdog_clock:
clk_hw_unregister(tcu->clocks->hws[i]);
err_unregister_timer_clocks: for (i = 0; i < tcu->clocks->num; i++) if (tcu->clocks->hws[i])
clk_hw_unregister(tcu->clocks->hws[i]);
kfree(tcu->clocks);
err_clk_disable: if (tcu->clk)
clk_disable_unprepare(tcu->clk);
err_put_clk: if (tcu->clk)
clk_put(tcu->clk);
err_free_tcu:
kfree(tcu); return ret;
}
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.