162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * MOXA ART SoCs clock driver.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2013 Jonas Jensen
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Jonas Jensen <jonas.jensen@gmail.com>
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/clk.h>
1162306a36Sopenharmony_ci#include <linux/clk-provider.h>
1262306a36Sopenharmony_ci#include <linux/io.h>
1362306a36Sopenharmony_ci#include <linux/of_address.h>
1462306a36Sopenharmony_ci#include <linux/clkdev.h>
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_cistatic void __init moxart_of_pll_clk_init(struct device_node *node)
1762306a36Sopenharmony_ci{
1862306a36Sopenharmony_ci	void __iomem *base;
1962306a36Sopenharmony_ci	struct clk_hw *hw;
2062306a36Sopenharmony_ci	struct clk *ref_clk;
2162306a36Sopenharmony_ci	unsigned int mul;
2262306a36Sopenharmony_ci	const char *name = node->name;
2362306a36Sopenharmony_ci	const char *parent_name;
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci	of_property_read_string(node, "clock-output-names", &name);
2662306a36Sopenharmony_ci	parent_name = of_clk_get_parent_name(node, 0);
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci	base = of_iomap(node, 0);
2962306a36Sopenharmony_ci	if (!base) {
3062306a36Sopenharmony_ci		pr_err("%pOF: of_iomap failed\n", node);
3162306a36Sopenharmony_ci		return;
3262306a36Sopenharmony_ci	}
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci	mul = readl(base + 0x30) >> 3 & 0x3f;
3562306a36Sopenharmony_ci	iounmap(base);
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci	ref_clk = of_clk_get(node, 0);
3862306a36Sopenharmony_ci	if (IS_ERR(ref_clk)) {
3962306a36Sopenharmony_ci		pr_err("%pOF: of_clk_get failed\n", node);
4062306a36Sopenharmony_ci		return;
4162306a36Sopenharmony_ci	}
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci	hw = clk_hw_register_fixed_factor(NULL, name, parent_name, 0, mul, 1);
4462306a36Sopenharmony_ci	if (IS_ERR(hw)) {
4562306a36Sopenharmony_ci		pr_err("%pOF: failed to register clock\n", node);
4662306a36Sopenharmony_ci		return;
4762306a36Sopenharmony_ci	}
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci	clk_hw_register_clkdev(hw, NULL, name);
5062306a36Sopenharmony_ci	of_clk_add_hw_provider(node, of_clk_hw_simple_get, hw);
5162306a36Sopenharmony_ci}
5262306a36Sopenharmony_ciCLK_OF_DECLARE(moxart_pll_clock, "moxa,moxart-pll-clock",
5362306a36Sopenharmony_ci	       moxart_of_pll_clk_init);
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_cistatic void __init moxart_of_apb_clk_init(struct device_node *node)
5662306a36Sopenharmony_ci{
5762306a36Sopenharmony_ci	void __iomem *base;
5862306a36Sopenharmony_ci	struct clk_hw *hw;
5962306a36Sopenharmony_ci	struct clk *pll_clk;
6062306a36Sopenharmony_ci	unsigned int div, val;
6162306a36Sopenharmony_ci	unsigned int div_idx[] = { 2, 3, 4, 6, 8};
6262306a36Sopenharmony_ci	const char *name = node->name;
6362306a36Sopenharmony_ci	const char *parent_name;
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci	of_property_read_string(node, "clock-output-names", &name);
6662306a36Sopenharmony_ci	parent_name = of_clk_get_parent_name(node, 0);
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci	base = of_iomap(node, 0);
6962306a36Sopenharmony_ci	if (!base) {
7062306a36Sopenharmony_ci		pr_err("%pOF: of_iomap failed\n", node);
7162306a36Sopenharmony_ci		return;
7262306a36Sopenharmony_ci	}
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci	val = readl(base + 0xc) >> 4 & 0x7;
7562306a36Sopenharmony_ci	iounmap(base);
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	if (val > 4)
7862306a36Sopenharmony_ci		val = 0;
7962306a36Sopenharmony_ci	div = div_idx[val] * 2;
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	pll_clk = of_clk_get(node, 0);
8262306a36Sopenharmony_ci	if (IS_ERR(pll_clk)) {
8362306a36Sopenharmony_ci		pr_err("%pOF: of_clk_get failed\n", node);
8462306a36Sopenharmony_ci		return;
8562306a36Sopenharmony_ci	}
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	hw = clk_hw_register_fixed_factor(NULL, name, parent_name, 0, 1, div);
8862306a36Sopenharmony_ci	if (IS_ERR(hw)) {
8962306a36Sopenharmony_ci		pr_err("%pOF: failed to register clock\n", node);
9062306a36Sopenharmony_ci		return;
9162306a36Sopenharmony_ci	}
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	clk_hw_register_clkdev(hw, NULL, name);
9462306a36Sopenharmony_ci	of_clk_add_hw_provider(node, of_clk_hw_simple_get, hw);
9562306a36Sopenharmony_ci}
9662306a36Sopenharmony_ciCLK_OF_DECLARE(moxart_apb_clock, "moxa,moxart-apb-clock",
9762306a36Sopenharmony_ci	       moxart_of_apb_clk_init);
98