2 * Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
11 #include <linux/clk-provider.h>
12 #include <linux/clkdev.h>
13 #include <linux/clk/at91_pmc.h>
15 #include <linux/mfd/syscon.h>
16 #include <linux/platform_device.h>
17 #include <linux/regmap.h>
18 #include <linux/syscore_ops.h>
20 #include <asm/proc-fns.h>
22 #include <dt-bindings/clock/at91.h>
26 #define PMC_MAX_IDS 128
27 #define PMC_MAX_PCKS 8
29 int of_at91_get_clk_range(struct device_node
*np
, const char *propname
,
30 struct clk_range
*range
)
35 ret
= of_property_read_u32_index(np
, propname
, 0, &min
);
39 ret
= of_property_read_u32_index(np
, propname
, 1, &max
);
50 EXPORT_SYMBOL_GPL(of_at91_get_clk_range
);
52 struct clk_hw
*of_clk_hw_pmc_get(struct of_phandle_args
*clkspec
, void *data
)
54 unsigned int type
= clkspec
->args
[0];
55 unsigned int idx
= clkspec
->args
[1];
56 struct pmc_data
*pmc_data
= data
;
60 if (idx
< pmc_data
->ncore
)
61 return pmc_data
->chws
[idx
];
64 if (idx
< pmc_data
->nsystem
)
65 return pmc_data
->shws
[idx
];
67 case PMC_TYPE_PERIPHERAL
:
68 if (idx
< pmc_data
->nperiph
)
69 return pmc_data
->phws
[idx
];
72 if (idx
< pmc_data
->ngck
)
73 return pmc_data
->ghws
[idx
];
79 pr_err("%s: invalid type (%u) or index (%u)\n", __func__
, type
, idx
);
81 return ERR_PTR(-EINVAL
);
84 void pmc_data_free(struct pmc_data
*pmc_data
)
86 kfree(pmc_data
->chws
);
87 kfree(pmc_data
->shws
);
88 kfree(pmc_data
->phws
);
89 kfree(pmc_data
->ghws
);
92 struct pmc_data
*pmc_data_allocate(unsigned int ncore
, unsigned int nsystem
,
93 unsigned int nperiph
, unsigned int ngck
)
95 struct pmc_data
*pmc_data
= kzalloc(sizeof(*pmc_data
), GFP_KERNEL
);
100 pmc_data
->ncore
= ncore
;
101 pmc_data
->chws
= kcalloc(ncore
, sizeof(struct clk_hw
*), GFP_KERNEL
);
105 pmc_data
->nsystem
= nsystem
;
106 pmc_data
->shws
= kcalloc(nsystem
, sizeof(struct clk_hw
*), GFP_KERNEL
);
110 pmc_data
->nperiph
= nperiph
;
111 pmc_data
->phws
= kcalloc(nperiph
, sizeof(struct clk_hw
*), GFP_KERNEL
);
115 pmc_data
->ngck
= ngck
;
116 pmc_data
->ghws
= kcalloc(ngck
, sizeof(struct clk_hw
*), GFP_KERNEL
);
123 pmc_data_free(pmc_data
);
129 static struct regmap
*pmcreg
;
131 static u8 registered_ids
[PMC_MAX_IDS
];
132 static u8 registered_pcks
[PMC_MAX_PCKS
];
146 u32 pcr
[PMC_MAX_IDS
];
149 u32 pckr
[PMC_MAX_PCKS
];
153 * As Peripheral ID 0 is invalid on AT91 chips, the identifier is stored
154 * without alteration in the table, and 0 is for unused clocks.
156 void pmc_register_id(u8 id
)
160 for (i
= 0; i
< PMC_MAX_IDS
; i
++) {
161 if (registered_ids
[i
] == 0) {
162 registered_ids
[i
] = id
;
165 if (registered_ids
[i
] == id
)
171 * As Programmable Clock 0 is valid on AT91 chips, there is an offset
172 * of 1 between the stored value and the real clock ID.
174 void pmc_register_pck(u8 pck
)
178 for (i
= 0; i
< PMC_MAX_PCKS
; i
++) {
179 if (registered_pcks
[i
] == 0) {
180 registered_pcks
[i
] = pck
+ 1;
183 if (registered_pcks
[i
] == (pck
+ 1))
188 static int pmc_suspend(void)
193 regmap_read(pmcreg
, AT91_PMC_SCSR
, &pmc_cache
.scsr
);
194 regmap_read(pmcreg
, AT91_PMC_PCSR
, &pmc_cache
.pcsr0
);
195 regmap_read(pmcreg
, AT91_CKGR_UCKR
, &pmc_cache
.uckr
);
196 regmap_read(pmcreg
, AT91_CKGR_MOR
, &pmc_cache
.mor
);
197 regmap_read(pmcreg
, AT91_CKGR_MCFR
, &pmc_cache
.mcfr
);
198 regmap_read(pmcreg
, AT91_CKGR_PLLAR
, &pmc_cache
.pllar
);
199 regmap_read(pmcreg
, AT91_PMC_MCKR
, &pmc_cache
.mckr
);
200 regmap_read(pmcreg
, AT91_PMC_USB
, &pmc_cache
.usb
);
201 regmap_read(pmcreg
, AT91_PMC_IMR
, &pmc_cache
.imr
);
202 regmap_read(pmcreg
, AT91_PMC_PCSR1
, &pmc_cache
.pcsr1
);
204 for (i
= 0; registered_ids
[i
]; i
++) {
205 regmap_write(pmcreg
, AT91_PMC_PCR
,
206 (registered_ids
[i
] & AT91_PMC_PCR_PID_MASK
));
207 regmap_read(pmcreg
, AT91_PMC_PCR
,
208 &pmc_cache
.pcr
[registered_ids
[i
]]);
210 for (i
= 0; registered_pcks
[i
]; i
++) {
211 num
= registered_pcks
[i
] - 1;
212 regmap_read(pmcreg
, AT91_PMC_PCKR(num
), &pmc_cache
.pckr
[num
]);
218 static bool pmc_ready(unsigned int mask
)
222 regmap_read(pmcreg
, AT91_PMC_SR
, &status
);
224 return ((status
& mask
) == mask
) ? 1 : 0;
227 static void pmc_resume(void)
232 u32 mask
= AT91_PMC_MCKRDY
| AT91_PMC_LOCKA
;
234 regmap_read(pmcreg
, AT91_PMC_MCKR
, &tmp
);
235 if (pmc_cache
.mckr
!= tmp
)
236 pr_warn("MCKR was not configured properly by the firmware\n");
237 regmap_read(pmcreg
, AT91_CKGR_PLLAR
, &tmp
);
238 if (pmc_cache
.pllar
!= tmp
)
239 pr_warn("PLLAR was not configured properly by the firmware\n");
241 regmap_write(pmcreg
, AT91_PMC_SCER
, pmc_cache
.scsr
);
242 regmap_write(pmcreg
, AT91_PMC_PCER
, pmc_cache
.pcsr0
);
243 regmap_write(pmcreg
, AT91_CKGR_UCKR
, pmc_cache
.uckr
);
244 regmap_write(pmcreg
, AT91_CKGR_MOR
, pmc_cache
.mor
);
245 regmap_write(pmcreg
, AT91_CKGR_MCFR
, pmc_cache
.mcfr
);
246 regmap_write(pmcreg
, AT91_PMC_USB
, pmc_cache
.usb
);
247 regmap_write(pmcreg
, AT91_PMC_IMR
, pmc_cache
.imr
);
248 regmap_write(pmcreg
, AT91_PMC_PCER1
, pmc_cache
.pcsr1
);
250 for (i
= 0; registered_ids
[i
]; i
++) {
251 regmap_write(pmcreg
, AT91_PMC_PCR
,
252 pmc_cache
.pcr
[registered_ids
[i
]] |
255 for (i
= 0; registered_pcks
[i
]; i
++) {
256 num
= registered_pcks
[i
] - 1;
257 regmap_write(pmcreg
, AT91_PMC_PCKR(num
), pmc_cache
.pckr
[num
]);
260 if (pmc_cache
.uckr
& AT91_PMC_UPLLEN
)
261 mask
|= AT91_PMC_LOCKU
;
263 while (!pmc_ready(mask
))
267 static struct syscore_ops pmc_syscore_ops
= {
268 .suspend
= pmc_suspend
,
269 .resume
= pmc_resume
,
272 static const struct of_device_id sama5d2_pmc_dt_ids
[] = {
273 { .compatible
= "atmel,sama5d2-pmc" },
277 static int __init
pmc_register_ops(void)
279 struct device_node
*np
;
281 np
= of_find_matching_node(NULL
, sama5d2_pmc_dt_ids
);
283 pmcreg
= syscon_node_to_regmap(np
);
285 return PTR_ERR(pmcreg
);
287 register_syscore_ops(&pmc_syscore_ops
);
291 /* This has to happen before arch_initcall because of the tcb_clksrc driver */
292 postcore_initcall(pmc_register_ops
);