18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * ARTPEC-6 clock initialization
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright 2015-2016 Axis Comunications AB.
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include <linux/clk-provider.h>
98c2ecf20Sopenharmony_ci#include <linux/device.h>
108c2ecf20Sopenharmony_ci#include <linux/io.h>
118c2ecf20Sopenharmony_ci#include <linux/of.h>
128c2ecf20Sopenharmony_ci#include <linux/of_address.h>
138c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
148c2ecf20Sopenharmony_ci#include <linux/slab.h>
158c2ecf20Sopenharmony_ci#include <dt-bindings/clock/axis,artpec6-clkctrl.h>
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci#define NUM_I2S_CLOCKS 2
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_cistruct artpec6_clkctrl_drvdata {
208c2ecf20Sopenharmony_ci	struct clk *clk_table[ARTPEC6_CLK_NUMCLOCKS];
218c2ecf20Sopenharmony_ci	void __iomem *syscon_base;
228c2ecf20Sopenharmony_ci	struct clk_onecell_data clk_data;
238c2ecf20Sopenharmony_ci	spinlock_t i2scfg_lock;
248c2ecf20Sopenharmony_ci};
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_cistatic struct artpec6_clkctrl_drvdata *clkdata;
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_cistatic const char *const i2s_clk_names[NUM_I2S_CLOCKS] = {
298c2ecf20Sopenharmony_ci	"i2s0",
308c2ecf20Sopenharmony_ci	"i2s1",
318c2ecf20Sopenharmony_ci};
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_cistatic const int i2s_clk_indexes[NUM_I2S_CLOCKS] = {
348c2ecf20Sopenharmony_ci	ARTPEC6_CLK_I2S0_CLK,
358c2ecf20Sopenharmony_ci	ARTPEC6_CLK_I2S1_CLK,
368c2ecf20Sopenharmony_ci};
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_cistatic void of_artpec6_clkctrl_setup(struct device_node *np)
398c2ecf20Sopenharmony_ci{
408c2ecf20Sopenharmony_ci	int i;
418c2ecf20Sopenharmony_ci	const char *sys_refclk_name;
428c2ecf20Sopenharmony_ci	u32 pll_mode, pll_m, pll_n;
438c2ecf20Sopenharmony_ci	struct clk **clks;
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci	/* Mandatory parent clock. */
468c2ecf20Sopenharmony_ci	i = of_property_match_string(np, "clock-names", "sys_refclk");
478c2ecf20Sopenharmony_ci	if (i < 0)
488c2ecf20Sopenharmony_ci		return;
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci	sys_refclk_name = of_clk_get_parent_name(np, i);
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci	clkdata = kzalloc(sizeof(*clkdata), GFP_KERNEL);
538c2ecf20Sopenharmony_ci	if (!clkdata)
548c2ecf20Sopenharmony_ci		return;
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci	clks = clkdata->clk_table;
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci	for (i = 0; i < ARTPEC6_CLK_NUMCLOCKS; ++i)
598c2ecf20Sopenharmony_ci		clks[i] = ERR_PTR(-EPROBE_DEFER);
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci	clkdata->syscon_base = of_iomap(np, 0);
628c2ecf20Sopenharmony_ci	BUG_ON(clkdata->syscon_base == NULL);
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci	/* Read PLL1 factors configured by boot strap pins. */
658c2ecf20Sopenharmony_ci	pll_mode = (readl(clkdata->syscon_base) >> 6) & 3;
668c2ecf20Sopenharmony_ci	switch (pll_mode) {
678c2ecf20Sopenharmony_ci	case 0:		/* DDR3-2133 mode */
688c2ecf20Sopenharmony_ci		pll_m = 4;
698c2ecf20Sopenharmony_ci		pll_n = 85;
708c2ecf20Sopenharmony_ci		break;
718c2ecf20Sopenharmony_ci	case 1:		/* DDR3-1866 mode */
728c2ecf20Sopenharmony_ci		pll_m = 6;
738c2ecf20Sopenharmony_ci		pll_n = 112;
748c2ecf20Sopenharmony_ci		break;
758c2ecf20Sopenharmony_ci	case 2:		/* DDR3-1600 mode */
768c2ecf20Sopenharmony_ci		pll_m = 4;
778c2ecf20Sopenharmony_ci		pll_n = 64;
788c2ecf20Sopenharmony_ci		break;
798c2ecf20Sopenharmony_ci	case 3:		/* DDR3-1333 mode */
808c2ecf20Sopenharmony_ci		pll_m = 8;
818c2ecf20Sopenharmony_ci		pll_n = 106;
828c2ecf20Sopenharmony_ci		break;
838c2ecf20Sopenharmony_ci	}
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci	clks[ARTPEC6_CLK_CPU] =
868c2ecf20Sopenharmony_ci	    clk_register_fixed_factor(NULL, "cpu", sys_refclk_name, 0, pll_n,
878c2ecf20Sopenharmony_ci				      pll_m);
888c2ecf20Sopenharmony_ci	clks[ARTPEC6_CLK_CPU_PERIPH] =
898c2ecf20Sopenharmony_ci	    clk_register_fixed_factor(NULL, "cpu_periph", "cpu", 0, 1, 2);
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci	/* EPROBE_DEFER on the apb_clock is not handled in amba devices. */
928c2ecf20Sopenharmony_ci	clks[ARTPEC6_CLK_UART_PCLK] =
938c2ecf20Sopenharmony_ci	    clk_register_fixed_factor(NULL, "uart_pclk", "cpu", 0, 1, 8);
948c2ecf20Sopenharmony_ci	clks[ARTPEC6_CLK_UART_REFCLK] =
958c2ecf20Sopenharmony_ci	    clk_register_fixed_rate(NULL, "uart_ref", sys_refclk_name, 0,
968c2ecf20Sopenharmony_ci				    50000000);
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci	clks[ARTPEC6_CLK_SPI_PCLK] =
998c2ecf20Sopenharmony_ci	    clk_register_fixed_factor(NULL, "spi_pclk", "cpu", 0, 1, 8);
1008c2ecf20Sopenharmony_ci	clks[ARTPEC6_CLK_SPI_SSPCLK] =
1018c2ecf20Sopenharmony_ci	    clk_register_fixed_rate(NULL, "spi_sspclk", sys_refclk_name, 0,
1028c2ecf20Sopenharmony_ci				    50000000);
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci	clks[ARTPEC6_CLK_DBG_PCLK] =
1058c2ecf20Sopenharmony_ci	    clk_register_fixed_factor(NULL, "dbg_pclk", "cpu", 0, 1, 8);
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	clkdata->clk_data.clks = clkdata->clk_table;
1088c2ecf20Sopenharmony_ci	clkdata->clk_data.clk_num = ARTPEC6_CLK_NUMCLOCKS;
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	of_clk_add_provider(np, of_clk_src_onecell_get, &clkdata->clk_data);
1118c2ecf20Sopenharmony_ci}
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ciCLK_OF_DECLARE_DRIVER(artpec6_clkctrl, "axis,artpec6-clkctrl",
1148c2ecf20Sopenharmony_ci		      of_artpec6_clkctrl_setup);
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_cistatic int artpec6_clkctrl_probe(struct platform_device *pdev)
1178c2ecf20Sopenharmony_ci{
1188c2ecf20Sopenharmony_ci	int propidx;
1198c2ecf20Sopenharmony_ci	struct device_node *np = pdev->dev.of_node;
1208c2ecf20Sopenharmony_ci	struct device *dev = &pdev->dev;
1218c2ecf20Sopenharmony_ci	struct clk **clks = clkdata->clk_table;
1228c2ecf20Sopenharmony_ci	const char *sys_refclk_name;
1238c2ecf20Sopenharmony_ci	const char *i2s_refclk_name = NULL;
1248c2ecf20Sopenharmony_ci	const char *frac_clk_name[2] = { NULL, NULL };
1258c2ecf20Sopenharmony_ci	const char *i2s_mux_parents[2];
1268c2ecf20Sopenharmony_ci	u32 muxreg;
1278c2ecf20Sopenharmony_ci	int i;
1288c2ecf20Sopenharmony_ci	int err = 0;
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci	/* Mandatory parent clock. */
1318c2ecf20Sopenharmony_ci	propidx = of_property_match_string(np, "clock-names", "sys_refclk");
1328c2ecf20Sopenharmony_ci	if (propidx < 0)
1338c2ecf20Sopenharmony_ci		return -EINVAL;
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci	sys_refclk_name = of_clk_get_parent_name(np, propidx);
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci	/* Find clock names of optional parent clocks. */
1388c2ecf20Sopenharmony_ci	propidx = of_property_match_string(np, "clock-names", "i2s_refclk");
1398c2ecf20Sopenharmony_ci	if (propidx >= 0)
1408c2ecf20Sopenharmony_ci		i2s_refclk_name = of_clk_get_parent_name(np, propidx);
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci	propidx = of_property_match_string(np, "clock-names", "frac_clk0");
1438c2ecf20Sopenharmony_ci	if (propidx >= 0)
1448c2ecf20Sopenharmony_ci		frac_clk_name[0] = of_clk_get_parent_name(np, propidx);
1458c2ecf20Sopenharmony_ci	propidx = of_property_match_string(np, "clock-names", "frac_clk1");
1468c2ecf20Sopenharmony_ci	if (propidx >= 0)
1478c2ecf20Sopenharmony_ci		frac_clk_name[1] = of_clk_get_parent_name(np, propidx);
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci	spin_lock_init(&clkdata->i2scfg_lock);
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci	clks[ARTPEC6_CLK_NAND_CLKA] =
1528c2ecf20Sopenharmony_ci	    clk_register_fixed_factor(dev, "nand_clka", "cpu", 0, 1, 8);
1538c2ecf20Sopenharmony_ci	clks[ARTPEC6_CLK_NAND_CLKB] =
1548c2ecf20Sopenharmony_ci	    clk_register_fixed_rate(dev, "nand_clkb", sys_refclk_name, 0,
1558c2ecf20Sopenharmony_ci				    100000000);
1568c2ecf20Sopenharmony_ci	clks[ARTPEC6_CLK_ETH_ACLK] =
1578c2ecf20Sopenharmony_ci	    clk_register_fixed_factor(dev, "eth_aclk", "cpu", 0, 1, 4);
1588c2ecf20Sopenharmony_ci	clks[ARTPEC6_CLK_DMA_ACLK] =
1598c2ecf20Sopenharmony_ci	    clk_register_fixed_factor(dev, "dma_aclk", "cpu", 0, 1, 4);
1608c2ecf20Sopenharmony_ci	clks[ARTPEC6_CLK_PTP_REF] =
1618c2ecf20Sopenharmony_ci	    clk_register_fixed_rate(dev, "ptp_ref", sys_refclk_name, 0,
1628c2ecf20Sopenharmony_ci				    100000000);
1638c2ecf20Sopenharmony_ci	clks[ARTPEC6_CLK_SD_PCLK] =
1648c2ecf20Sopenharmony_ci	    clk_register_fixed_rate(dev, "sd_pclk", sys_refclk_name, 0,
1658c2ecf20Sopenharmony_ci				    100000000);
1668c2ecf20Sopenharmony_ci	clks[ARTPEC6_CLK_SD_IMCLK] =
1678c2ecf20Sopenharmony_ci	    clk_register_fixed_rate(dev, "sd_imclk", sys_refclk_name, 0,
1688c2ecf20Sopenharmony_ci				    100000000);
1698c2ecf20Sopenharmony_ci	clks[ARTPEC6_CLK_I2S_HST] =
1708c2ecf20Sopenharmony_ci	    clk_register_fixed_factor(dev, "i2s_hst", "cpu", 0, 1, 8);
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci	for (i = 0; i < NUM_I2S_CLOCKS; ++i) {
1738c2ecf20Sopenharmony_ci		if (i2s_refclk_name && frac_clk_name[i]) {
1748c2ecf20Sopenharmony_ci			i2s_mux_parents[0] = frac_clk_name[i];
1758c2ecf20Sopenharmony_ci			i2s_mux_parents[1] = i2s_refclk_name;
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci			clks[i2s_clk_indexes[i]] =
1788c2ecf20Sopenharmony_ci			    clk_register_mux(dev, i2s_clk_names[i],
1798c2ecf20Sopenharmony_ci					     i2s_mux_parents, 2,
1808c2ecf20Sopenharmony_ci					     CLK_SET_RATE_NO_REPARENT |
1818c2ecf20Sopenharmony_ci					     CLK_SET_RATE_PARENT,
1828c2ecf20Sopenharmony_ci					     clkdata->syscon_base + 0x14, i, 1,
1838c2ecf20Sopenharmony_ci					     0, &clkdata->i2scfg_lock);
1848c2ecf20Sopenharmony_ci		} else if (frac_clk_name[i]) {
1858c2ecf20Sopenharmony_ci			/* Lock the mux for internal clock reference. */
1868c2ecf20Sopenharmony_ci			muxreg = readl(clkdata->syscon_base + 0x14);
1878c2ecf20Sopenharmony_ci			muxreg &= ~BIT(i);
1888c2ecf20Sopenharmony_ci			writel(muxreg, clkdata->syscon_base + 0x14);
1898c2ecf20Sopenharmony_ci			clks[i2s_clk_indexes[i]] =
1908c2ecf20Sopenharmony_ci			    clk_register_fixed_factor(dev, i2s_clk_names[i],
1918c2ecf20Sopenharmony_ci						      frac_clk_name[i], 0, 1,
1928c2ecf20Sopenharmony_ci						      1);
1938c2ecf20Sopenharmony_ci		} else if (i2s_refclk_name) {
1948c2ecf20Sopenharmony_ci			/* Lock the mux for external clock reference. */
1958c2ecf20Sopenharmony_ci			muxreg = readl(clkdata->syscon_base + 0x14);
1968c2ecf20Sopenharmony_ci			muxreg |= BIT(i);
1978c2ecf20Sopenharmony_ci			writel(muxreg, clkdata->syscon_base + 0x14);
1988c2ecf20Sopenharmony_ci			clks[i2s_clk_indexes[i]] =
1998c2ecf20Sopenharmony_ci			    clk_register_fixed_factor(dev, i2s_clk_names[i],
2008c2ecf20Sopenharmony_ci						      i2s_refclk_name, 0, 1, 1);
2018c2ecf20Sopenharmony_ci		}
2028c2ecf20Sopenharmony_ci	}
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci	clks[ARTPEC6_CLK_I2C] =
2058c2ecf20Sopenharmony_ci	    clk_register_fixed_rate(dev, "i2c", sys_refclk_name, 0, 100000000);
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci	clks[ARTPEC6_CLK_SYS_TIMER] =
2088c2ecf20Sopenharmony_ci	    clk_register_fixed_rate(dev, "timer", sys_refclk_name, 0,
2098c2ecf20Sopenharmony_ci				    100000000);
2108c2ecf20Sopenharmony_ci	clks[ARTPEC6_CLK_FRACDIV_IN] =
2118c2ecf20Sopenharmony_ci	    clk_register_fixed_rate(dev, "fracdiv_in", sys_refclk_name, 0,
2128c2ecf20Sopenharmony_ci				    600000000);
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci	for (i = 0; i < ARTPEC6_CLK_NUMCLOCKS; ++i) {
2158c2ecf20Sopenharmony_ci		if (IS_ERR(clks[i]) && PTR_ERR(clks[i]) != -EPROBE_DEFER) {
2168c2ecf20Sopenharmony_ci			dev_err(dev,
2178c2ecf20Sopenharmony_ci				"Failed to register clock at index %d err=%ld\n",
2188c2ecf20Sopenharmony_ci				i, PTR_ERR(clks[i]));
2198c2ecf20Sopenharmony_ci			err = PTR_ERR(clks[i]);
2208c2ecf20Sopenharmony_ci		}
2218c2ecf20Sopenharmony_ci	}
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci	return err;
2248c2ecf20Sopenharmony_ci}
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_cistatic const struct of_device_id artpec_clkctrl_of_match[] = {
2278c2ecf20Sopenharmony_ci	{ .compatible = "axis,artpec6-clkctrl" },
2288c2ecf20Sopenharmony_ci	{}
2298c2ecf20Sopenharmony_ci};
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_cistatic struct platform_driver artpec6_clkctrl_driver = {
2328c2ecf20Sopenharmony_ci	.probe = artpec6_clkctrl_probe,
2338c2ecf20Sopenharmony_ci	.driver = {
2348c2ecf20Sopenharmony_ci		   .name = "artpec6_clkctrl",
2358c2ecf20Sopenharmony_ci		   .of_match_table = artpec_clkctrl_of_match,
2368c2ecf20Sopenharmony_ci	},
2378c2ecf20Sopenharmony_ci};
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_cibuiltin_platform_driver(artpec6_clkctrl_driver);
240