162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci
362306a36Sopenharmony_ci#include <linux/delay.h>
462306a36Sopenharmony_ci#include <linux/clk-provider.h>
562306a36Sopenharmony_ci#include <linux/io.h>
662306a36Sopenharmony_ci#include <linux/of.h>
762306a36Sopenharmony_ci#include <linux/platform_device.h>
862306a36Sopenharmony_ci#include <dt-bindings/clock/en7523-clk.h>
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#define REG_PCI_CONTROL			0x88
1162306a36Sopenharmony_ci#define   REG_PCI_CONTROL_PERSTOUT	BIT(29)
1262306a36Sopenharmony_ci#define   REG_PCI_CONTROL_PERSTOUT1	BIT(26)
1362306a36Sopenharmony_ci#define   REG_PCI_CONTROL_REFCLK_EN1	BIT(22)
1462306a36Sopenharmony_ci#define REG_GSW_CLK_DIV_SEL		0x1b4
1562306a36Sopenharmony_ci#define REG_EMI_CLK_DIV_SEL		0x1b8
1662306a36Sopenharmony_ci#define REG_BUS_CLK_DIV_SEL		0x1bc
1762306a36Sopenharmony_ci#define REG_SPI_CLK_DIV_SEL		0x1c4
1862306a36Sopenharmony_ci#define REG_SPI_CLK_FREQ_SEL		0x1c8
1962306a36Sopenharmony_ci#define REG_NPU_CLK_DIV_SEL		0x1fc
2062306a36Sopenharmony_ci#define REG_CRYPTO_CLKSRC		0x200
2162306a36Sopenharmony_ci#define REG_RESET_CONTROL		0x834
2262306a36Sopenharmony_ci#define   REG_RESET_CONTROL_PCIEHB	BIT(29)
2362306a36Sopenharmony_ci#define   REG_RESET_CONTROL_PCIE1	BIT(27)
2462306a36Sopenharmony_ci#define   REG_RESET_CONTROL_PCIE2	BIT(26)
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_cistruct en_clk_desc {
2762306a36Sopenharmony_ci	int id;
2862306a36Sopenharmony_ci	const char *name;
2962306a36Sopenharmony_ci	u32 base_reg;
3062306a36Sopenharmony_ci	u8 base_bits;
3162306a36Sopenharmony_ci	u8 base_shift;
3262306a36Sopenharmony_ci	union {
3362306a36Sopenharmony_ci		const unsigned int *base_values;
3462306a36Sopenharmony_ci		unsigned int base_value;
3562306a36Sopenharmony_ci	};
3662306a36Sopenharmony_ci	size_t n_base_values;
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci	u16 div_reg;
3962306a36Sopenharmony_ci	u8 div_bits;
4062306a36Sopenharmony_ci	u8 div_shift;
4162306a36Sopenharmony_ci	u16 div_val0;
4262306a36Sopenharmony_ci	u8 div_step;
4362306a36Sopenharmony_ci};
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_cistruct en_clk_gate {
4662306a36Sopenharmony_ci	void __iomem *base;
4762306a36Sopenharmony_ci	struct clk_hw hw;
4862306a36Sopenharmony_ci};
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_cistatic const u32 gsw_base[] = { 400000000, 500000000 };
5162306a36Sopenharmony_cistatic const u32 emi_base[] = { 333000000, 400000000 };
5262306a36Sopenharmony_cistatic const u32 bus_base[] = { 500000000, 540000000 };
5362306a36Sopenharmony_cistatic const u32 slic_base[] = { 100000000, 3125000 };
5462306a36Sopenharmony_cistatic const u32 npu_base[] = { 333000000, 400000000, 500000000 };
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_cistatic const struct en_clk_desc en7523_base_clks[] = {
5762306a36Sopenharmony_ci	{
5862306a36Sopenharmony_ci		.id = EN7523_CLK_GSW,
5962306a36Sopenharmony_ci		.name = "gsw",
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci		.base_reg = REG_GSW_CLK_DIV_SEL,
6262306a36Sopenharmony_ci		.base_bits = 1,
6362306a36Sopenharmony_ci		.base_shift = 8,
6462306a36Sopenharmony_ci		.base_values = gsw_base,
6562306a36Sopenharmony_ci		.n_base_values = ARRAY_SIZE(gsw_base),
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci		.div_bits = 3,
6862306a36Sopenharmony_ci		.div_shift = 0,
6962306a36Sopenharmony_ci		.div_step = 1,
7062306a36Sopenharmony_ci	}, {
7162306a36Sopenharmony_ci		.id = EN7523_CLK_EMI,
7262306a36Sopenharmony_ci		.name = "emi",
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci		.base_reg = REG_EMI_CLK_DIV_SEL,
7562306a36Sopenharmony_ci		.base_bits = 1,
7662306a36Sopenharmony_ci		.base_shift = 8,
7762306a36Sopenharmony_ci		.base_values = emi_base,
7862306a36Sopenharmony_ci		.n_base_values = ARRAY_SIZE(emi_base),
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci		.div_bits = 3,
8162306a36Sopenharmony_ci		.div_shift = 0,
8262306a36Sopenharmony_ci		.div_step = 1,
8362306a36Sopenharmony_ci	}, {
8462306a36Sopenharmony_ci		.id = EN7523_CLK_BUS,
8562306a36Sopenharmony_ci		.name = "bus",
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci		.base_reg = REG_BUS_CLK_DIV_SEL,
8862306a36Sopenharmony_ci		.base_bits = 1,
8962306a36Sopenharmony_ci		.base_shift = 8,
9062306a36Sopenharmony_ci		.base_values = bus_base,
9162306a36Sopenharmony_ci		.n_base_values = ARRAY_SIZE(bus_base),
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci		.div_bits = 3,
9462306a36Sopenharmony_ci		.div_shift = 0,
9562306a36Sopenharmony_ci		.div_step = 1,
9662306a36Sopenharmony_ci	}, {
9762306a36Sopenharmony_ci		.id = EN7523_CLK_SLIC,
9862306a36Sopenharmony_ci		.name = "slic",
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci		.base_reg = REG_SPI_CLK_FREQ_SEL,
10162306a36Sopenharmony_ci		.base_bits = 1,
10262306a36Sopenharmony_ci		.base_shift = 0,
10362306a36Sopenharmony_ci		.base_values = slic_base,
10462306a36Sopenharmony_ci		.n_base_values = ARRAY_SIZE(slic_base),
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci		.div_reg = REG_SPI_CLK_DIV_SEL,
10762306a36Sopenharmony_ci		.div_bits = 5,
10862306a36Sopenharmony_ci		.div_shift = 24,
10962306a36Sopenharmony_ci		.div_val0 = 20,
11062306a36Sopenharmony_ci		.div_step = 2,
11162306a36Sopenharmony_ci	}, {
11262306a36Sopenharmony_ci		.id = EN7523_CLK_SPI,
11362306a36Sopenharmony_ci		.name = "spi",
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci		.base_reg = REG_SPI_CLK_DIV_SEL,
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci		.base_value = 400000000,
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci		.div_bits = 5,
12062306a36Sopenharmony_ci		.div_shift = 8,
12162306a36Sopenharmony_ci		.div_val0 = 40,
12262306a36Sopenharmony_ci		.div_step = 2,
12362306a36Sopenharmony_ci	}, {
12462306a36Sopenharmony_ci		.id = EN7523_CLK_NPU,
12562306a36Sopenharmony_ci		.name = "npu",
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci		.base_reg = REG_NPU_CLK_DIV_SEL,
12862306a36Sopenharmony_ci		.base_bits = 2,
12962306a36Sopenharmony_ci		.base_shift = 8,
13062306a36Sopenharmony_ci		.base_values = npu_base,
13162306a36Sopenharmony_ci		.n_base_values = ARRAY_SIZE(npu_base),
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci		.div_bits = 3,
13462306a36Sopenharmony_ci		.div_shift = 0,
13562306a36Sopenharmony_ci		.div_step = 1,
13662306a36Sopenharmony_ci	}, {
13762306a36Sopenharmony_ci		.id = EN7523_CLK_CRYPTO,
13862306a36Sopenharmony_ci		.name = "crypto",
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci		.base_reg = REG_CRYPTO_CLKSRC,
14162306a36Sopenharmony_ci		.base_bits = 1,
14262306a36Sopenharmony_ci		.base_shift = 8,
14362306a36Sopenharmony_ci		.base_values = emi_base,
14462306a36Sopenharmony_ci		.n_base_values = ARRAY_SIZE(emi_base),
14562306a36Sopenharmony_ci	}
14662306a36Sopenharmony_ci};
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_cistatic const struct of_device_id of_match_clk_en7523[] = {
14962306a36Sopenharmony_ci	{ .compatible = "airoha,en7523-scu", },
15062306a36Sopenharmony_ci	{ /* sentinel */ }
15162306a36Sopenharmony_ci};
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_cistatic unsigned int en7523_get_base_rate(void __iomem *base, unsigned int i)
15462306a36Sopenharmony_ci{
15562306a36Sopenharmony_ci	const struct en_clk_desc *desc = &en7523_base_clks[i];
15662306a36Sopenharmony_ci	u32 val;
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	if (!desc->base_bits)
15962306a36Sopenharmony_ci		return desc->base_value;
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	val = readl(base + desc->base_reg);
16262306a36Sopenharmony_ci	val >>= desc->base_shift;
16362306a36Sopenharmony_ci	val &= (1 << desc->base_bits) - 1;
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	if (val >= desc->n_base_values)
16662306a36Sopenharmony_ci		return 0;
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	return desc->base_values[val];
16962306a36Sopenharmony_ci}
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_cistatic u32 en7523_get_div(void __iomem *base, int i)
17262306a36Sopenharmony_ci{
17362306a36Sopenharmony_ci	const struct en_clk_desc *desc = &en7523_base_clks[i];
17462306a36Sopenharmony_ci	u32 reg, val;
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci	if (!desc->div_bits)
17762306a36Sopenharmony_ci		return 1;
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	reg = desc->div_reg ? desc->div_reg : desc->base_reg;
18062306a36Sopenharmony_ci	val = readl(base + reg);
18162306a36Sopenharmony_ci	val >>= desc->div_shift;
18262306a36Sopenharmony_ci	val &= (1 << desc->div_bits) - 1;
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci	if (!val && desc->div_val0)
18562306a36Sopenharmony_ci		return desc->div_val0;
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	return (val + 1) * desc->div_step;
18862306a36Sopenharmony_ci}
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_cistatic int en7523_pci_is_enabled(struct clk_hw *hw)
19162306a36Sopenharmony_ci{
19262306a36Sopenharmony_ci	struct en_clk_gate *cg = container_of(hw, struct en_clk_gate, hw);
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci	return !!(readl(cg->base + REG_PCI_CONTROL) & REG_PCI_CONTROL_REFCLK_EN1);
19562306a36Sopenharmony_ci}
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_cistatic int en7523_pci_prepare(struct clk_hw *hw)
19862306a36Sopenharmony_ci{
19962306a36Sopenharmony_ci	struct en_clk_gate *cg = container_of(hw, struct en_clk_gate, hw);
20062306a36Sopenharmony_ci	void __iomem *np_base = cg->base;
20162306a36Sopenharmony_ci	u32 val, mask;
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	/* Need to pull device low before reset */
20462306a36Sopenharmony_ci	val = readl(np_base + REG_PCI_CONTROL);
20562306a36Sopenharmony_ci	val &= ~(REG_PCI_CONTROL_PERSTOUT1 | REG_PCI_CONTROL_PERSTOUT);
20662306a36Sopenharmony_ci	writel(val, np_base + REG_PCI_CONTROL);
20762306a36Sopenharmony_ci	usleep_range(1000, 2000);
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci	/* Enable PCIe port 1 */
21062306a36Sopenharmony_ci	val |= REG_PCI_CONTROL_REFCLK_EN1;
21162306a36Sopenharmony_ci	writel(val, np_base + REG_PCI_CONTROL);
21262306a36Sopenharmony_ci	usleep_range(1000, 2000);
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	/* Reset to default */
21562306a36Sopenharmony_ci	val = readl(np_base + REG_RESET_CONTROL);
21662306a36Sopenharmony_ci	mask = REG_RESET_CONTROL_PCIE1 | REG_RESET_CONTROL_PCIE2 |
21762306a36Sopenharmony_ci	       REG_RESET_CONTROL_PCIEHB;
21862306a36Sopenharmony_ci	writel(val & ~mask, np_base + REG_RESET_CONTROL);
21962306a36Sopenharmony_ci	usleep_range(1000, 2000);
22062306a36Sopenharmony_ci	writel(val | mask, np_base + REG_RESET_CONTROL);
22162306a36Sopenharmony_ci	msleep(100);
22262306a36Sopenharmony_ci	writel(val & ~mask, np_base + REG_RESET_CONTROL);
22362306a36Sopenharmony_ci	usleep_range(5000, 10000);
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci	/* Release device */
22662306a36Sopenharmony_ci	mask = REG_PCI_CONTROL_PERSTOUT1 | REG_PCI_CONTROL_PERSTOUT;
22762306a36Sopenharmony_ci	val = readl(np_base + REG_PCI_CONTROL);
22862306a36Sopenharmony_ci	writel(val & ~mask, np_base + REG_PCI_CONTROL);
22962306a36Sopenharmony_ci	usleep_range(1000, 2000);
23062306a36Sopenharmony_ci	writel(val | mask, np_base + REG_PCI_CONTROL);
23162306a36Sopenharmony_ci	msleep(250);
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci	return 0;
23462306a36Sopenharmony_ci}
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_cistatic void en7523_pci_unprepare(struct clk_hw *hw)
23762306a36Sopenharmony_ci{
23862306a36Sopenharmony_ci	struct en_clk_gate *cg = container_of(hw, struct en_clk_gate, hw);
23962306a36Sopenharmony_ci	void __iomem *np_base = cg->base;
24062306a36Sopenharmony_ci	u32 val;
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci	val = readl(np_base + REG_PCI_CONTROL);
24362306a36Sopenharmony_ci	val &= ~REG_PCI_CONTROL_REFCLK_EN1;
24462306a36Sopenharmony_ci	writel(val, np_base + REG_PCI_CONTROL);
24562306a36Sopenharmony_ci}
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_cistatic struct clk_hw *en7523_register_pcie_clk(struct device *dev,
24862306a36Sopenharmony_ci					       void __iomem *np_base)
24962306a36Sopenharmony_ci{
25062306a36Sopenharmony_ci	static const struct clk_ops pcie_gate_ops = {
25162306a36Sopenharmony_ci		.is_enabled = en7523_pci_is_enabled,
25262306a36Sopenharmony_ci		.prepare = en7523_pci_prepare,
25362306a36Sopenharmony_ci		.unprepare = en7523_pci_unprepare,
25462306a36Sopenharmony_ci	};
25562306a36Sopenharmony_ci	struct clk_init_data init = {
25662306a36Sopenharmony_ci		.name = "pcie",
25762306a36Sopenharmony_ci		.ops = &pcie_gate_ops,
25862306a36Sopenharmony_ci	};
25962306a36Sopenharmony_ci	struct en_clk_gate *cg;
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci	cg = devm_kzalloc(dev, sizeof(*cg), GFP_KERNEL);
26262306a36Sopenharmony_ci	if (!cg)
26362306a36Sopenharmony_ci		return NULL;
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci	cg->base = np_base;
26662306a36Sopenharmony_ci	cg->hw.init = &init;
26762306a36Sopenharmony_ci	en7523_pci_unprepare(&cg->hw);
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci	if (clk_hw_register(dev, &cg->hw))
27062306a36Sopenharmony_ci		return NULL;
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci	return &cg->hw;
27362306a36Sopenharmony_ci}
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_cistatic void en7523_register_clocks(struct device *dev, struct clk_hw_onecell_data *clk_data,
27662306a36Sopenharmony_ci				   void __iomem *base, void __iomem *np_base)
27762306a36Sopenharmony_ci{
27862306a36Sopenharmony_ci	struct clk_hw *hw;
27962306a36Sopenharmony_ci	u32 rate;
28062306a36Sopenharmony_ci	int i;
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(en7523_base_clks); i++) {
28362306a36Sopenharmony_ci		const struct en_clk_desc *desc = &en7523_base_clks[i];
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci		rate = en7523_get_base_rate(base, i);
28662306a36Sopenharmony_ci		rate /= en7523_get_div(base, i);
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci		hw = clk_hw_register_fixed_rate(dev, desc->name, NULL, 0, rate);
28962306a36Sopenharmony_ci		if (IS_ERR(hw)) {
29062306a36Sopenharmony_ci			pr_err("Failed to register clk %s: %ld\n",
29162306a36Sopenharmony_ci			       desc->name, PTR_ERR(hw));
29262306a36Sopenharmony_ci			continue;
29362306a36Sopenharmony_ci		}
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci		clk_data->hws[desc->id] = hw;
29662306a36Sopenharmony_ci	}
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci	hw = en7523_register_pcie_clk(dev, np_base);
29962306a36Sopenharmony_ci	clk_data->hws[EN7523_CLK_PCIE] = hw;
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_ci	clk_data->num = EN7523_NUM_CLOCKS;
30262306a36Sopenharmony_ci}
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_cistatic int en7523_clk_probe(struct platform_device *pdev)
30562306a36Sopenharmony_ci{
30662306a36Sopenharmony_ci	struct device_node *node = pdev->dev.of_node;
30762306a36Sopenharmony_ci	struct clk_hw_onecell_data *clk_data;
30862306a36Sopenharmony_ci	void __iomem *base, *np_base;
30962306a36Sopenharmony_ci	int r;
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ci	base = devm_platform_ioremap_resource(pdev, 0);
31262306a36Sopenharmony_ci	if (IS_ERR(base))
31362306a36Sopenharmony_ci		return PTR_ERR(base);
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci	np_base = devm_platform_ioremap_resource(pdev, 1);
31662306a36Sopenharmony_ci	if (IS_ERR(np_base))
31762306a36Sopenharmony_ci		return PTR_ERR(np_base);
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci	clk_data = devm_kzalloc(&pdev->dev,
32062306a36Sopenharmony_ci				struct_size(clk_data, hws, EN7523_NUM_CLOCKS),
32162306a36Sopenharmony_ci				GFP_KERNEL);
32262306a36Sopenharmony_ci	if (!clk_data)
32362306a36Sopenharmony_ci		return -ENOMEM;
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci	en7523_register_clocks(&pdev->dev, clk_data, base, np_base);
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci	r = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
32862306a36Sopenharmony_ci	if (r)
32962306a36Sopenharmony_ci		dev_err(&pdev->dev,
33062306a36Sopenharmony_ci			"could not register clock provider: %s: %d\n",
33162306a36Sopenharmony_ci			pdev->name, r);
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci	return r;
33462306a36Sopenharmony_ci}
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_cistatic struct platform_driver clk_en7523_drv = {
33762306a36Sopenharmony_ci	.probe = en7523_clk_probe,
33862306a36Sopenharmony_ci	.driver = {
33962306a36Sopenharmony_ci		.name = "clk-en7523",
34062306a36Sopenharmony_ci		.of_match_table = of_match_clk_en7523,
34162306a36Sopenharmony_ci		.suppress_bind_attrs = true,
34262306a36Sopenharmony_ci	},
34362306a36Sopenharmony_ci};
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_cistatic int __init clk_en7523_init(void)
34662306a36Sopenharmony_ci{
34762306a36Sopenharmony_ci	return platform_driver_register(&clk_en7523_drv);
34862306a36Sopenharmony_ci}
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ciarch_initcall(clk_en7523_init);
351