in rtc-sun6i.c [212:321]
static void __init sun6i_rtc_clk_init(struct device_node *node,
const struct sun6i_rtc_clk_data *data)
{
struct clk_hw_onecell_data *clk_data;
struct sun6i_rtc_dev *rtc;
struct clk_init_data init = {
.ops = &sun6i_rtc_osc_ops,
.name = "losc",
};
const char *iosc_name = "rtc-int-osc";
const char *clkout_name = "osc32k-out";
const char *parents[2];
u32 reg;
rtc = kzalloc(sizeof(*rtc), GFP_KERNEL);
if (!rtc)
return;
rtc->data = data;
clk_data = kzalloc(struct_size(clk_data, hws, 3), GFP_KERNEL);
if (!clk_data) {
kfree(rtc);
return;
}
spin_lock_init(&rtc->lock);
rtc->base = of_io_request_and_map(node, 0, of_node_full_name(node));
if (IS_ERR(rtc->base)) {
pr_crit("Can't map RTC registers");
goto err;
}
reg = SUN6I_LOSC_CTRL_KEY;
if (rtc->data->has_auto_swt) {
/* Bypass auto-switch to int osc, on ext losc failure */
reg |= SUN6I_LOSC_CTRL_AUTO_SWT_BYPASS;
writel(reg, rtc->base + SUN6I_LOSC_CTRL);
}
/* Switch to the external, more precise, oscillator, if present */
if (of_get_property(node, "clocks", NULL)) {
reg |= SUN6I_LOSC_CTRL_EXT_OSC;
if (rtc->data->has_losc_en)
reg |= SUN6I_LOSC_CTRL_EXT_LOSC_EN;
}
writel(reg, rtc->base + SUN6I_LOSC_CTRL);
/* Yes, I know, this is ugly. */
sun6i_rtc = rtc;
/* Only read IOSC name from device tree if it is exported */
if (rtc->data->export_iosc)
of_property_read_string_index(node, "clock-output-names", 2,
&iosc_name);
rtc->int_osc = clk_hw_register_fixed_rate_with_accuracy(NULL,
iosc_name,
NULL, 0,
rtc->data->rc_osc_rate,
300000000);
if (IS_ERR(rtc->int_osc)) {
pr_crit("Couldn't register the internal oscillator\n");
goto err;
}
parents[0] = clk_hw_get_name(rtc->int_osc);
/* If there is no external oscillator, this will be NULL and ... */
parents[1] = of_clk_get_parent_name(node, 0);
rtc->hw.init = &init;
init.parent_names = parents;
/* ... number of clock parents will be 1. */
init.num_parents = of_clk_get_parent_count(node) + 1;
of_property_read_string_index(node, "clock-output-names", 0,
&init.name);
rtc->losc = clk_register(NULL, &rtc->hw);
if (IS_ERR(rtc->losc)) {
pr_crit("Couldn't register the LOSC clock\n");
goto err_register;
}
of_property_read_string_index(node, "clock-output-names", 1,
&clkout_name);
rtc->ext_losc = clk_register_gate(NULL, clkout_name, init.name,
0, rtc->base + SUN6I_LOSC_OUT_GATING,
SUN6I_LOSC_OUT_GATING_EN_OFFSET, 0,
&rtc->lock);
if (IS_ERR(rtc->ext_losc)) {
pr_crit("Couldn't register the LOSC external gate\n");
goto err_register;
}
clk_data->num = 2;
clk_data->hws[0] = &rtc->hw;
clk_data->hws[1] = __clk_get_hw(rtc->ext_losc);
if (rtc->data->export_iosc) {
clk_data->hws[2] = rtc->int_osc;
clk_data->num = 3;
}
of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
return;
err_register:
clk_hw_unregister_fixed_rate(rtc->int_osc);
err:
kfree(clk_data);
}