xref: /openbmc/linux/drivers/clk/at91/pmc.c (revision 5df4cd90)
12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
20ad6125bSBoris BREZILLON /*
30ad6125bSBoris BREZILLON  *  Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com>
40ad6125bSBoris BREZILLON  */
50ad6125bSBoris BREZILLON 
636971566SClaudiu Beznea #include <linux/clk.h>
70ad6125bSBoris BREZILLON #include <linux/clk-provider.h>
80ad6125bSBoris BREZILLON #include <linux/clkdev.h>
90ad6125bSBoris BREZILLON #include <linux/clk/at91_pmc.h>
100ad6125bSBoris BREZILLON #include <linux/of.h>
114d21be86SClaudiu Beznea #include <linux/of_address.h>
12863a81c3SBoris Brezillon #include <linux/mfd/syscon.h>
13b3b02eacSAlexandre Belloni #include <linux/platform_device.h>
141bdf0232SBoris Brezillon #include <linux/regmap.h>
15b3b02eacSAlexandre Belloni #include <linux/syscore_ops.h>
160ad6125bSBoris BREZILLON 
170ad6125bSBoris BREZILLON #include <asm/proc-fns.h>
180ad6125bSBoris BREZILLON 
190ad6125bSBoris BREZILLON #include "pmc.h"
200ad6125bSBoris BREZILLON 
21b3b02eacSAlexandre Belloni #define PMC_MAX_IDS 128
2213967beaSRomain Izard #define PMC_MAX_PCKS 8
23b3b02eacSAlexandre Belloni 
of_at91_get_clk_range(struct device_node * np,const char * propname,struct clk_range * range)240ad6125bSBoris BREZILLON int of_at91_get_clk_range(struct device_node *np, const char *propname,
250ad6125bSBoris BREZILLON 			  struct clk_range *range)
260ad6125bSBoris BREZILLON {
270ad6125bSBoris BREZILLON 	u32 min, max;
280ad6125bSBoris BREZILLON 	int ret;
290ad6125bSBoris BREZILLON 
300ad6125bSBoris BREZILLON 	ret = of_property_read_u32_index(np, propname, 0, &min);
310ad6125bSBoris BREZILLON 	if (ret)
320ad6125bSBoris BREZILLON 		return ret;
330ad6125bSBoris BREZILLON 
340ad6125bSBoris BREZILLON 	ret = of_property_read_u32_index(np, propname, 1, &max);
350ad6125bSBoris BREZILLON 	if (ret)
360ad6125bSBoris BREZILLON 		return ret;
370ad6125bSBoris BREZILLON 
380ad6125bSBoris BREZILLON 	if (range) {
390ad6125bSBoris BREZILLON 		range->min = min;
400ad6125bSBoris BREZILLON 		range->max = max;
410ad6125bSBoris BREZILLON 	}
420ad6125bSBoris BREZILLON 
430ad6125bSBoris BREZILLON 	return 0;
440ad6125bSBoris BREZILLON }
450ad6125bSBoris BREZILLON EXPORT_SYMBOL_GPL(of_at91_get_clk_range);
46b3b02eacSAlexandre Belloni 
of_clk_hw_pmc_get(struct of_phandle_args * clkspec,void * data)47d387ff54SAlexandre Belloni struct clk_hw *of_clk_hw_pmc_get(struct of_phandle_args *clkspec, void *data)
48d387ff54SAlexandre Belloni {
49d387ff54SAlexandre Belloni 	unsigned int type = clkspec->args[0];
50d387ff54SAlexandre Belloni 	unsigned int idx = clkspec->args[1];
51d387ff54SAlexandre Belloni 	struct pmc_data *pmc_data = data;
52d387ff54SAlexandre Belloni 
53d387ff54SAlexandre Belloni 	switch (type) {
54d387ff54SAlexandre Belloni 	case PMC_TYPE_CORE:
55d387ff54SAlexandre Belloni 		if (idx < pmc_data->ncore)
56d387ff54SAlexandre Belloni 			return pmc_data->chws[idx];
57d387ff54SAlexandre Belloni 		break;
58d387ff54SAlexandre Belloni 	case PMC_TYPE_SYSTEM:
59d387ff54SAlexandre Belloni 		if (idx < pmc_data->nsystem)
60d387ff54SAlexandre Belloni 			return pmc_data->shws[idx];
61d387ff54SAlexandre Belloni 		break;
62d387ff54SAlexandre Belloni 	case PMC_TYPE_PERIPHERAL:
63d387ff54SAlexandre Belloni 		if (idx < pmc_data->nperiph)
64d387ff54SAlexandre Belloni 			return pmc_data->phws[idx];
65d387ff54SAlexandre Belloni 		break;
66d387ff54SAlexandre Belloni 	case PMC_TYPE_GCK:
67d387ff54SAlexandre Belloni 		if (idx < pmc_data->ngck)
68d387ff54SAlexandre Belloni 			return pmc_data->ghws[idx];
69d387ff54SAlexandre Belloni 		break;
7099767cd4SMichał Mirosław 	case PMC_TYPE_PROGRAMMABLE:
7199767cd4SMichał Mirosław 		if (idx < pmc_data->npck)
7299767cd4SMichał Mirosław 			return pmc_data->pchws[idx];
7399767cd4SMichał Mirosław 		break;
74d387ff54SAlexandre Belloni 	default:
75d387ff54SAlexandre Belloni 		break;
76d387ff54SAlexandre Belloni 	}
77d387ff54SAlexandre Belloni 
78d387ff54SAlexandre Belloni 	pr_err("%s: invalid type (%u) or index (%u)\n", __func__, type, idx);
79d387ff54SAlexandre Belloni 
80d387ff54SAlexandre Belloni 	return ERR_PTR(-EINVAL);
81d387ff54SAlexandre Belloni }
82d387ff54SAlexandre Belloni 
pmc_data_allocate(unsigned int ncore,unsigned int nsystem,unsigned int nperiph,unsigned int ngck,unsigned int npck)83b00cd8e4SAlexandre Belloni struct pmc_data *pmc_data_allocate(unsigned int ncore, unsigned int nsystem,
8499767cd4SMichał Mirosław 				   unsigned int nperiph, unsigned int ngck,
8599767cd4SMichał Mirosław 				   unsigned int npck)
86b00cd8e4SAlexandre Belloni {
8799767cd4SMichał Mirosław 	unsigned int num_clks = ncore + nsystem + nperiph + ngck + npck;
887425f246SMichał Mirosław 	struct pmc_data *pmc_data;
89b00cd8e4SAlexandre Belloni 
907425f246SMichał Mirosław 	pmc_data = kzalloc(struct_size(pmc_data, hwtable, num_clks),
917425f246SMichał Mirosław 			   GFP_KERNEL);
92b00cd8e4SAlexandre Belloni 	if (!pmc_data)
93b00cd8e4SAlexandre Belloni 		return NULL;
94b00cd8e4SAlexandre Belloni 
95b00cd8e4SAlexandre Belloni 	pmc_data->ncore = ncore;
967425f246SMichał Mirosław 	pmc_data->chws = pmc_data->hwtable;
97b00cd8e4SAlexandre Belloni 
98b00cd8e4SAlexandre Belloni 	pmc_data->nsystem = nsystem;
997425f246SMichał Mirosław 	pmc_data->shws = pmc_data->chws + ncore;
100b00cd8e4SAlexandre Belloni 
101b00cd8e4SAlexandre Belloni 	pmc_data->nperiph = nperiph;
1027425f246SMichał Mirosław 	pmc_data->phws = pmc_data->shws + nsystem;
103b00cd8e4SAlexandre Belloni 
104b00cd8e4SAlexandre Belloni 	pmc_data->ngck = ngck;
1057425f246SMichał Mirosław 	pmc_data->ghws = pmc_data->phws + nperiph;
106b00cd8e4SAlexandre Belloni 
10799767cd4SMichał Mirosław 	pmc_data->npck = npck;
10899767cd4SMichał Mirosław 	pmc_data->pchws = pmc_data->ghws + ngck;
10999767cd4SMichał Mirosław 
110b00cd8e4SAlexandre Belloni 	return pmc_data;
111b00cd8e4SAlexandre Belloni }
112b00cd8e4SAlexandre Belloni 
113b3b02eacSAlexandre Belloni #ifdef CONFIG_PM
1144d21be86SClaudiu Beznea 
1154d21be86SClaudiu Beznea /* Address in SECURAM that say if we suspend to backup mode. */
1164d21be86SClaudiu Beznea static void __iomem *at91_pmc_backup_suspend;
1174d21be86SClaudiu Beznea 
at91_pmc_suspend(void)11836971566SClaudiu Beznea static int at91_pmc_suspend(void)
119b3b02eacSAlexandre Belloni {
1204d21be86SClaudiu Beznea 	unsigned int backup;
1214d21be86SClaudiu Beznea 
1224d21be86SClaudiu Beznea 	if (!at91_pmc_backup_suspend)
1234d21be86SClaudiu Beznea 		return 0;
1244d21be86SClaudiu Beznea 
1254d21be86SClaudiu Beznea 	backup = readl_relaxed(at91_pmc_backup_suspend);
1264d21be86SClaudiu Beznea 	if (!backup)
1274d21be86SClaudiu Beznea 		return 0;
1284d21be86SClaudiu Beznea 
12936971566SClaudiu Beznea 	return clk_save_context();
13036971566SClaudiu Beznea }
131b3b02eacSAlexandre Belloni 
at91_pmc_resume(void)13236971566SClaudiu Beznea static void at91_pmc_resume(void)
133b3b02eacSAlexandre Belloni {
1344d21be86SClaudiu Beznea 	unsigned int backup;
1354d21be86SClaudiu Beznea 
1364d21be86SClaudiu Beznea 	if (!at91_pmc_backup_suspend)
1374d21be86SClaudiu Beznea 		return;
1384d21be86SClaudiu Beznea 
1394d21be86SClaudiu Beznea 	backup = readl_relaxed(at91_pmc_backup_suspend);
1404d21be86SClaudiu Beznea 	if (!backup)
1414d21be86SClaudiu Beznea 		return;
1424d21be86SClaudiu Beznea 
14336971566SClaudiu Beznea 	clk_restore_context();
144b3b02eacSAlexandre Belloni }
145b3b02eacSAlexandre Belloni 
146b3b02eacSAlexandre Belloni static struct syscore_ops pmc_syscore_ops = {
14736971566SClaudiu Beznea 	.suspend = at91_pmc_suspend,
14836971566SClaudiu Beznea 	.resume = at91_pmc_resume,
149b3b02eacSAlexandre Belloni };
150b3b02eacSAlexandre Belloni 
151*5df4cd90SClaudiu Beznea static const struct of_device_id pmc_dt_ids[] = {
152b3b02eacSAlexandre Belloni 	{ .compatible = "atmel,sama5d2-pmc" },
153*5df4cd90SClaudiu Beznea 	{ .compatible = "microchip,sama7g5-pmc", },
154b3b02eacSAlexandre Belloni 	{ /* sentinel */ }
155b3b02eacSAlexandre Belloni };
156b3b02eacSAlexandre Belloni 
pmc_register_ops(void)157b3b02eacSAlexandre Belloni static int __init pmc_register_ops(void)
158b3b02eacSAlexandre Belloni {
159b3b02eacSAlexandre Belloni 	struct device_node *np;
160b3b02eacSAlexandre Belloni 
161*5df4cd90SClaudiu Beznea 	np = of_find_matching_node(NULL, pmc_dt_ids);
162f6363c43SClaudiu Beznea 	if (!np)
163f6363c43SClaudiu Beznea 		return -ENODEV;
164b3b02eacSAlexandre Belloni 
165c405f5c1SClément Léger 	if (!of_device_is_available(np)) {
166c405f5c1SClément Léger 		of_node_put(np);
167c405f5c1SClément Léger 		return -ENODEV;
168c405f5c1SClément Léger 	}
169e218325fSClaudiu Beznea 	of_node_put(np);
170b3b02eacSAlexandre Belloni 
1714d21be86SClaudiu Beznea 	np = of_find_compatible_node(NULL, NULL, "atmel,sama5d2-securam");
1724d21be86SClaudiu Beznea 	if (!np)
1734d21be86SClaudiu Beznea 		return -ENODEV;
1744d21be86SClaudiu Beznea 
1754d21be86SClaudiu Beznea 	if (!of_device_is_available(np)) {
1764d21be86SClaudiu Beznea 		of_node_put(np);
1774d21be86SClaudiu Beznea 		return -ENODEV;
1784d21be86SClaudiu Beznea 	}
1794d21be86SClaudiu Beznea 	of_node_put(np);
1804d21be86SClaudiu Beznea 
1814d21be86SClaudiu Beznea 	at91_pmc_backup_suspend = of_iomap(np, 0);
1824d21be86SClaudiu Beznea 	if (!at91_pmc_backup_suspend) {
1834d21be86SClaudiu Beznea 		pr_warn("%s(): unable to map securam\n", __func__);
1844d21be86SClaudiu Beznea 		return -ENOMEM;
1854d21be86SClaudiu Beznea 	}
1864d21be86SClaudiu Beznea 
187b3b02eacSAlexandre Belloni 	register_syscore_ops(&pmc_syscore_ops);
188b3b02eacSAlexandre Belloni 
189b3b02eacSAlexandre Belloni 	return 0;
190b3b02eacSAlexandre Belloni }
191b3b02eacSAlexandre Belloni /* This has to happen before arch_initcall because of the tcb_clksrc driver */
192b3b02eacSAlexandre Belloni postcore_initcall(pmc_register_ops);
193b3b02eacSAlexandre Belloni #endif
194