162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Nomadik clock implementation
462306a36Sopenharmony_ci * Copyright (C) 2013 ST-Ericsson AB
562306a36Sopenharmony_ci * Author: Linus Walleij <linus.walleij@linaro.org>
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#define pr_fmt(fmt) "Nomadik SRC clocks: " fmt
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/bitops.h>
1162306a36Sopenharmony_ci#include <linux/slab.h>
1262306a36Sopenharmony_ci#include <linux/err.h>
1362306a36Sopenharmony_ci#include <linux/io.h>
1462306a36Sopenharmony_ci#include <linux/clk-provider.h>
1562306a36Sopenharmony_ci#include <linux/of.h>
1662306a36Sopenharmony_ci#include <linux/of_address.h>
1762306a36Sopenharmony_ci#include <linux/debugfs.h>
1862306a36Sopenharmony_ci#include <linux/seq_file.h>
1962306a36Sopenharmony_ci#include <linux/spinlock.h>
2062306a36Sopenharmony_ci#include <linux/reboot.h>
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci/*
2362306a36Sopenharmony_ci * The Nomadik clock tree is described in the STN8815A12 DB V4.2
2462306a36Sopenharmony_ci * reference manual for the chip, page 94 ff.
2562306a36Sopenharmony_ci * Clock IDs are in the STn8815 Reference Manual table 3, page 27.
2662306a36Sopenharmony_ci */
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci#define SRC_CR			0x00U
2962306a36Sopenharmony_ci#define SRC_CR_T0_ENSEL		BIT(15)
3062306a36Sopenharmony_ci#define SRC_CR_T1_ENSEL		BIT(17)
3162306a36Sopenharmony_ci#define SRC_CR_T2_ENSEL		BIT(19)
3262306a36Sopenharmony_ci#define SRC_CR_T3_ENSEL		BIT(21)
3362306a36Sopenharmony_ci#define SRC_CR_T4_ENSEL		BIT(23)
3462306a36Sopenharmony_ci#define SRC_CR_T5_ENSEL		BIT(25)
3562306a36Sopenharmony_ci#define SRC_CR_T6_ENSEL		BIT(27)
3662306a36Sopenharmony_ci#define SRC_CR_T7_ENSEL		BIT(29)
3762306a36Sopenharmony_ci#define SRC_XTALCR		0x0CU
3862306a36Sopenharmony_ci#define SRC_XTALCR_XTALTIMEN	BIT(20)
3962306a36Sopenharmony_ci#define SRC_XTALCR_SXTALDIS	BIT(19)
4062306a36Sopenharmony_ci#define SRC_XTALCR_MXTALSTAT	BIT(2)
4162306a36Sopenharmony_ci#define SRC_XTALCR_MXTALEN	BIT(1)
4262306a36Sopenharmony_ci#define SRC_XTALCR_MXTALOVER	BIT(0)
4362306a36Sopenharmony_ci#define SRC_PLLCR		0x10U
4462306a36Sopenharmony_ci#define SRC_PLLCR_PLLTIMEN	BIT(29)
4562306a36Sopenharmony_ci#define SRC_PLLCR_PLL2EN	BIT(28)
4662306a36Sopenharmony_ci#define SRC_PLLCR_PLL1STAT	BIT(2)
4762306a36Sopenharmony_ci#define SRC_PLLCR_PLL1EN	BIT(1)
4862306a36Sopenharmony_ci#define SRC_PLLCR_PLL1OVER	BIT(0)
4962306a36Sopenharmony_ci#define SRC_PLLFR		0x14U
5062306a36Sopenharmony_ci#define SRC_PCKEN0		0x24U
5162306a36Sopenharmony_ci#define SRC_PCKDIS0		0x28U
5262306a36Sopenharmony_ci#define SRC_PCKENSR0		0x2CU
5362306a36Sopenharmony_ci#define SRC_PCKSR0		0x30U
5462306a36Sopenharmony_ci#define SRC_PCKEN1		0x34U
5562306a36Sopenharmony_ci#define SRC_PCKDIS1		0x38U
5662306a36Sopenharmony_ci#define SRC_PCKENSR1		0x3CU
5762306a36Sopenharmony_ci#define SRC_PCKSR1		0x40U
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci/* Lock protecting the SRC_CR register */
6062306a36Sopenharmony_cistatic DEFINE_SPINLOCK(src_lock);
6162306a36Sopenharmony_ci/* Base address of the SRC */
6262306a36Sopenharmony_cistatic void __iomem *src_base;
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_cistatic int nomadik_clk_reboot_handler(struct notifier_block *this,
6562306a36Sopenharmony_ci				unsigned long code,
6662306a36Sopenharmony_ci				void *unused)
6762306a36Sopenharmony_ci{
6862306a36Sopenharmony_ci	u32 val;
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	/* The main chrystal need to be enabled for reboot to work */
7162306a36Sopenharmony_ci	val = readl(src_base + SRC_XTALCR);
7262306a36Sopenharmony_ci	val &= ~SRC_XTALCR_MXTALOVER;
7362306a36Sopenharmony_ci	val |= SRC_XTALCR_MXTALEN;
7462306a36Sopenharmony_ci	pr_crit("force-enabling MXTALO\n");
7562306a36Sopenharmony_ci	writel(val, src_base + SRC_XTALCR);
7662306a36Sopenharmony_ci	return NOTIFY_OK;
7762306a36Sopenharmony_ci}
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_cistatic struct notifier_block nomadik_clk_reboot_notifier = {
8062306a36Sopenharmony_ci	.notifier_call = nomadik_clk_reboot_handler,
8162306a36Sopenharmony_ci};
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_cistatic const struct of_device_id nomadik_src_match[] __initconst = {
8462306a36Sopenharmony_ci	{ .compatible = "stericsson,nomadik-src" },
8562306a36Sopenharmony_ci	{ /* sentinel */ }
8662306a36Sopenharmony_ci};
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_cistatic void __init nomadik_src_init(void)
8962306a36Sopenharmony_ci{
9062306a36Sopenharmony_ci	struct device_node *np;
9162306a36Sopenharmony_ci	u32 val;
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	np = of_find_matching_node(NULL, nomadik_src_match);
9462306a36Sopenharmony_ci	if (!np) {
9562306a36Sopenharmony_ci		pr_crit("no matching node for SRC, aborting clock init\n");
9662306a36Sopenharmony_ci		return;
9762306a36Sopenharmony_ci	}
9862306a36Sopenharmony_ci	src_base = of_iomap(np, 0);
9962306a36Sopenharmony_ci	if (!src_base) {
10062306a36Sopenharmony_ci		pr_err("%s: must have src parent node with REGS (%pOFn)\n",
10162306a36Sopenharmony_ci		       __func__, np);
10262306a36Sopenharmony_ci		goto out_put;
10362306a36Sopenharmony_ci	}
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci	/* Set all timers to use the 2.4 MHz TIMCLK */
10662306a36Sopenharmony_ci	val = readl(src_base + SRC_CR);
10762306a36Sopenharmony_ci	val |= SRC_CR_T0_ENSEL;
10862306a36Sopenharmony_ci	val |= SRC_CR_T1_ENSEL;
10962306a36Sopenharmony_ci	val |= SRC_CR_T2_ENSEL;
11062306a36Sopenharmony_ci	val |= SRC_CR_T3_ENSEL;
11162306a36Sopenharmony_ci	val |= SRC_CR_T4_ENSEL;
11262306a36Sopenharmony_ci	val |= SRC_CR_T5_ENSEL;
11362306a36Sopenharmony_ci	val |= SRC_CR_T6_ENSEL;
11462306a36Sopenharmony_ci	val |= SRC_CR_T7_ENSEL;
11562306a36Sopenharmony_ci	writel(val, src_base + SRC_CR);
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	val = readl(src_base + SRC_XTALCR);
11862306a36Sopenharmony_ci	pr_info("SXTALO is %s\n",
11962306a36Sopenharmony_ci		(val & SRC_XTALCR_SXTALDIS) ? "disabled" : "enabled");
12062306a36Sopenharmony_ci	pr_info("MXTAL is %s\n",
12162306a36Sopenharmony_ci		(val & SRC_XTALCR_MXTALSTAT) ? "enabled" : "disabled");
12262306a36Sopenharmony_ci	if (of_property_read_bool(np, "disable-sxtalo")) {
12362306a36Sopenharmony_ci		/* The machine uses an external oscillator circuit */
12462306a36Sopenharmony_ci		val |= SRC_XTALCR_SXTALDIS;
12562306a36Sopenharmony_ci		pr_info("disabling SXTALO\n");
12662306a36Sopenharmony_ci	}
12762306a36Sopenharmony_ci	if (of_property_read_bool(np, "disable-mxtalo")) {
12862306a36Sopenharmony_ci		/* Disable this too: also run by external oscillator */
12962306a36Sopenharmony_ci		val |= SRC_XTALCR_MXTALOVER;
13062306a36Sopenharmony_ci		val &= ~SRC_XTALCR_MXTALEN;
13162306a36Sopenharmony_ci		pr_info("disabling MXTALO\n");
13262306a36Sopenharmony_ci	}
13362306a36Sopenharmony_ci	writel(val, src_base + SRC_XTALCR);
13462306a36Sopenharmony_ci	register_reboot_notifier(&nomadik_clk_reboot_notifier);
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ciout_put:
13762306a36Sopenharmony_ci	of_node_put(np);
13862306a36Sopenharmony_ci}
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci/**
14162306a36Sopenharmony_ci * struct clk_pll - Nomadik PLL clock
14262306a36Sopenharmony_ci * @hw: corresponding clock hardware entry
14362306a36Sopenharmony_ci * @id: PLL instance: 1 or 2
14462306a36Sopenharmony_ci */
14562306a36Sopenharmony_cistruct clk_pll {
14662306a36Sopenharmony_ci	struct clk_hw hw;
14762306a36Sopenharmony_ci	int id;
14862306a36Sopenharmony_ci};
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci/**
15162306a36Sopenharmony_ci * struct clk_src - Nomadik src clock
15262306a36Sopenharmony_ci * @hw: corresponding clock hardware entry
15362306a36Sopenharmony_ci * @id: the clock ID
15462306a36Sopenharmony_ci * @group1: true if the clock is in group1, else it is in group0
15562306a36Sopenharmony_ci * @clkbit: bit 0...31 corresponding to the clock in each clock register
15662306a36Sopenharmony_ci */
15762306a36Sopenharmony_cistruct clk_src {
15862306a36Sopenharmony_ci	struct clk_hw hw;
15962306a36Sopenharmony_ci	int id;
16062306a36Sopenharmony_ci	bool group1;
16162306a36Sopenharmony_ci	u32 clkbit;
16262306a36Sopenharmony_ci};
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci#define to_pll(_hw) container_of(_hw, struct clk_pll, hw)
16562306a36Sopenharmony_ci#define to_src(_hw) container_of(_hw, struct clk_src, hw)
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_cistatic int pll_clk_enable(struct clk_hw *hw)
16862306a36Sopenharmony_ci{
16962306a36Sopenharmony_ci	struct clk_pll *pll = to_pll(hw);
17062306a36Sopenharmony_ci	u32 val;
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	spin_lock(&src_lock);
17362306a36Sopenharmony_ci	val = readl(src_base + SRC_PLLCR);
17462306a36Sopenharmony_ci	if (pll->id == 1) {
17562306a36Sopenharmony_ci		if (val & SRC_PLLCR_PLL1OVER) {
17662306a36Sopenharmony_ci			val |= SRC_PLLCR_PLL1EN;
17762306a36Sopenharmony_ci			writel(val, src_base + SRC_PLLCR);
17862306a36Sopenharmony_ci		}
17962306a36Sopenharmony_ci	} else if (pll->id == 2) {
18062306a36Sopenharmony_ci		val |= SRC_PLLCR_PLL2EN;
18162306a36Sopenharmony_ci		writel(val, src_base + SRC_PLLCR);
18262306a36Sopenharmony_ci	}
18362306a36Sopenharmony_ci	spin_unlock(&src_lock);
18462306a36Sopenharmony_ci	return 0;
18562306a36Sopenharmony_ci}
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_cistatic void pll_clk_disable(struct clk_hw *hw)
18862306a36Sopenharmony_ci{
18962306a36Sopenharmony_ci	struct clk_pll *pll = to_pll(hw);
19062306a36Sopenharmony_ci	u32 val;
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci	spin_lock(&src_lock);
19362306a36Sopenharmony_ci	val = readl(src_base + SRC_PLLCR);
19462306a36Sopenharmony_ci	if (pll->id == 1) {
19562306a36Sopenharmony_ci		if (val & SRC_PLLCR_PLL1OVER) {
19662306a36Sopenharmony_ci			val &= ~SRC_PLLCR_PLL1EN;
19762306a36Sopenharmony_ci			writel(val, src_base + SRC_PLLCR);
19862306a36Sopenharmony_ci		}
19962306a36Sopenharmony_ci	} else if (pll->id == 2) {
20062306a36Sopenharmony_ci		val &= ~SRC_PLLCR_PLL2EN;
20162306a36Sopenharmony_ci		writel(val, src_base + SRC_PLLCR);
20262306a36Sopenharmony_ci	}
20362306a36Sopenharmony_ci	spin_unlock(&src_lock);
20462306a36Sopenharmony_ci}
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_cistatic int pll_clk_is_enabled(struct clk_hw *hw)
20762306a36Sopenharmony_ci{
20862306a36Sopenharmony_ci	struct clk_pll *pll = to_pll(hw);
20962306a36Sopenharmony_ci	u32 val;
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	val = readl(src_base + SRC_PLLCR);
21262306a36Sopenharmony_ci	if (pll->id == 1) {
21362306a36Sopenharmony_ci		if (val & SRC_PLLCR_PLL1OVER)
21462306a36Sopenharmony_ci			return !!(val & SRC_PLLCR_PLL1EN);
21562306a36Sopenharmony_ci	} else if (pll->id == 2) {
21662306a36Sopenharmony_ci		return !!(val & SRC_PLLCR_PLL2EN);
21762306a36Sopenharmony_ci	}
21862306a36Sopenharmony_ci	return 1;
21962306a36Sopenharmony_ci}
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_cistatic unsigned long pll_clk_recalc_rate(struct clk_hw *hw,
22262306a36Sopenharmony_ci					  unsigned long parent_rate)
22362306a36Sopenharmony_ci{
22462306a36Sopenharmony_ci	struct clk_pll *pll = to_pll(hw);
22562306a36Sopenharmony_ci	u32 val;
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci	val = readl(src_base + SRC_PLLFR);
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci	if (pll->id == 1) {
23062306a36Sopenharmony_ci		u8 mul;
23162306a36Sopenharmony_ci		u8 div;
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci		mul = (val >> 8) & 0x3FU;
23462306a36Sopenharmony_ci		mul += 2;
23562306a36Sopenharmony_ci		div = val & 0x07U;
23662306a36Sopenharmony_ci		return (parent_rate * mul) >> div;
23762306a36Sopenharmony_ci	}
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci	if (pll->id == 2) {
24062306a36Sopenharmony_ci		u8 mul;
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci		mul = (val >> 24) & 0x3FU;
24362306a36Sopenharmony_ci		mul += 2;
24462306a36Sopenharmony_ci		return (parent_rate * mul);
24562306a36Sopenharmony_ci	}
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci	/* Unknown PLL */
24862306a36Sopenharmony_ci	return 0;
24962306a36Sopenharmony_ci}
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_cistatic const struct clk_ops pll_clk_ops = {
25362306a36Sopenharmony_ci	.enable = pll_clk_enable,
25462306a36Sopenharmony_ci	.disable = pll_clk_disable,
25562306a36Sopenharmony_ci	.is_enabled = pll_clk_is_enabled,
25662306a36Sopenharmony_ci	.recalc_rate = pll_clk_recalc_rate,
25762306a36Sopenharmony_ci};
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_cistatic struct clk_hw * __init
26062306a36Sopenharmony_cipll_clk_register(struct device *dev, const char *name,
26162306a36Sopenharmony_ci		 const char *parent_name, u32 id)
26262306a36Sopenharmony_ci{
26362306a36Sopenharmony_ci	int ret;
26462306a36Sopenharmony_ci	struct clk_pll *pll;
26562306a36Sopenharmony_ci	struct clk_init_data init;
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci	if (id != 1 && id != 2) {
26862306a36Sopenharmony_ci		pr_err("%s: the Nomadik has only PLL 1 & 2\n", __func__);
26962306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
27062306a36Sopenharmony_ci	}
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci	pll = kzalloc(sizeof(*pll), GFP_KERNEL);
27362306a36Sopenharmony_ci	if (!pll)
27462306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci	init.name = name;
27762306a36Sopenharmony_ci	init.ops = &pll_clk_ops;
27862306a36Sopenharmony_ci	init.parent_names = (parent_name ? &parent_name : NULL);
27962306a36Sopenharmony_ci	init.num_parents = (parent_name ? 1 : 0);
28062306a36Sopenharmony_ci	pll->hw.init = &init;
28162306a36Sopenharmony_ci	pll->id = id;
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci	pr_debug("register PLL1 clock \"%s\"\n", name);
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci	ret = clk_hw_register(dev, &pll->hw);
28662306a36Sopenharmony_ci	if (ret) {
28762306a36Sopenharmony_ci		kfree(pll);
28862306a36Sopenharmony_ci		return ERR_PTR(ret);
28962306a36Sopenharmony_ci	}
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci	return &pll->hw;
29262306a36Sopenharmony_ci}
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci/*
29562306a36Sopenharmony_ci * The Nomadik SRC clocks are gated, but not in the sense that
29662306a36Sopenharmony_ci * you read-modify-write a register. Instead there are separate
29762306a36Sopenharmony_ci * clock enable and clock disable registers. Writing a '1' bit in
29862306a36Sopenharmony_ci * the enable register for a certain clock ungates that clock without
29962306a36Sopenharmony_ci * affecting the other clocks. The disable register works the opposite
30062306a36Sopenharmony_ci * way.
30162306a36Sopenharmony_ci */
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_cistatic int src_clk_enable(struct clk_hw *hw)
30462306a36Sopenharmony_ci{
30562306a36Sopenharmony_ci	struct clk_src *sclk = to_src(hw);
30662306a36Sopenharmony_ci	u32 enreg = sclk->group1 ? SRC_PCKEN1 : SRC_PCKEN0;
30762306a36Sopenharmony_ci	u32 sreg = sclk->group1 ? SRC_PCKSR1 : SRC_PCKSR0;
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci	writel(sclk->clkbit, src_base + enreg);
31062306a36Sopenharmony_ci	/* spin until enabled */
31162306a36Sopenharmony_ci	while (!(readl(src_base + sreg) & sclk->clkbit))
31262306a36Sopenharmony_ci		cpu_relax();
31362306a36Sopenharmony_ci	return 0;
31462306a36Sopenharmony_ci}
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_cistatic void src_clk_disable(struct clk_hw *hw)
31762306a36Sopenharmony_ci{
31862306a36Sopenharmony_ci	struct clk_src *sclk = to_src(hw);
31962306a36Sopenharmony_ci	u32 disreg = sclk->group1 ? SRC_PCKDIS1 : SRC_PCKDIS0;
32062306a36Sopenharmony_ci	u32 sreg = sclk->group1 ? SRC_PCKSR1 : SRC_PCKSR0;
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci	writel(sclk->clkbit, src_base + disreg);
32362306a36Sopenharmony_ci	/* spin until disabled */
32462306a36Sopenharmony_ci	while (readl(src_base + sreg) & sclk->clkbit)
32562306a36Sopenharmony_ci		cpu_relax();
32662306a36Sopenharmony_ci}
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_cistatic int src_clk_is_enabled(struct clk_hw *hw)
32962306a36Sopenharmony_ci{
33062306a36Sopenharmony_ci	struct clk_src *sclk = to_src(hw);
33162306a36Sopenharmony_ci	u32 sreg = sclk->group1 ? SRC_PCKSR1 : SRC_PCKSR0;
33262306a36Sopenharmony_ci	u32 val = readl(src_base + sreg);
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci	return !!(val & sclk->clkbit);
33562306a36Sopenharmony_ci}
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_cistatic unsigned long
33862306a36Sopenharmony_cisrc_clk_recalc_rate(struct clk_hw *hw,
33962306a36Sopenharmony_ci		    unsigned long parent_rate)
34062306a36Sopenharmony_ci{
34162306a36Sopenharmony_ci	return parent_rate;
34262306a36Sopenharmony_ci}
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_cistatic const struct clk_ops src_clk_ops = {
34562306a36Sopenharmony_ci	.enable = src_clk_enable,
34662306a36Sopenharmony_ci	.disable = src_clk_disable,
34762306a36Sopenharmony_ci	.is_enabled = src_clk_is_enabled,
34862306a36Sopenharmony_ci	.recalc_rate = src_clk_recalc_rate,
34962306a36Sopenharmony_ci};
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_cistatic struct clk_hw * __init
35262306a36Sopenharmony_cisrc_clk_register(struct device *dev, const char *name,
35362306a36Sopenharmony_ci		 const char *parent_name, u8 id)
35462306a36Sopenharmony_ci{
35562306a36Sopenharmony_ci	int ret;
35662306a36Sopenharmony_ci	struct clk_src *sclk;
35762306a36Sopenharmony_ci	struct clk_init_data init;
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci	sclk = kzalloc(sizeof(*sclk), GFP_KERNEL);
36062306a36Sopenharmony_ci	if (!sclk)
36162306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_ci	init.name = name;
36462306a36Sopenharmony_ci	init.ops = &src_clk_ops;
36562306a36Sopenharmony_ci	/* Do not force-disable the static SDRAM controller */
36662306a36Sopenharmony_ci	if (id == 2)
36762306a36Sopenharmony_ci		init.flags = CLK_IGNORE_UNUSED;
36862306a36Sopenharmony_ci	else
36962306a36Sopenharmony_ci		init.flags = 0;
37062306a36Sopenharmony_ci	init.parent_names = (parent_name ? &parent_name : NULL);
37162306a36Sopenharmony_ci	init.num_parents = (parent_name ? 1 : 0);
37262306a36Sopenharmony_ci	sclk->hw.init = &init;
37362306a36Sopenharmony_ci	sclk->id = id;
37462306a36Sopenharmony_ci	sclk->group1 = (id > 31);
37562306a36Sopenharmony_ci	sclk->clkbit = BIT(id & 0x1f);
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci	pr_debug("register clock \"%s\" ID: %d group: %d bits: %08x\n",
37862306a36Sopenharmony_ci		 name, id, sclk->group1, sclk->clkbit);
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci	ret = clk_hw_register(dev, &sclk->hw);
38162306a36Sopenharmony_ci	if (ret) {
38262306a36Sopenharmony_ci		kfree(sclk);
38362306a36Sopenharmony_ci		return ERR_PTR(ret);
38462306a36Sopenharmony_ci	}
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci	return &sclk->hw;
38762306a36Sopenharmony_ci}
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci#ifdef CONFIG_DEBUG_FS
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_cistatic u32 src_pcksr0_boot;
39262306a36Sopenharmony_cistatic u32 src_pcksr1_boot;
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_cistatic const char * const src_clk_names[] = {
39562306a36Sopenharmony_ci	"HCLKDMA0  ",
39662306a36Sopenharmony_ci	"HCLKSMC   ",
39762306a36Sopenharmony_ci	"HCLKSDRAM ",
39862306a36Sopenharmony_ci	"HCLKDMA1  ",
39962306a36Sopenharmony_ci	"HCLKCLCD  ",
40062306a36Sopenharmony_ci	"PCLKIRDA  ",
40162306a36Sopenharmony_ci	"PCLKSSP   ",
40262306a36Sopenharmony_ci	"PCLKUART0 ",
40362306a36Sopenharmony_ci	"PCLKSDI   ",
40462306a36Sopenharmony_ci	"PCLKI2C0  ",
40562306a36Sopenharmony_ci	"PCLKI2C1  ",
40662306a36Sopenharmony_ci	"PCLKUART1 ",
40762306a36Sopenharmony_ci	"PCLMSP0   ",
40862306a36Sopenharmony_ci	"HCLKUSB   ",
40962306a36Sopenharmony_ci	"HCLKDIF   ",
41062306a36Sopenharmony_ci	"HCLKSAA   ",
41162306a36Sopenharmony_ci	"HCLKSVA   ",
41262306a36Sopenharmony_ci	"PCLKHSI   ",
41362306a36Sopenharmony_ci	"PCLKXTI   ",
41462306a36Sopenharmony_ci	"PCLKUART2 ",
41562306a36Sopenharmony_ci	"PCLKMSP1  ",
41662306a36Sopenharmony_ci	"PCLKMSP2  ",
41762306a36Sopenharmony_ci	"PCLKOWM   ",
41862306a36Sopenharmony_ci	"HCLKHPI   ",
41962306a36Sopenharmony_ci	"PCLKSKE   ",
42062306a36Sopenharmony_ci	"PCLKHSEM  ",
42162306a36Sopenharmony_ci	"HCLK3D    ",
42262306a36Sopenharmony_ci	"HCLKHASH  ",
42362306a36Sopenharmony_ci	"HCLKCRYP  ",
42462306a36Sopenharmony_ci	"PCLKMSHC  ",
42562306a36Sopenharmony_ci	"HCLKUSBM  ",
42662306a36Sopenharmony_ci	"HCLKRNG   ",
42762306a36Sopenharmony_ci	"RESERVED  ",
42862306a36Sopenharmony_ci	"RESERVED  ",
42962306a36Sopenharmony_ci	"RESERVED  ",
43062306a36Sopenharmony_ci	"RESERVED  ",
43162306a36Sopenharmony_ci	"CLDCLK    ",
43262306a36Sopenharmony_ci	"IRDACLK   ",
43362306a36Sopenharmony_ci	"SSPICLK   ",
43462306a36Sopenharmony_ci	"UART0CLK  ",
43562306a36Sopenharmony_ci	"SDICLK    ",
43662306a36Sopenharmony_ci	"I2C0CLK   ",
43762306a36Sopenharmony_ci	"I2C1CLK   ",
43862306a36Sopenharmony_ci	"UART1CLK  ",
43962306a36Sopenharmony_ci	"MSPCLK0   ",
44062306a36Sopenharmony_ci	"USBCLK    ",
44162306a36Sopenharmony_ci	"DIFCLK    ",
44262306a36Sopenharmony_ci	"IPI2CCLK  ",
44362306a36Sopenharmony_ci	"IPBMCCLK  ",
44462306a36Sopenharmony_ci	"HSICLKRX  ",
44562306a36Sopenharmony_ci	"HSICLKTX  ",
44662306a36Sopenharmony_ci	"UART2CLK  ",
44762306a36Sopenharmony_ci	"MSPCLK1   ",
44862306a36Sopenharmony_ci	"MSPCLK2   ",
44962306a36Sopenharmony_ci	"OWMCLK    ",
45062306a36Sopenharmony_ci	"RESERVED  ",
45162306a36Sopenharmony_ci	"SKECLK    ",
45262306a36Sopenharmony_ci	"RESERVED  ",
45362306a36Sopenharmony_ci	"3DCLK     ",
45462306a36Sopenharmony_ci	"PCLKMSP3  ",
45562306a36Sopenharmony_ci	"MSPCLK3   ",
45662306a36Sopenharmony_ci	"MSHCCLK   ",
45762306a36Sopenharmony_ci	"USBMCLK   ",
45862306a36Sopenharmony_ci	"RNGCCLK   ",
45962306a36Sopenharmony_ci};
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_cistatic int nomadik_src_clk_debugfs_show(struct seq_file *s, void *what)
46262306a36Sopenharmony_ci{
46362306a36Sopenharmony_ci	int i;
46462306a36Sopenharmony_ci	u32 src_pcksr0 = readl(src_base + SRC_PCKSR0);
46562306a36Sopenharmony_ci	u32 src_pcksr1 = readl(src_base + SRC_PCKSR1);
46662306a36Sopenharmony_ci	u32 src_pckensr0 = readl(src_base + SRC_PCKENSR0);
46762306a36Sopenharmony_ci	u32 src_pckensr1 = readl(src_base + SRC_PCKENSR1);
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci	seq_puts(s, "Clock:      Boot:   Now:    Request: ASKED:\n");
47062306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(src_clk_names); i++) {
47162306a36Sopenharmony_ci		u32 pcksrb = (i < 0x20) ? src_pcksr0_boot : src_pcksr1_boot;
47262306a36Sopenharmony_ci		u32 pcksr = (i < 0x20) ? src_pcksr0 : src_pcksr1;
47362306a36Sopenharmony_ci		u32 pckreq = (i < 0x20) ? src_pckensr0 : src_pckensr1;
47462306a36Sopenharmony_ci		u32 mask = BIT(i & 0x1f);
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_ci		seq_printf(s, "%s  %s     %s     %s\n",
47762306a36Sopenharmony_ci			   src_clk_names[i],
47862306a36Sopenharmony_ci			   (pcksrb & mask) ? "on " : "off",
47962306a36Sopenharmony_ci			   (pcksr & mask) ? "on " : "off",
48062306a36Sopenharmony_ci			   (pckreq & mask) ? "on " : "off");
48162306a36Sopenharmony_ci	}
48262306a36Sopenharmony_ci	return 0;
48362306a36Sopenharmony_ci}
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(nomadik_src_clk_debugfs);
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_cistatic int __init nomadik_src_clk_init_debugfs(void)
48862306a36Sopenharmony_ci{
48962306a36Sopenharmony_ci	/* Vital for multiplatform */
49062306a36Sopenharmony_ci	if (!src_base)
49162306a36Sopenharmony_ci		return -ENODEV;
49262306a36Sopenharmony_ci	src_pcksr0_boot = readl(src_base + SRC_PCKSR0);
49362306a36Sopenharmony_ci	src_pcksr1_boot = readl(src_base + SRC_PCKSR1);
49462306a36Sopenharmony_ci	debugfs_create_file("nomadik-src-clk", S_IFREG | S_IRUGO,
49562306a36Sopenharmony_ci			    NULL, NULL, &nomadik_src_clk_debugfs_fops);
49662306a36Sopenharmony_ci	return 0;
49762306a36Sopenharmony_ci}
49862306a36Sopenharmony_cidevice_initcall(nomadik_src_clk_init_debugfs);
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_ci#endif
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_cistatic void __init of_nomadik_pll_setup(struct device_node *np)
50362306a36Sopenharmony_ci{
50462306a36Sopenharmony_ci	struct clk_hw *hw;
50562306a36Sopenharmony_ci	const char *clk_name = np->name;
50662306a36Sopenharmony_ci	const char *parent_name;
50762306a36Sopenharmony_ci	u32 pll_id;
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_ci	if (!src_base)
51062306a36Sopenharmony_ci		nomadik_src_init();
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_ci	if (of_property_read_u32(np, "pll-id", &pll_id)) {
51362306a36Sopenharmony_ci		pr_err("%s: PLL \"%s\" missing pll-id property\n",
51462306a36Sopenharmony_ci			__func__, clk_name);
51562306a36Sopenharmony_ci		return;
51662306a36Sopenharmony_ci	}
51762306a36Sopenharmony_ci	parent_name = of_clk_get_parent_name(np, 0);
51862306a36Sopenharmony_ci	hw = pll_clk_register(NULL, clk_name, parent_name, pll_id);
51962306a36Sopenharmony_ci	if (!IS_ERR(hw))
52062306a36Sopenharmony_ci		of_clk_add_hw_provider(np, of_clk_hw_simple_get, hw);
52162306a36Sopenharmony_ci}
52262306a36Sopenharmony_ciCLK_OF_DECLARE(nomadik_pll_clk,
52362306a36Sopenharmony_ci	"st,nomadik-pll-clock", of_nomadik_pll_setup);
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_cistatic void __init of_nomadik_hclk_setup(struct device_node *np)
52662306a36Sopenharmony_ci{
52762306a36Sopenharmony_ci	struct clk_hw *hw;
52862306a36Sopenharmony_ci	const char *clk_name = np->name;
52962306a36Sopenharmony_ci	const char *parent_name;
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ci	if (!src_base)
53262306a36Sopenharmony_ci		nomadik_src_init();
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_ci	parent_name = of_clk_get_parent_name(np, 0);
53562306a36Sopenharmony_ci	/*
53662306a36Sopenharmony_ci	 * The HCLK divides PLL1 with 1 (passthru), 2, 3 or 4.
53762306a36Sopenharmony_ci	 */
53862306a36Sopenharmony_ci	hw = clk_hw_register_divider(NULL, clk_name, parent_name,
53962306a36Sopenharmony_ci			   0, src_base + SRC_CR,
54062306a36Sopenharmony_ci			   13, 2,
54162306a36Sopenharmony_ci			   CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO,
54262306a36Sopenharmony_ci			   &src_lock);
54362306a36Sopenharmony_ci	if (!IS_ERR(hw))
54462306a36Sopenharmony_ci		of_clk_add_hw_provider(np, of_clk_hw_simple_get, hw);
54562306a36Sopenharmony_ci}
54662306a36Sopenharmony_ciCLK_OF_DECLARE(nomadik_hclk_clk,
54762306a36Sopenharmony_ci	"st,nomadik-hclk-clock", of_nomadik_hclk_setup);
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_cistatic void __init of_nomadik_src_clk_setup(struct device_node *np)
55062306a36Sopenharmony_ci{
55162306a36Sopenharmony_ci	struct clk_hw *hw;
55262306a36Sopenharmony_ci	const char *clk_name = np->name;
55362306a36Sopenharmony_ci	const char *parent_name;
55462306a36Sopenharmony_ci	u32 clk_id;
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_ci	if (!src_base)
55762306a36Sopenharmony_ci		nomadik_src_init();
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_ci	if (of_property_read_u32(np, "clock-id", &clk_id)) {
56062306a36Sopenharmony_ci		pr_err("%s: SRC clock \"%s\" missing clock-id property\n",
56162306a36Sopenharmony_ci			__func__, clk_name);
56262306a36Sopenharmony_ci		return;
56362306a36Sopenharmony_ci	}
56462306a36Sopenharmony_ci	parent_name = of_clk_get_parent_name(np, 0);
56562306a36Sopenharmony_ci	hw = src_clk_register(NULL, clk_name, parent_name, clk_id);
56662306a36Sopenharmony_ci	if (!IS_ERR(hw))
56762306a36Sopenharmony_ci		of_clk_add_hw_provider(np, of_clk_hw_simple_get, hw);
56862306a36Sopenharmony_ci}
56962306a36Sopenharmony_ciCLK_OF_DECLARE(nomadik_src_clk,
57062306a36Sopenharmony_ci	"st,nomadik-src-clock", of_nomadik_src_clk_setup);
571