static void __init sama7g5_pmc_setup()

in at91/sama7g5.c [882:1132]


static void __init sama7g5_pmc_setup(struct device_node *np)
{
	const char *td_slck_name, *md_slck_name, *mainxtal_name;
	struct pmc_data *sama7g5_pmc;
	const char *parent_names[10];
	void **alloc_mem = NULL;
	int alloc_mem_size = 0;
	struct regmap *regmap;
	struct clk_hw *hw;
	bool bypass;
	int i, j;

	i = of_property_match_string(np, "clock-names", "td_slck");
	if (i < 0)
		return;

	td_slck_name = of_clk_get_parent_name(np, i);

	i = of_property_match_string(np, "clock-names", "md_slck");
	if (i < 0)
		return;

	md_slck_name = of_clk_get_parent_name(np, i);

	i = of_property_match_string(np, "clock-names", "main_xtal");
	if (i < 0)
		return;

	mainxtal_name = of_clk_get_parent_name(np, i);

	regmap = device_node_to_regmap(np);
	if (IS_ERR(regmap))
		return;

	sama7g5_pmc = pmc_data_allocate(PMC_CPU + 1,
					nck(sama7g5_systemck),
					nck(sama7g5_periphck),
					nck(sama7g5_gck), 8);
	if (!sama7g5_pmc)
		return;

	alloc_mem = kmalloc(sizeof(void *) *
			    (ARRAY_SIZE(sama7g5_mckx) + ARRAY_SIZE(sama7g5_gck)),
			    GFP_KERNEL);
	if (!alloc_mem)
		goto err_free;

	hw = at91_clk_register_main_rc_osc(regmap, "main_rc_osc", 12000000,
					   50000000);
	if (IS_ERR(hw))
		goto err_free;

	bypass = of_property_read_bool(np, "atmel,osc-bypass");

	hw = at91_clk_register_main_osc(regmap, "main_osc", mainxtal_name,
					bypass);
	if (IS_ERR(hw))
		goto err_free;

	parent_names[0] = "main_rc_osc";
	parent_names[1] = "main_osc";
	hw = at91_clk_register_sam9x5_main(regmap, "mainck", parent_names, 2);
	if (IS_ERR(hw))
		goto err_free;

	sama7g5_pmc->chws[PMC_MAIN] = hw;

	for (i = 0; i < PLL_ID_MAX; i++) {
		for (j = 0; j < 3; j++) {
			struct clk_hw *parent_hw;

			if (!sama7g5_plls[i][j].n)
				continue;

			switch (sama7g5_plls[i][j].t) {
			case PLL_TYPE_FRAC:
				if (!strcmp(sama7g5_plls[i][j].p, "mainck"))
					parent_hw = sama7g5_pmc->chws[PMC_MAIN];
				else
					parent_hw = __clk_get_hw(of_clk_get_by_name(np,
						sama7g5_plls[i][j].p));

				hw = sam9x60_clk_register_frac_pll(regmap,
					&pmc_pll_lock, sama7g5_plls[i][j].n,
					sama7g5_plls[i][j].p, parent_hw, i,
					sama7g5_plls[i][j].c,
					sama7g5_plls[i][j].l,
					sama7g5_plls[i][j].f);
				break;

			case PLL_TYPE_DIV:
				hw = sam9x60_clk_register_div_pll(regmap,
					&pmc_pll_lock, sama7g5_plls[i][j].n,
					sama7g5_plls[i][j].p, i,
					sama7g5_plls[i][j].c,
					sama7g5_plls[i][j].l,
					sama7g5_plls[i][j].f,
					sama7g5_plls[i][j].safe_div);
				break;

			default:
				continue;
			}

			if (IS_ERR(hw))
				goto err_free;

			if (sama7g5_plls[i][j].eid)
				sama7g5_pmc->chws[sama7g5_plls[i][j].eid] = hw;
		}
	}

	parent_names[0] = "cpupll_divpmcck";
	hw = at91_clk_register_master_div(regmap, "mck0", "cpupll_divpmcck",
					  &mck0_layout, &mck0_characteristics,
					  &pmc_mck0_lock, CLK_GET_RATE_NOCACHE, 5);
	if (IS_ERR(hw))
		goto err_free;

	sama7g5_pmc->chws[PMC_MCK] = hw;

	parent_names[0] = md_slck_name;
	parent_names[1] = td_slck_name;
	parent_names[2] = "mainck";
	for (i = 0; i < ARRAY_SIZE(sama7g5_mckx); i++) {
		u8 num_parents = 3 + sama7g5_mckx[i].ep_count;
		u32 *mux_table;

		mux_table = kmalloc_array(num_parents, sizeof(*mux_table),
					  GFP_KERNEL);
		if (!mux_table)
			goto err_free;

		SAMA7G5_INIT_TABLE(mux_table, 3);
		SAMA7G5_FILL_TABLE(&mux_table[3], sama7g5_mckx[i].ep_mux_table,
				   sama7g5_mckx[i].ep_count);
		SAMA7G5_FILL_TABLE(&parent_names[3], sama7g5_mckx[i].ep,
				   sama7g5_mckx[i].ep_count);

		hw = at91_clk_sama7g5_register_master(regmap, sama7g5_mckx[i].n,
				   num_parents, parent_names, mux_table,
				   &pmc_mckX_lock, sama7g5_mckx[i].id,
				   sama7g5_mckx[i].c,
				   sama7g5_mckx[i].ep_chg_id);
		if (IS_ERR(hw))
			goto err_free;

		alloc_mem[alloc_mem_size++] = mux_table;
	}

	hw = at91_clk_sama7g5_register_utmi(regmap, "utmick", "main_xtal");
	if (IS_ERR(hw))
		goto err_free;

	sama7g5_pmc->chws[PMC_UTMI] = hw;

	parent_names[0] = md_slck_name;
	parent_names[1] = td_slck_name;
	parent_names[2] = "mainck";
	parent_names[3] = "syspll_divpmcck";
	parent_names[4] = "ddrpll_divpmcck";
	parent_names[5] = "imgpll_divpmcck";
	parent_names[6] = "baudpll_divpmcck";
	parent_names[7] = "audiopll_divpmcck";
	parent_names[8] = "ethpll_divpmcck";
	for (i = 0; i < 8; i++) {
		char name[6];

		snprintf(name, sizeof(name), "prog%d", i);

		hw = at91_clk_register_programmable(regmap, name, parent_names,
						    9, i,
						    &programmable_layout,
						    sama7g5_prog_mux_table);
		if (IS_ERR(hw))
			goto err_free;

		sama7g5_pmc->pchws[i] = hw;
	}

	for (i = 0; i < ARRAY_SIZE(sama7g5_systemck); i++) {
		hw = at91_clk_register_system(regmap, sama7g5_systemck[i].n,
					      sama7g5_systemck[i].p,
					      sama7g5_systemck[i].id);
		if (IS_ERR(hw))
			goto err_free;

		sama7g5_pmc->shws[sama7g5_systemck[i].id] = hw;
	}

	for (i = 0; i < ARRAY_SIZE(sama7g5_periphck); i++) {
		hw = at91_clk_register_sam9x5_peripheral(regmap, &pmc_pcr_lock,
						&sama7g5_pcr_layout,
						sama7g5_periphck[i].n,
						sama7g5_periphck[i].p,
						sama7g5_periphck[i].id,
						&sama7g5_periphck[i].r,
						sama7g5_periphck[i].chgp ? 0 :
						INT_MIN);
		if (IS_ERR(hw))
			goto err_free;

		sama7g5_pmc->phws[sama7g5_periphck[i].id] = hw;
	}

	parent_names[0] = md_slck_name;
	parent_names[1] = td_slck_name;
	parent_names[2] = "mainck";
	for (i = 0; i < ARRAY_SIZE(sama7g5_gck); i++) {
		u8 num_parents = 3 + sama7g5_gck[i].pp_count;
		u32 *mux_table;

		mux_table = kmalloc_array(num_parents, sizeof(*mux_table),
					  GFP_KERNEL);
		if (!mux_table)
			goto err_free;

		SAMA7G5_INIT_TABLE(mux_table, 3);
		SAMA7G5_FILL_TABLE(&mux_table[3], sama7g5_gck[i].pp_mux_table,
				   sama7g5_gck[i].pp_count);
		SAMA7G5_FILL_TABLE(&parent_names[3], sama7g5_gck[i].pp,
				   sama7g5_gck[i].pp_count);

		hw = at91_clk_register_generated(regmap, &pmc_pcr_lock,
						 &sama7g5_pcr_layout,
						 sama7g5_gck[i].n,
						 parent_names, mux_table,
						 num_parents,
						 sama7g5_gck[i].id,
						 &sama7g5_gck[i].r,
						 sama7g5_gck[i].pp_chg_id);
		if (IS_ERR(hw))
			goto err_free;

		sama7g5_pmc->ghws[sama7g5_gck[i].id] = hw;
		alloc_mem[alloc_mem_size++] = mux_table;
	}

	of_clk_add_hw_provider(np, of_clk_hw_pmc_get, sama7g5_pmc);

	return;

err_free:
	if (alloc_mem) {
		for (i = 0; i < alloc_mem_size; i++)
			kfree(alloc_mem[i]);
		kfree(alloc_mem);
	}

	kfree(sama7g5_pmc);
}