162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *  Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com>
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#include <linux/clk.h>
762306a36Sopenharmony_ci#include <linux/clk-provider.h>
862306a36Sopenharmony_ci#include <linux/clkdev.h>
962306a36Sopenharmony_ci#include <linux/clk/at91_pmc.h>
1062306a36Sopenharmony_ci#include <linux/of.h>
1162306a36Sopenharmony_ci#include <linux/of_address.h>
1262306a36Sopenharmony_ci#include <linux/mfd/syscon.h>
1362306a36Sopenharmony_ci#include <linux/platform_device.h>
1462306a36Sopenharmony_ci#include <linux/regmap.h>
1562306a36Sopenharmony_ci#include <linux/syscore_ops.h>
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci#include <asm/proc-fns.h>
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#include "pmc.h"
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci#define PMC_MAX_IDS 128
2262306a36Sopenharmony_ci#define PMC_MAX_PCKS 8
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ciint of_at91_get_clk_range(struct device_node *np, const char *propname,
2562306a36Sopenharmony_ci			  struct clk_range *range)
2662306a36Sopenharmony_ci{
2762306a36Sopenharmony_ci	u32 min, max;
2862306a36Sopenharmony_ci	int ret;
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci	ret = of_property_read_u32_index(np, propname, 0, &min);
3162306a36Sopenharmony_ci	if (ret)
3262306a36Sopenharmony_ci		return ret;
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci	ret = of_property_read_u32_index(np, propname, 1, &max);
3562306a36Sopenharmony_ci	if (ret)
3662306a36Sopenharmony_ci		return ret;
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci	if (range) {
3962306a36Sopenharmony_ci		range->min = min;
4062306a36Sopenharmony_ci		range->max = max;
4162306a36Sopenharmony_ci	}
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci	return 0;
4462306a36Sopenharmony_ci}
4562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(of_at91_get_clk_range);
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_cistruct clk_hw *of_clk_hw_pmc_get(struct of_phandle_args *clkspec, void *data)
4862306a36Sopenharmony_ci{
4962306a36Sopenharmony_ci	unsigned int type = clkspec->args[0];
5062306a36Sopenharmony_ci	unsigned int idx = clkspec->args[1];
5162306a36Sopenharmony_ci	struct pmc_data *pmc_data = data;
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci	switch (type) {
5462306a36Sopenharmony_ci	case PMC_TYPE_CORE:
5562306a36Sopenharmony_ci		if (idx < pmc_data->ncore)
5662306a36Sopenharmony_ci			return pmc_data->chws[idx];
5762306a36Sopenharmony_ci		break;
5862306a36Sopenharmony_ci	case PMC_TYPE_SYSTEM:
5962306a36Sopenharmony_ci		if (idx < pmc_data->nsystem)
6062306a36Sopenharmony_ci			return pmc_data->shws[idx];
6162306a36Sopenharmony_ci		break;
6262306a36Sopenharmony_ci	case PMC_TYPE_PERIPHERAL:
6362306a36Sopenharmony_ci		if (idx < pmc_data->nperiph)
6462306a36Sopenharmony_ci			return pmc_data->phws[idx];
6562306a36Sopenharmony_ci		break;
6662306a36Sopenharmony_ci	case PMC_TYPE_GCK:
6762306a36Sopenharmony_ci		if (idx < pmc_data->ngck)
6862306a36Sopenharmony_ci			return pmc_data->ghws[idx];
6962306a36Sopenharmony_ci		break;
7062306a36Sopenharmony_ci	case PMC_TYPE_PROGRAMMABLE:
7162306a36Sopenharmony_ci		if (idx < pmc_data->npck)
7262306a36Sopenharmony_ci			return pmc_data->pchws[idx];
7362306a36Sopenharmony_ci		break;
7462306a36Sopenharmony_ci	default:
7562306a36Sopenharmony_ci		break;
7662306a36Sopenharmony_ci	}
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	pr_err("%s: invalid type (%u) or index (%u)\n", __func__, type, idx);
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	return ERR_PTR(-EINVAL);
8162306a36Sopenharmony_ci}
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_cistruct pmc_data *pmc_data_allocate(unsigned int ncore, unsigned int nsystem,
8462306a36Sopenharmony_ci				   unsigned int nperiph, unsigned int ngck,
8562306a36Sopenharmony_ci				   unsigned int npck)
8662306a36Sopenharmony_ci{
8762306a36Sopenharmony_ci	unsigned int num_clks = ncore + nsystem + nperiph + ngck + npck;
8862306a36Sopenharmony_ci	struct pmc_data *pmc_data;
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	pmc_data = kzalloc(struct_size(pmc_data, hwtable, num_clks),
9162306a36Sopenharmony_ci			   GFP_KERNEL);
9262306a36Sopenharmony_ci	if (!pmc_data)
9362306a36Sopenharmony_ci		return NULL;
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	pmc_data->ncore = ncore;
9662306a36Sopenharmony_ci	pmc_data->chws = pmc_data->hwtable;
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	pmc_data->nsystem = nsystem;
9962306a36Sopenharmony_ci	pmc_data->shws = pmc_data->chws + ncore;
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	pmc_data->nperiph = nperiph;
10262306a36Sopenharmony_ci	pmc_data->phws = pmc_data->shws + nsystem;
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	pmc_data->ngck = ngck;
10562306a36Sopenharmony_ci	pmc_data->ghws = pmc_data->phws + nperiph;
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	pmc_data->npck = npck;
10862306a36Sopenharmony_ci	pmc_data->pchws = pmc_data->ghws + ngck;
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	return pmc_data;
11162306a36Sopenharmony_ci}
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci#ifdef CONFIG_PM
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci/* Address in SECURAM that say if we suspend to backup mode. */
11662306a36Sopenharmony_cistatic void __iomem *at91_pmc_backup_suspend;
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_cistatic int at91_pmc_suspend(void)
11962306a36Sopenharmony_ci{
12062306a36Sopenharmony_ci	unsigned int backup;
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	if (!at91_pmc_backup_suspend)
12362306a36Sopenharmony_ci		return 0;
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	backup = readl_relaxed(at91_pmc_backup_suspend);
12662306a36Sopenharmony_ci	if (!backup)
12762306a36Sopenharmony_ci		return 0;
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	return clk_save_context();
13062306a36Sopenharmony_ci}
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_cistatic void at91_pmc_resume(void)
13362306a36Sopenharmony_ci{
13462306a36Sopenharmony_ci	unsigned int backup;
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	if (!at91_pmc_backup_suspend)
13762306a36Sopenharmony_ci		return;
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	backup = readl_relaxed(at91_pmc_backup_suspend);
14062306a36Sopenharmony_ci	if (!backup)
14162306a36Sopenharmony_ci		return;
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	clk_restore_context();
14462306a36Sopenharmony_ci}
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_cistatic struct syscore_ops pmc_syscore_ops = {
14762306a36Sopenharmony_ci	.suspend = at91_pmc_suspend,
14862306a36Sopenharmony_ci	.resume = at91_pmc_resume,
14962306a36Sopenharmony_ci};
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_cistatic const struct of_device_id pmc_dt_ids[] = {
15262306a36Sopenharmony_ci	{ .compatible = "atmel,sama5d2-pmc" },
15362306a36Sopenharmony_ci	{ .compatible = "microchip,sama7g5-pmc", },
15462306a36Sopenharmony_ci	{ /* sentinel */ }
15562306a36Sopenharmony_ci};
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_cistatic int __init pmc_register_ops(void)
15862306a36Sopenharmony_ci{
15962306a36Sopenharmony_ci	struct device_node *np;
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	np = of_find_matching_node(NULL, pmc_dt_ids);
16262306a36Sopenharmony_ci	if (!np)
16362306a36Sopenharmony_ci		return -ENODEV;
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	if (!of_device_is_available(np)) {
16662306a36Sopenharmony_ci		of_node_put(np);
16762306a36Sopenharmony_ci		return -ENODEV;
16862306a36Sopenharmony_ci	}
16962306a36Sopenharmony_ci	of_node_put(np);
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	np = of_find_compatible_node(NULL, NULL, "atmel,sama5d2-securam");
17262306a36Sopenharmony_ci	if (!np)
17362306a36Sopenharmony_ci		return -ENODEV;
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci	if (!of_device_is_available(np)) {
17662306a36Sopenharmony_ci		of_node_put(np);
17762306a36Sopenharmony_ci		return -ENODEV;
17862306a36Sopenharmony_ci	}
17962306a36Sopenharmony_ci	of_node_put(np);
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci	at91_pmc_backup_suspend = of_iomap(np, 0);
18262306a36Sopenharmony_ci	if (!at91_pmc_backup_suspend) {
18362306a36Sopenharmony_ci		pr_warn("%s(): unable to map securam\n", __func__);
18462306a36Sopenharmony_ci		return -ENOMEM;
18562306a36Sopenharmony_ci	}
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	register_syscore_ops(&pmc_syscore_ops);
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	return 0;
19062306a36Sopenharmony_ci}
19162306a36Sopenharmony_ci/* This has to happen before arch_initcall because of the tcb_clksrc driver */
19262306a36Sopenharmony_cipostcore_initcall(pmc_register_ops);
19362306a36Sopenharmony_ci#endif
194