static void __init sun6i_rtc_clk_init()

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);
}