static int vc5_probe()

in clk-versaclock5.c [908:1139]


static int vc5_probe(struct i2c_client *client)
{
	unsigned int oe, sd, src_mask = 0, src_val = 0;
	struct vc5_driver_data *vc5;
	struct clk_init_data init;
	const char *parent_names[2];
	unsigned int n, idx = 0;
	int ret;

	vc5 = devm_kzalloc(&client->dev, sizeof(*vc5), GFP_KERNEL);
	if (!vc5)
		return -ENOMEM;

	i2c_set_clientdata(client, vc5);
	vc5->client = client;
	vc5->chip_info = of_device_get_match_data(&client->dev);

	vc5->pin_xin = devm_clk_get(&client->dev, "xin");
	if (PTR_ERR(vc5->pin_xin) == -EPROBE_DEFER)
		return -EPROBE_DEFER;

	vc5->pin_clkin = devm_clk_get(&client->dev, "clkin");
	if (PTR_ERR(vc5->pin_clkin) == -EPROBE_DEFER)
		return -EPROBE_DEFER;

	vc5->regmap = devm_regmap_init_i2c(client, &vc5_regmap_config);
	if (IS_ERR(vc5->regmap))
		return dev_err_probe(&client->dev, PTR_ERR(vc5->regmap),
				     "failed to allocate register map\n");

	ret = of_property_read_u32(client->dev.of_node, "idt,shutdown", &sd);
	if (!ret) {
		src_mask |= VC5_PRIM_SRC_SHDN_EN_GBL_SHDN;
		if (sd)
			src_val |= VC5_PRIM_SRC_SHDN_EN_GBL_SHDN;
	} else if (ret != -EINVAL) {
		return dev_err_probe(&client->dev, ret,
				     "could not read idt,shutdown\n");
	}

	ret = of_property_read_u32(client->dev.of_node,
				   "idt,output-enable-active", &oe);
	if (!ret) {
		src_mask |= VC5_PRIM_SRC_SHDN_SP;
		if (oe)
			src_val |= VC5_PRIM_SRC_SHDN_SP;
	} else if (ret != -EINVAL) {
		return dev_err_probe(&client->dev, ret,
				     "could not read idt,output-enable-active\n");
	}

	regmap_update_bits(vc5->regmap, VC5_PRIM_SRC_SHDN, src_mask, src_val);

	/* Register clock input mux */
	memset(&init, 0, sizeof(init));

	if (!IS_ERR(vc5->pin_xin)) {
		vc5->clk_mux_ins |= VC5_MUX_IN_XIN;
		parent_names[init.num_parents++] = __clk_get_name(vc5->pin_xin);
	} else if (vc5->chip_info->flags & VC5_HAS_INTERNAL_XTAL) {
		vc5->pin_xin = clk_register_fixed_rate(&client->dev,
						       "internal-xtal", NULL,
						       0, 25000000);
		if (IS_ERR(vc5->pin_xin))
			return PTR_ERR(vc5->pin_xin);
		vc5->clk_mux_ins |= VC5_MUX_IN_XIN;
		parent_names[init.num_parents++] = __clk_get_name(vc5->pin_xin);
	}

	if (!IS_ERR(vc5->pin_clkin)) {
		vc5->clk_mux_ins |= VC5_MUX_IN_CLKIN;
		parent_names[init.num_parents++] =
		    __clk_get_name(vc5->pin_clkin);
	}

	if (!init.num_parents)
		return dev_err_probe(&client->dev, -EINVAL,
				     "no input clock specified!\n");

	/* Configure Optional Loading Capacitance for external XTAL */
	if (!(vc5->chip_info->flags & VC5_HAS_INTERNAL_XTAL)) {
		ret = vc5_update_cap_load(client->dev.of_node, vc5);
		if (ret)
			goto err_clk_register;
	}

	init.name = kasprintf(GFP_KERNEL, "%pOFn.mux", client->dev.of_node);
	init.ops = &vc5_mux_ops;
	init.flags = 0;
	init.parent_names = parent_names;
	vc5->clk_mux.init = &init;
	ret = devm_clk_hw_register(&client->dev, &vc5->clk_mux);
	if (ret)
		goto err_clk_register;
	kfree(init.name);	/* clock framework made a copy of the name */

	if (vc5->chip_info->flags & VC5_HAS_PFD_FREQ_DBL) {
		/* Register frequency doubler */
		memset(&init, 0, sizeof(init));
		init.name = kasprintf(GFP_KERNEL, "%pOFn.dbl",
				      client->dev.of_node);
		init.ops = &vc5_dbl_ops;
		init.flags = CLK_SET_RATE_PARENT;
		init.parent_names = parent_names;
		parent_names[0] = clk_hw_get_name(&vc5->clk_mux);
		init.num_parents = 1;
		vc5->clk_mul.init = &init;
		ret = devm_clk_hw_register(&client->dev, &vc5->clk_mul);
		if (ret)
			goto err_clk_register;
		kfree(init.name); /* clock framework made a copy of the name */
	}

	/* Register PFD */
	memset(&init, 0, sizeof(init));
	init.name = kasprintf(GFP_KERNEL, "%pOFn.pfd", client->dev.of_node);
	init.ops = &vc5_pfd_ops;
	init.flags = CLK_SET_RATE_PARENT;
	init.parent_names = parent_names;
	if (vc5->chip_info->flags & VC5_HAS_PFD_FREQ_DBL)
		parent_names[0] = clk_hw_get_name(&vc5->clk_mul);
	else
		parent_names[0] = clk_hw_get_name(&vc5->clk_mux);
	init.num_parents = 1;
	vc5->clk_pfd.init = &init;
	ret = devm_clk_hw_register(&client->dev, &vc5->clk_pfd);
	if (ret)
		goto err_clk_register;
	kfree(init.name);	/* clock framework made a copy of the name */

	/* Register PLL */
	memset(&init, 0, sizeof(init));
	init.name = kasprintf(GFP_KERNEL, "%pOFn.pll", client->dev.of_node);
	init.ops = &vc5_pll_ops;
	init.flags = CLK_SET_RATE_PARENT;
	init.parent_names = parent_names;
	parent_names[0] = clk_hw_get_name(&vc5->clk_pfd);
	init.num_parents = 1;
	vc5->clk_pll.num = 0;
	vc5->clk_pll.vc5 = vc5;
	vc5->clk_pll.hw.init = &init;
	ret = devm_clk_hw_register(&client->dev, &vc5->clk_pll.hw);
	if (ret)
		goto err_clk_register;
	kfree(init.name); /* clock framework made a copy of the name */

	/* Register FODs */
	for (n = 0; n < vc5->chip_info->clk_fod_cnt; n++) {
		idx = vc5_map_index_to_output(vc5->chip_info->model, n);
		memset(&init, 0, sizeof(init));
		init.name = kasprintf(GFP_KERNEL, "%pOFn.fod%d",
				      client->dev.of_node, idx);
		init.ops = &vc5_fod_ops;
		init.flags = CLK_SET_RATE_PARENT;
		init.parent_names = parent_names;
		parent_names[0] = clk_hw_get_name(&vc5->clk_pll.hw);
		init.num_parents = 1;
		vc5->clk_fod[n].num = idx;
		vc5->clk_fod[n].vc5 = vc5;
		vc5->clk_fod[n].hw.init = &init;
		ret = devm_clk_hw_register(&client->dev, &vc5->clk_fod[n].hw);
		if (ret)
			goto err_clk_register;
		kfree(init.name); /* clock framework made a copy of the name */
	}

	/* Register MUX-connected OUT0_I2C_SELB output */
	memset(&init, 0, sizeof(init));
	init.name = kasprintf(GFP_KERNEL, "%pOFn.out0_sel_i2cb",
			      client->dev.of_node);
	init.ops = &vc5_clk_out_ops;
	init.flags = CLK_SET_RATE_PARENT;
	init.parent_names = parent_names;
	parent_names[0] = clk_hw_get_name(&vc5->clk_mux);
	init.num_parents = 1;
	vc5->clk_out[0].num = idx;
	vc5->clk_out[0].vc5 = vc5;
	vc5->clk_out[0].hw.init = &init;
	ret = devm_clk_hw_register(&client->dev, &vc5->clk_out[0].hw);
	if (ret)
		goto err_clk_register;
	kfree(init.name); /* clock framework made a copy of the name */

	/* Register FOD-connected OUTx outputs */
	for (n = 1; n < vc5->chip_info->clk_out_cnt; n++) {
		idx = vc5_map_index_to_output(vc5->chip_info->model, n - 1);
		parent_names[0] = clk_hw_get_name(&vc5->clk_fod[idx].hw);
		if (n == 1)
			parent_names[1] = clk_hw_get_name(&vc5->clk_mux);
		else
			parent_names[1] =
			    clk_hw_get_name(&vc5->clk_out[n - 1].hw);

		memset(&init, 0, sizeof(init));
		init.name = kasprintf(GFP_KERNEL, "%pOFn.out%d",
				      client->dev.of_node, idx + 1);
		init.ops = &vc5_clk_out_ops;
		init.flags = CLK_SET_RATE_PARENT;
		init.parent_names = parent_names;
		init.num_parents = 2;
		vc5->clk_out[n].num = idx;
		vc5->clk_out[n].vc5 = vc5;
		vc5->clk_out[n].hw.init = &init;
		ret = devm_clk_hw_register(&client->dev, &vc5->clk_out[n].hw);
		if (ret)
			goto err_clk_register;
		kfree(init.name); /* clock framework made a copy of the name */

		/* Fetch Clock Output configuration from DT (if specified) */
		ret = vc5_get_output_config(client, &vc5->clk_out[n]);
		if (ret)
			goto err_clk;
	}

	ret = of_clk_add_hw_provider(client->dev.of_node, vc5_of_clk_get, vc5);
	if (ret) {
		dev_err_probe(&client->dev, ret,
			      "unable to add clk provider\n");
		goto err_clk;
	}

	return 0;

err_clk_register:
	dev_err_probe(&client->dev, ret,
		      "unable to register %s\n", init.name);
	kfree(init.name); /* clock framework made a copy of the name */
err_clk:
	if (vc5->chip_info->flags & VC5_HAS_INTERNAL_XTAL)
		clk_unregister_fixed_rate(vc5->pin_xin);
	return ret;
}