162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * ARTPEC-6 clock initialization
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright 2015-2016 Axis Communications AB.
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/clk-provider.h>
962306a36Sopenharmony_ci#include <linux/device.h>
1062306a36Sopenharmony_ci#include <linux/io.h>
1162306a36Sopenharmony_ci#include <linux/of.h>
1262306a36Sopenharmony_ci#include <linux/of_address.h>
1362306a36Sopenharmony_ci#include <linux/platform_device.h>
1462306a36Sopenharmony_ci#include <linux/slab.h>
1562306a36Sopenharmony_ci#include <dt-bindings/clock/axis,artpec6-clkctrl.h>
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci#define NUM_I2S_CLOCKS 2
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_cistruct artpec6_clkctrl_drvdata {
2062306a36Sopenharmony_ci	struct clk *clk_table[ARTPEC6_CLK_NUMCLOCKS];
2162306a36Sopenharmony_ci	void __iomem *syscon_base;
2262306a36Sopenharmony_ci	struct clk_onecell_data clk_data;
2362306a36Sopenharmony_ci	spinlock_t i2scfg_lock;
2462306a36Sopenharmony_ci};
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_cistatic struct artpec6_clkctrl_drvdata *clkdata;
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_cistatic const char *const i2s_clk_names[NUM_I2S_CLOCKS] = {
2962306a36Sopenharmony_ci	"i2s0",
3062306a36Sopenharmony_ci	"i2s1",
3162306a36Sopenharmony_ci};
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_cistatic const int i2s_clk_indexes[NUM_I2S_CLOCKS] = {
3462306a36Sopenharmony_ci	ARTPEC6_CLK_I2S0_CLK,
3562306a36Sopenharmony_ci	ARTPEC6_CLK_I2S1_CLK,
3662306a36Sopenharmony_ci};
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_cistatic void of_artpec6_clkctrl_setup(struct device_node *np)
3962306a36Sopenharmony_ci{
4062306a36Sopenharmony_ci	int i;
4162306a36Sopenharmony_ci	const char *sys_refclk_name;
4262306a36Sopenharmony_ci	u32 pll_mode, pll_m, pll_n;
4362306a36Sopenharmony_ci	struct clk **clks;
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci	/* Mandatory parent clock. */
4662306a36Sopenharmony_ci	i = of_property_match_string(np, "clock-names", "sys_refclk");
4762306a36Sopenharmony_ci	if (i < 0)
4862306a36Sopenharmony_ci		return;
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci	sys_refclk_name = of_clk_get_parent_name(np, i);
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci	clkdata = kzalloc(sizeof(*clkdata), GFP_KERNEL);
5362306a36Sopenharmony_ci	if (!clkdata)
5462306a36Sopenharmony_ci		return;
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	clks = clkdata->clk_table;
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci	for (i = 0; i < ARTPEC6_CLK_NUMCLOCKS; ++i)
5962306a36Sopenharmony_ci		clks[i] = ERR_PTR(-EPROBE_DEFER);
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci	clkdata->syscon_base = of_iomap(np, 0);
6262306a36Sopenharmony_ci	BUG_ON(clkdata->syscon_base == NULL);
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci	/* Read PLL1 factors configured by boot strap pins. */
6562306a36Sopenharmony_ci	pll_mode = (readl(clkdata->syscon_base) >> 6) & 3;
6662306a36Sopenharmony_ci	switch (pll_mode) {
6762306a36Sopenharmony_ci	case 0:		/* DDR3-2133 mode */
6862306a36Sopenharmony_ci		pll_m = 4;
6962306a36Sopenharmony_ci		pll_n = 85;
7062306a36Sopenharmony_ci		break;
7162306a36Sopenharmony_ci	case 1:		/* DDR3-1866 mode */
7262306a36Sopenharmony_ci		pll_m = 6;
7362306a36Sopenharmony_ci		pll_n = 112;
7462306a36Sopenharmony_ci		break;
7562306a36Sopenharmony_ci	case 2:		/* DDR3-1600 mode */
7662306a36Sopenharmony_ci		pll_m = 4;
7762306a36Sopenharmony_ci		pll_n = 64;
7862306a36Sopenharmony_ci		break;
7962306a36Sopenharmony_ci	case 3:		/* DDR3-1333 mode */
8062306a36Sopenharmony_ci		pll_m = 8;
8162306a36Sopenharmony_ci		pll_n = 106;
8262306a36Sopenharmony_ci		break;
8362306a36Sopenharmony_ci	}
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	clks[ARTPEC6_CLK_CPU] =
8662306a36Sopenharmony_ci	    clk_register_fixed_factor(NULL, "cpu", sys_refclk_name, 0, pll_n,
8762306a36Sopenharmony_ci				      pll_m);
8862306a36Sopenharmony_ci	clks[ARTPEC6_CLK_CPU_PERIPH] =
8962306a36Sopenharmony_ci	    clk_register_fixed_factor(NULL, "cpu_periph", "cpu", 0, 1, 2);
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	/* EPROBE_DEFER on the apb_clock is not handled in amba devices. */
9262306a36Sopenharmony_ci	clks[ARTPEC6_CLK_UART_PCLK] =
9362306a36Sopenharmony_ci	    clk_register_fixed_factor(NULL, "uart_pclk", "cpu", 0, 1, 8);
9462306a36Sopenharmony_ci	clks[ARTPEC6_CLK_UART_REFCLK] =
9562306a36Sopenharmony_ci	    clk_register_fixed_rate(NULL, "uart_ref", sys_refclk_name, 0,
9662306a36Sopenharmony_ci				    50000000);
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	clks[ARTPEC6_CLK_SPI_PCLK] =
9962306a36Sopenharmony_ci	    clk_register_fixed_factor(NULL, "spi_pclk", "cpu", 0, 1, 8);
10062306a36Sopenharmony_ci	clks[ARTPEC6_CLK_SPI_SSPCLK] =
10162306a36Sopenharmony_ci	    clk_register_fixed_rate(NULL, "spi_sspclk", sys_refclk_name, 0,
10262306a36Sopenharmony_ci				    50000000);
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	clks[ARTPEC6_CLK_DBG_PCLK] =
10562306a36Sopenharmony_ci	    clk_register_fixed_factor(NULL, "dbg_pclk", "cpu", 0, 1, 8);
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	clkdata->clk_data.clks = clkdata->clk_table;
10862306a36Sopenharmony_ci	clkdata->clk_data.clk_num = ARTPEC6_CLK_NUMCLOCKS;
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	of_clk_add_provider(np, of_clk_src_onecell_get, &clkdata->clk_data);
11162306a36Sopenharmony_ci}
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ciCLK_OF_DECLARE_DRIVER(artpec6_clkctrl, "axis,artpec6-clkctrl",
11462306a36Sopenharmony_ci		      of_artpec6_clkctrl_setup);
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_cistatic int artpec6_clkctrl_probe(struct platform_device *pdev)
11762306a36Sopenharmony_ci{
11862306a36Sopenharmony_ci	int propidx;
11962306a36Sopenharmony_ci	struct device_node *np = pdev->dev.of_node;
12062306a36Sopenharmony_ci	struct device *dev = &pdev->dev;
12162306a36Sopenharmony_ci	struct clk **clks = clkdata->clk_table;
12262306a36Sopenharmony_ci	const char *sys_refclk_name;
12362306a36Sopenharmony_ci	const char *i2s_refclk_name = NULL;
12462306a36Sopenharmony_ci	const char *frac_clk_name[2] = { NULL, NULL };
12562306a36Sopenharmony_ci	const char *i2s_mux_parents[2];
12662306a36Sopenharmony_ci	u32 muxreg;
12762306a36Sopenharmony_ci	int i;
12862306a36Sopenharmony_ci	int err = 0;
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci	/* Mandatory parent clock. */
13162306a36Sopenharmony_ci	propidx = of_property_match_string(np, "clock-names", "sys_refclk");
13262306a36Sopenharmony_ci	if (propidx < 0)
13362306a36Sopenharmony_ci		return -EINVAL;
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	sys_refclk_name = of_clk_get_parent_name(np, propidx);
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	/* Find clock names of optional parent clocks. */
13862306a36Sopenharmony_ci	propidx = of_property_match_string(np, "clock-names", "i2s_refclk");
13962306a36Sopenharmony_ci	if (propidx >= 0)
14062306a36Sopenharmony_ci		i2s_refclk_name = of_clk_get_parent_name(np, propidx);
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	propidx = of_property_match_string(np, "clock-names", "frac_clk0");
14362306a36Sopenharmony_ci	if (propidx >= 0)
14462306a36Sopenharmony_ci		frac_clk_name[0] = of_clk_get_parent_name(np, propidx);
14562306a36Sopenharmony_ci	propidx = of_property_match_string(np, "clock-names", "frac_clk1");
14662306a36Sopenharmony_ci	if (propidx >= 0)
14762306a36Sopenharmony_ci		frac_clk_name[1] = of_clk_get_parent_name(np, propidx);
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci	spin_lock_init(&clkdata->i2scfg_lock);
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	clks[ARTPEC6_CLK_NAND_CLKA] =
15262306a36Sopenharmony_ci	    clk_register_fixed_factor(dev, "nand_clka", "cpu", 0, 1, 8);
15362306a36Sopenharmony_ci	clks[ARTPEC6_CLK_NAND_CLKB] =
15462306a36Sopenharmony_ci	    clk_register_fixed_rate(dev, "nand_clkb", sys_refclk_name, 0,
15562306a36Sopenharmony_ci				    100000000);
15662306a36Sopenharmony_ci	clks[ARTPEC6_CLK_ETH_ACLK] =
15762306a36Sopenharmony_ci	    clk_register_fixed_factor(dev, "eth_aclk", "cpu", 0, 1, 4);
15862306a36Sopenharmony_ci	clks[ARTPEC6_CLK_DMA_ACLK] =
15962306a36Sopenharmony_ci	    clk_register_fixed_factor(dev, "dma_aclk", "cpu", 0, 1, 4);
16062306a36Sopenharmony_ci	clks[ARTPEC6_CLK_PTP_REF] =
16162306a36Sopenharmony_ci	    clk_register_fixed_rate(dev, "ptp_ref", sys_refclk_name, 0,
16262306a36Sopenharmony_ci				    100000000);
16362306a36Sopenharmony_ci	clks[ARTPEC6_CLK_SD_PCLK] =
16462306a36Sopenharmony_ci	    clk_register_fixed_rate(dev, "sd_pclk", sys_refclk_name, 0,
16562306a36Sopenharmony_ci				    100000000);
16662306a36Sopenharmony_ci	clks[ARTPEC6_CLK_SD_IMCLK] =
16762306a36Sopenharmony_ci	    clk_register_fixed_rate(dev, "sd_imclk", sys_refclk_name, 0,
16862306a36Sopenharmony_ci				    100000000);
16962306a36Sopenharmony_ci	clks[ARTPEC6_CLK_I2S_HST] =
17062306a36Sopenharmony_ci	    clk_register_fixed_factor(dev, "i2s_hst", "cpu", 0, 1, 8);
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	for (i = 0; i < NUM_I2S_CLOCKS; ++i) {
17362306a36Sopenharmony_ci		if (i2s_refclk_name && frac_clk_name[i]) {
17462306a36Sopenharmony_ci			i2s_mux_parents[0] = frac_clk_name[i];
17562306a36Sopenharmony_ci			i2s_mux_parents[1] = i2s_refclk_name;
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci			clks[i2s_clk_indexes[i]] =
17862306a36Sopenharmony_ci			    clk_register_mux(dev, i2s_clk_names[i],
17962306a36Sopenharmony_ci					     i2s_mux_parents, 2,
18062306a36Sopenharmony_ci					     CLK_SET_RATE_NO_REPARENT |
18162306a36Sopenharmony_ci					     CLK_SET_RATE_PARENT,
18262306a36Sopenharmony_ci					     clkdata->syscon_base + 0x14, i, 1,
18362306a36Sopenharmony_ci					     0, &clkdata->i2scfg_lock);
18462306a36Sopenharmony_ci		} else if (frac_clk_name[i]) {
18562306a36Sopenharmony_ci			/* Lock the mux for internal clock reference. */
18662306a36Sopenharmony_ci			muxreg = readl(clkdata->syscon_base + 0x14);
18762306a36Sopenharmony_ci			muxreg &= ~BIT(i);
18862306a36Sopenharmony_ci			writel(muxreg, clkdata->syscon_base + 0x14);
18962306a36Sopenharmony_ci			clks[i2s_clk_indexes[i]] =
19062306a36Sopenharmony_ci			    clk_register_fixed_factor(dev, i2s_clk_names[i],
19162306a36Sopenharmony_ci						      frac_clk_name[i], 0, 1,
19262306a36Sopenharmony_ci						      1);
19362306a36Sopenharmony_ci		} else if (i2s_refclk_name) {
19462306a36Sopenharmony_ci			/* Lock the mux for external clock reference. */
19562306a36Sopenharmony_ci			muxreg = readl(clkdata->syscon_base + 0x14);
19662306a36Sopenharmony_ci			muxreg |= BIT(i);
19762306a36Sopenharmony_ci			writel(muxreg, clkdata->syscon_base + 0x14);
19862306a36Sopenharmony_ci			clks[i2s_clk_indexes[i]] =
19962306a36Sopenharmony_ci			    clk_register_fixed_factor(dev, i2s_clk_names[i],
20062306a36Sopenharmony_ci						      i2s_refclk_name, 0, 1, 1);
20162306a36Sopenharmony_ci		}
20262306a36Sopenharmony_ci	}
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci	clks[ARTPEC6_CLK_I2C] =
20562306a36Sopenharmony_ci	    clk_register_fixed_rate(dev, "i2c", sys_refclk_name, 0, 100000000);
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci	clks[ARTPEC6_CLK_SYS_TIMER] =
20862306a36Sopenharmony_ci	    clk_register_fixed_rate(dev, "timer", sys_refclk_name, 0,
20962306a36Sopenharmony_ci				    100000000);
21062306a36Sopenharmony_ci	clks[ARTPEC6_CLK_FRACDIV_IN] =
21162306a36Sopenharmony_ci	    clk_register_fixed_rate(dev, "fracdiv_in", sys_refclk_name, 0,
21262306a36Sopenharmony_ci				    600000000);
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	for (i = 0; i < ARTPEC6_CLK_NUMCLOCKS; ++i) {
21562306a36Sopenharmony_ci		if (IS_ERR(clks[i]) && PTR_ERR(clks[i]) != -EPROBE_DEFER) {
21662306a36Sopenharmony_ci			dev_err(dev,
21762306a36Sopenharmony_ci				"Failed to register clock at index %d err=%ld\n",
21862306a36Sopenharmony_ci				i, PTR_ERR(clks[i]));
21962306a36Sopenharmony_ci			err = PTR_ERR(clks[i]);
22062306a36Sopenharmony_ci		}
22162306a36Sopenharmony_ci	}
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci	return err;
22462306a36Sopenharmony_ci}
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_cistatic const struct of_device_id artpec_clkctrl_of_match[] = {
22762306a36Sopenharmony_ci	{ .compatible = "axis,artpec6-clkctrl" },
22862306a36Sopenharmony_ci	{}
22962306a36Sopenharmony_ci};
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_cistatic struct platform_driver artpec6_clkctrl_driver = {
23262306a36Sopenharmony_ci	.probe = artpec6_clkctrl_probe,
23362306a36Sopenharmony_ci	.driver = {
23462306a36Sopenharmony_ci		   .name = "artpec6_clkctrl",
23562306a36Sopenharmony_ci		   .of_match_table = artpec_clkctrl_of_match,
23662306a36Sopenharmony_ci	},
23762306a36Sopenharmony_ci};
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_cibuiltin_platform_driver(artpec6_clkctrl_driver);
240