162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * MVEBU Core divider clock
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2013 Marvell
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Ezequiel Garcia <ezequiel.garcia@free-electrons.com>
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci */
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include <linux/kernel.h>
1262306a36Sopenharmony_ci#include <linux/clk-provider.h>
1362306a36Sopenharmony_ci#include <linux/io.h>
1462306a36Sopenharmony_ci#include <linux/of_address.h>
1562306a36Sopenharmony_ci#include <linux/slab.h>
1662306a36Sopenharmony_ci#include <linux/delay.h>
1762306a36Sopenharmony_ci#include "common.h"
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#define CORE_CLK_DIV_RATIO_MASK		0xff
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci/*
2262306a36Sopenharmony_ci * This structure describes the hardware details (bit offset and mask)
2362306a36Sopenharmony_ci * to configure one particular core divider clock. Those hardware
2462306a36Sopenharmony_ci * details may differ from one SoC to another. This structure is
2562306a36Sopenharmony_ci * therefore typically instantiated statically to describe the
2662306a36Sopenharmony_ci * hardware details.
2762306a36Sopenharmony_ci */
2862306a36Sopenharmony_cistruct clk_corediv_desc {
2962306a36Sopenharmony_ci	unsigned int mask;
3062306a36Sopenharmony_ci	unsigned int offset;
3162306a36Sopenharmony_ci	unsigned int fieldbit;
3262306a36Sopenharmony_ci};
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci/*
3562306a36Sopenharmony_ci * This structure describes the hardware details to configure the core
3662306a36Sopenharmony_ci * divider clocks on a given SoC. Amongst others, it points to the
3762306a36Sopenharmony_ci * array of core divider clock descriptors for this SoC, as well as
3862306a36Sopenharmony_ci * the corresponding operations to manipulate them.
3962306a36Sopenharmony_ci */
4062306a36Sopenharmony_cistruct clk_corediv_soc_desc {
4162306a36Sopenharmony_ci	const struct clk_corediv_desc *descs;
4262306a36Sopenharmony_ci	unsigned int ndescs;
4362306a36Sopenharmony_ci	const struct clk_ops ops;
4462306a36Sopenharmony_ci	u32 ratio_reload;
4562306a36Sopenharmony_ci	u32 enable_bit_offset;
4662306a36Sopenharmony_ci	u32 ratio_offset;
4762306a36Sopenharmony_ci};
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci/*
5062306a36Sopenharmony_ci * This structure represents one core divider clock for the clock
5162306a36Sopenharmony_ci * framework, and is dynamically allocated for each core divider clock
5262306a36Sopenharmony_ci * existing in the current SoC.
5362306a36Sopenharmony_ci */
5462306a36Sopenharmony_cistruct clk_corediv {
5562306a36Sopenharmony_ci	struct clk_hw hw;
5662306a36Sopenharmony_ci	void __iomem *reg;
5762306a36Sopenharmony_ci	const struct clk_corediv_desc *desc;
5862306a36Sopenharmony_ci	const struct clk_corediv_soc_desc *soc_desc;
5962306a36Sopenharmony_ci	spinlock_t lock;
6062306a36Sopenharmony_ci};
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_cistatic struct clk_onecell_data clk_data;
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci/*
6562306a36Sopenharmony_ci * Description of the core divider clocks available. For now, we
6662306a36Sopenharmony_ci * support only NAND, and it is available at the same register
6762306a36Sopenharmony_ci * locations regardless of the SoC.
6862306a36Sopenharmony_ci */
6962306a36Sopenharmony_cistatic const struct clk_corediv_desc mvebu_corediv_desc[] = {
7062306a36Sopenharmony_ci	{ .mask = 0x3f, .offset = 8, .fieldbit = 1 }, /* NAND clock */
7162306a36Sopenharmony_ci};
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_cistatic const struct clk_corediv_desc mv98dx3236_corediv_desc[] = {
7462306a36Sopenharmony_ci	{ .mask = 0x0f, .offset = 6, .fieldbit = 27 }, /* NAND clock */
7562306a36Sopenharmony_ci};
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci#define to_corediv_clk(p) container_of(p, struct clk_corediv, hw)
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_cistatic int clk_corediv_is_enabled(struct clk_hw *hwclk)
8062306a36Sopenharmony_ci{
8162306a36Sopenharmony_ci	struct clk_corediv *corediv = to_corediv_clk(hwclk);
8262306a36Sopenharmony_ci	const struct clk_corediv_soc_desc *soc_desc = corediv->soc_desc;
8362306a36Sopenharmony_ci	const struct clk_corediv_desc *desc = corediv->desc;
8462306a36Sopenharmony_ci	u32 enable_mask = BIT(desc->fieldbit) << soc_desc->enable_bit_offset;
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci	return !!(readl(corediv->reg) & enable_mask);
8762306a36Sopenharmony_ci}
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_cistatic int clk_corediv_enable(struct clk_hw *hwclk)
9062306a36Sopenharmony_ci{
9162306a36Sopenharmony_ci	struct clk_corediv *corediv = to_corediv_clk(hwclk);
9262306a36Sopenharmony_ci	const struct clk_corediv_soc_desc *soc_desc = corediv->soc_desc;
9362306a36Sopenharmony_ci	const struct clk_corediv_desc *desc = corediv->desc;
9462306a36Sopenharmony_ci	unsigned long flags = 0;
9562306a36Sopenharmony_ci	u32 reg;
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	spin_lock_irqsave(&corediv->lock, flags);
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	reg = readl(corediv->reg);
10062306a36Sopenharmony_ci	reg |= (BIT(desc->fieldbit) << soc_desc->enable_bit_offset);
10162306a36Sopenharmony_ci	writel(reg, corediv->reg);
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	spin_unlock_irqrestore(&corediv->lock, flags);
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci	return 0;
10662306a36Sopenharmony_ci}
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_cistatic void clk_corediv_disable(struct clk_hw *hwclk)
10962306a36Sopenharmony_ci{
11062306a36Sopenharmony_ci	struct clk_corediv *corediv = to_corediv_clk(hwclk);
11162306a36Sopenharmony_ci	const struct clk_corediv_soc_desc *soc_desc = corediv->soc_desc;
11262306a36Sopenharmony_ci	const struct clk_corediv_desc *desc = corediv->desc;
11362306a36Sopenharmony_ci	unsigned long flags = 0;
11462306a36Sopenharmony_ci	u32 reg;
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	spin_lock_irqsave(&corediv->lock, flags);
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci	reg = readl(corediv->reg);
11962306a36Sopenharmony_ci	reg &= ~(BIT(desc->fieldbit) << soc_desc->enable_bit_offset);
12062306a36Sopenharmony_ci	writel(reg, corediv->reg);
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	spin_unlock_irqrestore(&corediv->lock, flags);
12362306a36Sopenharmony_ci}
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_cistatic unsigned long clk_corediv_recalc_rate(struct clk_hw *hwclk,
12662306a36Sopenharmony_ci					 unsigned long parent_rate)
12762306a36Sopenharmony_ci{
12862306a36Sopenharmony_ci	struct clk_corediv *corediv = to_corediv_clk(hwclk);
12962306a36Sopenharmony_ci	const struct clk_corediv_soc_desc *soc_desc = corediv->soc_desc;
13062306a36Sopenharmony_ci	const struct clk_corediv_desc *desc = corediv->desc;
13162306a36Sopenharmony_ci	u32 reg, div;
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	reg = readl(corediv->reg + soc_desc->ratio_offset);
13462306a36Sopenharmony_ci	div = (reg >> desc->offset) & desc->mask;
13562306a36Sopenharmony_ci	return parent_rate / div;
13662306a36Sopenharmony_ci}
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_cistatic long clk_corediv_round_rate(struct clk_hw *hwclk, unsigned long rate,
13962306a36Sopenharmony_ci			       unsigned long *parent_rate)
14062306a36Sopenharmony_ci{
14162306a36Sopenharmony_ci	/* Valid ratio are 1:4, 1:5, 1:6 and 1:8 */
14262306a36Sopenharmony_ci	u32 div;
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci	div = *parent_rate / rate;
14562306a36Sopenharmony_ci	if (div < 4)
14662306a36Sopenharmony_ci		div = 4;
14762306a36Sopenharmony_ci	else if (div > 6)
14862306a36Sopenharmony_ci		div = 8;
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	return *parent_rate / div;
15162306a36Sopenharmony_ci}
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_cistatic int clk_corediv_set_rate(struct clk_hw *hwclk, unsigned long rate,
15462306a36Sopenharmony_ci			    unsigned long parent_rate)
15562306a36Sopenharmony_ci{
15662306a36Sopenharmony_ci	struct clk_corediv *corediv = to_corediv_clk(hwclk);
15762306a36Sopenharmony_ci	const struct clk_corediv_soc_desc *soc_desc = corediv->soc_desc;
15862306a36Sopenharmony_ci	const struct clk_corediv_desc *desc = corediv->desc;
15962306a36Sopenharmony_ci	unsigned long flags = 0;
16062306a36Sopenharmony_ci	u32 reg, div;
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci	div = parent_rate / rate;
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	spin_lock_irqsave(&corediv->lock, flags);
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci	/* Write new divider to the divider ratio register */
16762306a36Sopenharmony_ci	reg = readl(corediv->reg + soc_desc->ratio_offset);
16862306a36Sopenharmony_ci	reg &= ~(desc->mask << desc->offset);
16962306a36Sopenharmony_ci	reg |= (div & desc->mask) << desc->offset;
17062306a36Sopenharmony_ci	writel(reg, corediv->reg + soc_desc->ratio_offset);
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	/* Set reload-force for this clock */
17362306a36Sopenharmony_ci	reg = readl(corediv->reg) | BIT(desc->fieldbit);
17462306a36Sopenharmony_ci	writel(reg, corediv->reg);
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci	/* Now trigger the clock update */
17762306a36Sopenharmony_ci	reg = readl(corediv->reg) | soc_desc->ratio_reload;
17862306a36Sopenharmony_ci	writel(reg, corediv->reg);
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	/*
18162306a36Sopenharmony_ci	 * Wait for clocks to settle down, and then clear all the
18262306a36Sopenharmony_ci	 * ratios request and the reload request.
18362306a36Sopenharmony_ci	 */
18462306a36Sopenharmony_ci	udelay(1000);
18562306a36Sopenharmony_ci	reg &= ~(CORE_CLK_DIV_RATIO_MASK | soc_desc->ratio_reload);
18662306a36Sopenharmony_ci	writel(reg, corediv->reg);
18762306a36Sopenharmony_ci	udelay(1000);
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	spin_unlock_irqrestore(&corediv->lock, flags);
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	return 0;
19262306a36Sopenharmony_ci}
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_cistatic const struct clk_corediv_soc_desc armada370_corediv_soc = {
19562306a36Sopenharmony_ci	.descs = mvebu_corediv_desc,
19662306a36Sopenharmony_ci	.ndescs = ARRAY_SIZE(mvebu_corediv_desc),
19762306a36Sopenharmony_ci	.ops = {
19862306a36Sopenharmony_ci		.enable = clk_corediv_enable,
19962306a36Sopenharmony_ci		.disable = clk_corediv_disable,
20062306a36Sopenharmony_ci		.is_enabled = clk_corediv_is_enabled,
20162306a36Sopenharmony_ci		.recalc_rate = clk_corediv_recalc_rate,
20262306a36Sopenharmony_ci		.round_rate = clk_corediv_round_rate,
20362306a36Sopenharmony_ci		.set_rate = clk_corediv_set_rate,
20462306a36Sopenharmony_ci	},
20562306a36Sopenharmony_ci	.ratio_reload = BIT(8),
20662306a36Sopenharmony_ci	.enable_bit_offset = 24,
20762306a36Sopenharmony_ci	.ratio_offset = 0x8,
20862306a36Sopenharmony_ci};
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_cistatic const struct clk_corediv_soc_desc armada380_corediv_soc = {
21162306a36Sopenharmony_ci	.descs = mvebu_corediv_desc,
21262306a36Sopenharmony_ci	.ndescs = ARRAY_SIZE(mvebu_corediv_desc),
21362306a36Sopenharmony_ci	.ops = {
21462306a36Sopenharmony_ci		.enable = clk_corediv_enable,
21562306a36Sopenharmony_ci		.disable = clk_corediv_disable,
21662306a36Sopenharmony_ci		.is_enabled = clk_corediv_is_enabled,
21762306a36Sopenharmony_ci		.recalc_rate = clk_corediv_recalc_rate,
21862306a36Sopenharmony_ci		.round_rate = clk_corediv_round_rate,
21962306a36Sopenharmony_ci		.set_rate = clk_corediv_set_rate,
22062306a36Sopenharmony_ci	},
22162306a36Sopenharmony_ci	.ratio_reload = BIT(8),
22262306a36Sopenharmony_ci	.enable_bit_offset = 16,
22362306a36Sopenharmony_ci	.ratio_offset = 0x4,
22462306a36Sopenharmony_ci};
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_cistatic const struct clk_corediv_soc_desc armada375_corediv_soc = {
22762306a36Sopenharmony_ci	.descs = mvebu_corediv_desc,
22862306a36Sopenharmony_ci	.ndescs = ARRAY_SIZE(mvebu_corediv_desc),
22962306a36Sopenharmony_ci	.ops = {
23062306a36Sopenharmony_ci		.recalc_rate = clk_corediv_recalc_rate,
23162306a36Sopenharmony_ci		.round_rate = clk_corediv_round_rate,
23262306a36Sopenharmony_ci		.set_rate = clk_corediv_set_rate,
23362306a36Sopenharmony_ci	},
23462306a36Sopenharmony_ci	.ratio_reload = BIT(8),
23562306a36Sopenharmony_ci	.ratio_offset = 0x4,
23662306a36Sopenharmony_ci};
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_cistatic const struct clk_corediv_soc_desc mv98dx3236_corediv_soc = {
23962306a36Sopenharmony_ci	.descs = mv98dx3236_corediv_desc,
24062306a36Sopenharmony_ci	.ndescs = ARRAY_SIZE(mv98dx3236_corediv_desc),
24162306a36Sopenharmony_ci	.ops = {
24262306a36Sopenharmony_ci		.recalc_rate = clk_corediv_recalc_rate,
24362306a36Sopenharmony_ci		.round_rate = clk_corediv_round_rate,
24462306a36Sopenharmony_ci		.set_rate = clk_corediv_set_rate,
24562306a36Sopenharmony_ci	},
24662306a36Sopenharmony_ci	.ratio_reload = BIT(10),
24762306a36Sopenharmony_ci	.ratio_offset = 0x8,
24862306a36Sopenharmony_ci};
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_cistatic void __init
25162306a36Sopenharmony_cimvebu_corediv_clk_init(struct device_node *node,
25262306a36Sopenharmony_ci		       const struct clk_corediv_soc_desc *soc_desc)
25362306a36Sopenharmony_ci{
25462306a36Sopenharmony_ci	struct clk_init_data init;
25562306a36Sopenharmony_ci	struct clk_corediv *corediv;
25662306a36Sopenharmony_ci	struct clk **clks;
25762306a36Sopenharmony_ci	void __iomem *base;
25862306a36Sopenharmony_ci	const char *parent_name;
25962306a36Sopenharmony_ci	const char *clk_name;
26062306a36Sopenharmony_ci	int i;
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci	base = of_iomap(node, 0);
26362306a36Sopenharmony_ci	if (WARN_ON(!base))
26462306a36Sopenharmony_ci		return;
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci	parent_name = of_clk_get_parent_name(node, 0);
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci	clk_data.clk_num = soc_desc->ndescs;
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	/* clks holds the clock array */
27162306a36Sopenharmony_ci	clks = kcalloc(clk_data.clk_num, sizeof(struct clk *),
27262306a36Sopenharmony_ci				GFP_KERNEL);
27362306a36Sopenharmony_ci	if (WARN_ON(!clks))
27462306a36Sopenharmony_ci		goto err_unmap;
27562306a36Sopenharmony_ci	/* corediv holds the clock specific array */
27662306a36Sopenharmony_ci	corediv = kcalloc(clk_data.clk_num, sizeof(struct clk_corediv),
27762306a36Sopenharmony_ci				GFP_KERNEL);
27862306a36Sopenharmony_ci	if (WARN_ON(!corediv))
27962306a36Sopenharmony_ci		goto err_free_clks;
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci	spin_lock_init(&corediv->lock);
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci	for (i = 0; i < clk_data.clk_num; i++) {
28462306a36Sopenharmony_ci		of_property_read_string_index(node, "clock-output-names",
28562306a36Sopenharmony_ci					      i, &clk_name);
28662306a36Sopenharmony_ci		init.num_parents = 1;
28762306a36Sopenharmony_ci		init.parent_names = &parent_name;
28862306a36Sopenharmony_ci		init.name = clk_name;
28962306a36Sopenharmony_ci		init.ops = &soc_desc->ops;
29062306a36Sopenharmony_ci		init.flags = 0;
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci		corediv[i].soc_desc = soc_desc;
29362306a36Sopenharmony_ci		corediv[i].desc = soc_desc->descs + i;
29462306a36Sopenharmony_ci		corediv[i].reg = base;
29562306a36Sopenharmony_ci		corediv[i].hw.init = &init;
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci		clks[i] = clk_register(NULL, &corediv[i].hw);
29862306a36Sopenharmony_ci		WARN_ON(IS_ERR(clks[i]));
29962306a36Sopenharmony_ci	}
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_ci	clk_data.clks = clks;
30262306a36Sopenharmony_ci	of_clk_add_provider(node, of_clk_src_onecell_get, &clk_data);
30362306a36Sopenharmony_ci	return;
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_cierr_free_clks:
30662306a36Sopenharmony_ci	kfree(clks);
30762306a36Sopenharmony_cierr_unmap:
30862306a36Sopenharmony_ci	iounmap(base);
30962306a36Sopenharmony_ci}
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_cistatic void __init armada370_corediv_clk_init(struct device_node *node)
31262306a36Sopenharmony_ci{
31362306a36Sopenharmony_ci	return mvebu_corediv_clk_init(node, &armada370_corediv_soc);
31462306a36Sopenharmony_ci}
31562306a36Sopenharmony_ciCLK_OF_DECLARE(armada370_corediv_clk, "marvell,armada-370-corediv-clock",
31662306a36Sopenharmony_ci	       armada370_corediv_clk_init);
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_cistatic void __init armada375_corediv_clk_init(struct device_node *node)
31962306a36Sopenharmony_ci{
32062306a36Sopenharmony_ci	return mvebu_corediv_clk_init(node, &armada375_corediv_soc);
32162306a36Sopenharmony_ci}
32262306a36Sopenharmony_ciCLK_OF_DECLARE(armada375_corediv_clk, "marvell,armada-375-corediv-clock",
32362306a36Sopenharmony_ci	       armada375_corediv_clk_init);
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_cistatic void __init armada380_corediv_clk_init(struct device_node *node)
32662306a36Sopenharmony_ci{
32762306a36Sopenharmony_ci	return mvebu_corediv_clk_init(node, &armada380_corediv_soc);
32862306a36Sopenharmony_ci}
32962306a36Sopenharmony_ciCLK_OF_DECLARE(armada380_corediv_clk, "marvell,armada-380-corediv-clock",
33062306a36Sopenharmony_ci	       armada380_corediv_clk_init);
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_cistatic void __init mv98dx3236_corediv_clk_init(struct device_node *node)
33362306a36Sopenharmony_ci{
33462306a36Sopenharmony_ci	return mvebu_corediv_clk_init(node, &mv98dx3236_corediv_soc);
33562306a36Sopenharmony_ci}
33662306a36Sopenharmony_ciCLK_OF_DECLARE(mv98dx3236_corediv_clk, "marvell,mv98dx3236-corediv-clock",
33762306a36Sopenharmony_ci	       mv98dx3236_corediv_clk_init);
338