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