xref: /kernel/linux/linux-5.10/drivers/clk/at91/pmc.c (revision 8c2ecf20)
18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *  Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com>
48c2ecf20Sopenharmony_ci */
58c2ecf20Sopenharmony_ci
68c2ecf20Sopenharmony_ci#include <linux/clk-provider.h>
78c2ecf20Sopenharmony_ci#include <linux/clkdev.h>
88c2ecf20Sopenharmony_ci#include <linux/clk/at91_pmc.h>
98c2ecf20Sopenharmony_ci#include <linux/of.h>
108c2ecf20Sopenharmony_ci#include <linux/mfd/syscon.h>
118c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
128c2ecf20Sopenharmony_ci#include <linux/regmap.h>
138c2ecf20Sopenharmony_ci#include <linux/syscore_ops.h>
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci#include <asm/proc-fns.h>
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci#include <dt-bindings/clock/at91.h>
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci#include "pmc.h"
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci#define PMC_MAX_IDS 128
228c2ecf20Sopenharmony_ci#define PMC_MAX_PCKS 8
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ciint of_at91_get_clk_range(struct device_node *np, const char *propname,
258c2ecf20Sopenharmony_ci			  struct clk_range *range)
268c2ecf20Sopenharmony_ci{
278c2ecf20Sopenharmony_ci	u32 min, max;
288c2ecf20Sopenharmony_ci	int ret;
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci	ret = of_property_read_u32_index(np, propname, 0, &min);
318c2ecf20Sopenharmony_ci	if (ret)
328c2ecf20Sopenharmony_ci		return ret;
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci	ret = of_property_read_u32_index(np, propname, 1, &max);
358c2ecf20Sopenharmony_ci	if (ret)
368c2ecf20Sopenharmony_ci		return ret;
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci	if (range) {
398c2ecf20Sopenharmony_ci		range->min = min;
408c2ecf20Sopenharmony_ci		range->max = max;
418c2ecf20Sopenharmony_ci	}
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci	return 0;
448c2ecf20Sopenharmony_ci}
458c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(of_at91_get_clk_range);
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_cistruct clk_hw *of_clk_hw_pmc_get(struct of_phandle_args *clkspec, void *data)
488c2ecf20Sopenharmony_ci{
498c2ecf20Sopenharmony_ci	unsigned int type = clkspec->args[0];
508c2ecf20Sopenharmony_ci	unsigned int idx = clkspec->args[1];
518c2ecf20Sopenharmony_ci	struct pmc_data *pmc_data = data;
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci	switch (type) {
548c2ecf20Sopenharmony_ci	case PMC_TYPE_CORE:
558c2ecf20Sopenharmony_ci		if (idx < pmc_data->ncore)
568c2ecf20Sopenharmony_ci			return pmc_data->chws[idx];
578c2ecf20Sopenharmony_ci		break;
588c2ecf20Sopenharmony_ci	case PMC_TYPE_SYSTEM:
598c2ecf20Sopenharmony_ci		if (idx < pmc_data->nsystem)
608c2ecf20Sopenharmony_ci			return pmc_data->shws[idx];
618c2ecf20Sopenharmony_ci		break;
628c2ecf20Sopenharmony_ci	case PMC_TYPE_PERIPHERAL:
638c2ecf20Sopenharmony_ci		if (idx < pmc_data->nperiph)
648c2ecf20Sopenharmony_ci			return pmc_data->phws[idx];
658c2ecf20Sopenharmony_ci		break;
668c2ecf20Sopenharmony_ci	case PMC_TYPE_GCK:
678c2ecf20Sopenharmony_ci		if (idx < pmc_data->ngck)
688c2ecf20Sopenharmony_ci			return pmc_data->ghws[idx];
698c2ecf20Sopenharmony_ci		break;
708c2ecf20Sopenharmony_ci	case PMC_TYPE_PROGRAMMABLE:
718c2ecf20Sopenharmony_ci		if (idx < pmc_data->npck)
728c2ecf20Sopenharmony_ci			return pmc_data->pchws[idx];
738c2ecf20Sopenharmony_ci		break;
748c2ecf20Sopenharmony_ci	default:
758c2ecf20Sopenharmony_ci		break;
768c2ecf20Sopenharmony_ci	}
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci	pr_err("%s: invalid type (%u) or index (%u)\n", __func__, type, idx);
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	return ERR_PTR(-EINVAL);
818c2ecf20Sopenharmony_ci}
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_cistruct pmc_data *pmc_data_allocate(unsigned int ncore, unsigned int nsystem,
848c2ecf20Sopenharmony_ci				   unsigned int nperiph, unsigned int ngck,
858c2ecf20Sopenharmony_ci				   unsigned int npck)
868c2ecf20Sopenharmony_ci{
878c2ecf20Sopenharmony_ci	unsigned int num_clks = ncore + nsystem + nperiph + ngck + npck;
888c2ecf20Sopenharmony_ci	struct pmc_data *pmc_data;
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci	pmc_data = kzalloc(struct_size(pmc_data, hwtable, num_clks),
918c2ecf20Sopenharmony_ci			   GFP_KERNEL);
928c2ecf20Sopenharmony_ci	if (!pmc_data)
938c2ecf20Sopenharmony_ci		return NULL;
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci	pmc_data->ncore = ncore;
968c2ecf20Sopenharmony_ci	pmc_data->chws = pmc_data->hwtable;
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci	pmc_data->nsystem = nsystem;
998c2ecf20Sopenharmony_ci	pmc_data->shws = pmc_data->chws + ncore;
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	pmc_data->nperiph = nperiph;
1028c2ecf20Sopenharmony_ci	pmc_data->phws = pmc_data->shws + nsystem;
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci	pmc_data->ngck = ngck;
1058c2ecf20Sopenharmony_ci	pmc_data->ghws = pmc_data->phws + nperiph;
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	pmc_data->npck = npck;
1088c2ecf20Sopenharmony_ci	pmc_data->pchws = pmc_data->ghws + ngck;
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	return pmc_data;
1118c2ecf20Sopenharmony_ci}
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci#ifdef CONFIG_PM
1148c2ecf20Sopenharmony_cistatic struct regmap *pmcreg;
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_cistatic u8 registered_ids[PMC_MAX_IDS];
1178c2ecf20Sopenharmony_cistatic u8 registered_pcks[PMC_MAX_PCKS];
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_cistatic struct
1208c2ecf20Sopenharmony_ci{
1218c2ecf20Sopenharmony_ci	u32 scsr;
1228c2ecf20Sopenharmony_ci	u32 pcsr0;
1238c2ecf20Sopenharmony_ci	u32 uckr;
1248c2ecf20Sopenharmony_ci	u32 mor;
1258c2ecf20Sopenharmony_ci	u32 mcfr;
1268c2ecf20Sopenharmony_ci	u32 pllar;
1278c2ecf20Sopenharmony_ci	u32 mckr;
1288c2ecf20Sopenharmony_ci	u32 usb;
1298c2ecf20Sopenharmony_ci	u32 imr;
1308c2ecf20Sopenharmony_ci	u32 pcsr1;
1318c2ecf20Sopenharmony_ci	u32 pcr[PMC_MAX_IDS];
1328c2ecf20Sopenharmony_ci	u32 audio_pll0;
1338c2ecf20Sopenharmony_ci	u32 audio_pll1;
1348c2ecf20Sopenharmony_ci	u32 pckr[PMC_MAX_PCKS];
1358c2ecf20Sopenharmony_ci} pmc_cache;
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci/*
1388c2ecf20Sopenharmony_ci * As Peripheral ID 0 is invalid on AT91 chips, the identifier is stored
1398c2ecf20Sopenharmony_ci * without alteration in the table, and 0 is for unused clocks.
1408c2ecf20Sopenharmony_ci */
1418c2ecf20Sopenharmony_civoid pmc_register_id(u8 id)
1428c2ecf20Sopenharmony_ci{
1438c2ecf20Sopenharmony_ci	int i;
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci	for (i = 0; i < PMC_MAX_IDS; i++) {
1468c2ecf20Sopenharmony_ci		if (registered_ids[i] == 0) {
1478c2ecf20Sopenharmony_ci			registered_ids[i] = id;
1488c2ecf20Sopenharmony_ci			break;
1498c2ecf20Sopenharmony_ci		}
1508c2ecf20Sopenharmony_ci		if (registered_ids[i] == id)
1518c2ecf20Sopenharmony_ci			break;
1528c2ecf20Sopenharmony_ci	}
1538c2ecf20Sopenharmony_ci}
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci/*
1568c2ecf20Sopenharmony_ci * As Programmable Clock 0 is valid on AT91 chips, there is an offset
1578c2ecf20Sopenharmony_ci * of 1 between the stored value and the real clock ID.
1588c2ecf20Sopenharmony_ci */
1598c2ecf20Sopenharmony_civoid pmc_register_pck(u8 pck)
1608c2ecf20Sopenharmony_ci{
1618c2ecf20Sopenharmony_ci	int i;
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci	for (i = 0; i < PMC_MAX_PCKS; i++) {
1648c2ecf20Sopenharmony_ci		if (registered_pcks[i] == 0) {
1658c2ecf20Sopenharmony_ci			registered_pcks[i] = pck + 1;
1668c2ecf20Sopenharmony_ci			break;
1678c2ecf20Sopenharmony_ci		}
1688c2ecf20Sopenharmony_ci		if (registered_pcks[i] == (pck + 1))
1698c2ecf20Sopenharmony_ci			break;
1708c2ecf20Sopenharmony_ci	}
1718c2ecf20Sopenharmony_ci}
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_cistatic int pmc_suspend(void)
1748c2ecf20Sopenharmony_ci{
1758c2ecf20Sopenharmony_ci	int i;
1768c2ecf20Sopenharmony_ci	u8 num;
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci	regmap_read(pmcreg, AT91_PMC_SCSR, &pmc_cache.scsr);
1798c2ecf20Sopenharmony_ci	regmap_read(pmcreg, AT91_PMC_PCSR, &pmc_cache.pcsr0);
1808c2ecf20Sopenharmony_ci	regmap_read(pmcreg, AT91_CKGR_UCKR, &pmc_cache.uckr);
1818c2ecf20Sopenharmony_ci	regmap_read(pmcreg, AT91_CKGR_MOR, &pmc_cache.mor);
1828c2ecf20Sopenharmony_ci	regmap_read(pmcreg, AT91_CKGR_MCFR, &pmc_cache.mcfr);
1838c2ecf20Sopenharmony_ci	regmap_read(pmcreg, AT91_CKGR_PLLAR, &pmc_cache.pllar);
1848c2ecf20Sopenharmony_ci	regmap_read(pmcreg, AT91_PMC_MCKR, &pmc_cache.mckr);
1858c2ecf20Sopenharmony_ci	regmap_read(pmcreg, AT91_PMC_USB, &pmc_cache.usb);
1868c2ecf20Sopenharmony_ci	regmap_read(pmcreg, AT91_PMC_IMR, &pmc_cache.imr);
1878c2ecf20Sopenharmony_ci	regmap_read(pmcreg, AT91_PMC_PCSR1, &pmc_cache.pcsr1);
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci	for (i = 0; registered_ids[i]; i++) {
1908c2ecf20Sopenharmony_ci		regmap_write(pmcreg, AT91_PMC_PCR,
1918c2ecf20Sopenharmony_ci			     (registered_ids[i] & AT91_PMC_PCR_PID_MASK));
1928c2ecf20Sopenharmony_ci		regmap_read(pmcreg, AT91_PMC_PCR,
1938c2ecf20Sopenharmony_ci			    &pmc_cache.pcr[registered_ids[i]]);
1948c2ecf20Sopenharmony_ci	}
1958c2ecf20Sopenharmony_ci	for (i = 0; registered_pcks[i]; i++) {
1968c2ecf20Sopenharmony_ci		num = registered_pcks[i] - 1;
1978c2ecf20Sopenharmony_ci		regmap_read(pmcreg, AT91_PMC_PCKR(num), &pmc_cache.pckr[num]);
1988c2ecf20Sopenharmony_ci	}
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci	return 0;
2018c2ecf20Sopenharmony_ci}
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_cistatic bool pmc_ready(unsigned int mask)
2048c2ecf20Sopenharmony_ci{
2058c2ecf20Sopenharmony_ci	unsigned int status;
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci	regmap_read(pmcreg, AT91_PMC_SR, &status);
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci	return ((status & mask) == mask) ? 1 : 0;
2108c2ecf20Sopenharmony_ci}
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_cistatic void pmc_resume(void)
2138c2ecf20Sopenharmony_ci{
2148c2ecf20Sopenharmony_ci	int i;
2158c2ecf20Sopenharmony_ci	u8 num;
2168c2ecf20Sopenharmony_ci	u32 tmp;
2178c2ecf20Sopenharmony_ci	u32 mask = AT91_PMC_MCKRDY | AT91_PMC_LOCKA;
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci	regmap_read(pmcreg, AT91_PMC_MCKR, &tmp);
2208c2ecf20Sopenharmony_ci	if (pmc_cache.mckr != tmp)
2218c2ecf20Sopenharmony_ci		pr_warn("MCKR was not configured properly by the firmware\n");
2228c2ecf20Sopenharmony_ci	regmap_read(pmcreg, AT91_CKGR_PLLAR, &tmp);
2238c2ecf20Sopenharmony_ci	if (pmc_cache.pllar != tmp)
2248c2ecf20Sopenharmony_ci		pr_warn("PLLAR was not configured properly by the firmware\n");
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci	regmap_write(pmcreg, AT91_PMC_SCER, pmc_cache.scsr);
2278c2ecf20Sopenharmony_ci	regmap_write(pmcreg, AT91_PMC_PCER, pmc_cache.pcsr0);
2288c2ecf20Sopenharmony_ci	regmap_write(pmcreg, AT91_CKGR_UCKR, pmc_cache.uckr);
2298c2ecf20Sopenharmony_ci	regmap_write(pmcreg, AT91_CKGR_MOR, pmc_cache.mor);
2308c2ecf20Sopenharmony_ci	regmap_write(pmcreg, AT91_CKGR_MCFR, pmc_cache.mcfr);
2318c2ecf20Sopenharmony_ci	regmap_write(pmcreg, AT91_PMC_USB, pmc_cache.usb);
2328c2ecf20Sopenharmony_ci	regmap_write(pmcreg, AT91_PMC_IMR, pmc_cache.imr);
2338c2ecf20Sopenharmony_ci	regmap_write(pmcreg, AT91_PMC_PCER1, pmc_cache.pcsr1);
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci	for (i = 0; registered_ids[i]; i++) {
2368c2ecf20Sopenharmony_ci		regmap_write(pmcreg, AT91_PMC_PCR,
2378c2ecf20Sopenharmony_ci			     pmc_cache.pcr[registered_ids[i]] |
2388c2ecf20Sopenharmony_ci			     AT91_PMC_PCR_CMD);
2398c2ecf20Sopenharmony_ci	}
2408c2ecf20Sopenharmony_ci	for (i = 0; registered_pcks[i]; i++) {
2418c2ecf20Sopenharmony_ci		num = registered_pcks[i] - 1;
2428c2ecf20Sopenharmony_ci		regmap_write(pmcreg, AT91_PMC_PCKR(num), pmc_cache.pckr[num]);
2438c2ecf20Sopenharmony_ci	}
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci	if (pmc_cache.uckr & AT91_PMC_UPLLEN)
2468c2ecf20Sopenharmony_ci		mask |= AT91_PMC_LOCKU;
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci	while (!pmc_ready(mask))
2498c2ecf20Sopenharmony_ci		cpu_relax();
2508c2ecf20Sopenharmony_ci}
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_cistatic struct syscore_ops pmc_syscore_ops = {
2538c2ecf20Sopenharmony_ci	.suspend = pmc_suspend,
2548c2ecf20Sopenharmony_ci	.resume = pmc_resume,
2558c2ecf20Sopenharmony_ci};
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_cistatic const struct of_device_id sama5d2_pmc_dt_ids[] = {
2588c2ecf20Sopenharmony_ci	{ .compatible = "atmel,sama5d2-pmc" },
2598c2ecf20Sopenharmony_ci	{ /* sentinel */ }
2608c2ecf20Sopenharmony_ci};
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_cistatic int __init pmc_register_ops(void)
2638c2ecf20Sopenharmony_ci{
2648c2ecf20Sopenharmony_ci	struct device_node *np;
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_ci	np = of_find_matching_node(NULL, sama5d2_pmc_dt_ids);
2678c2ecf20Sopenharmony_ci	if (!np)
2688c2ecf20Sopenharmony_ci		return -ENODEV;
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_ci	if (!of_device_is_available(np)) {
2718c2ecf20Sopenharmony_ci		of_node_put(np);
2728c2ecf20Sopenharmony_ci		return -ENODEV;
2738c2ecf20Sopenharmony_ci	}
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci	pmcreg = device_node_to_regmap(np);
2768c2ecf20Sopenharmony_ci	of_node_put(np);
2778c2ecf20Sopenharmony_ci	if (IS_ERR(pmcreg))
2788c2ecf20Sopenharmony_ci		return PTR_ERR(pmcreg);
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci	register_syscore_ops(&pmc_syscore_ops);
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_ci	return 0;
2838c2ecf20Sopenharmony_ci}
2848c2ecf20Sopenharmony_ci/* This has to happen before arch_initcall because of the tcb_clksrc driver */
2858c2ecf20Sopenharmony_cipostcore_initcall(pmc_register_ops);
2868c2ecf20Sopenharmony_ci#endif
287