]>
Commit | Line | Data |
---|---|---|
16502bfa CB |
1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* | |
3 | * System clock support for AT91 architectures. | |
4 | * | |
5 | * Copyright (C) Microchip Technology Inc. and its subsidiaries | |
6 | * | |
7 | * Author: Claudiu Beznea <[email protected]> | |
8 | * | |
9 | * Based on drivers/clk/at91/clk-system.c from Linux. | |
10 | */ | |
11 | #include <asm/processor.h> | |
16502bfa CB |
12 | #include <clk-uclass.h> |
13 | #include <dm.h> | |
14 | #include <linux/io.h> | |
15 | #include <linux/clk-provider.h> | |
16 | #include <linux/clk/at91_pmc.h> | |
17 | ||
18 | #include "pmc.h" | |
19 | ||
20 | #define UBOOT_DM_CLK_AT91_SYSTEM "at91-system-clk" | |
21 | ||
22 | #define SYSTEM_MAX_ID 31 | |
23 | ||
24 | struct clk_system { | |
25 | void __iomem *base; | |
26 | struct clk clk; | |
27 | u8 id; | |
28 | }; | |
29 | ||
30 | #define to_clk_system(_c) container_of(_c, struct clk_system, clk) | |
31 | ||
32 | static inline int is_pck(int id) | |
33 | { | |
34 | return (id >= 8) && (id <= 15); | |
35 | } | |
36 | ||
37 | static inline bool clk_system_ready(void __iomem *base, int id) | |
38 | { | |
39 | unsigned int status; | |
40 | ||
41 | pmc_read(base, AT91_PMC_SR, &status); | |
42 | ||
43 | return !!(status & (1 << id)); | |
44 | } | |
45 | ||
46 | static int clk_system_enable(struct clk *clk) | |
47 | { | |
48 | struct clk_system *sys = to_clk_system(clk); | |
49 | ||
50 | pmc_write(sys->base, AT91_PMC_SCER, 1 << sys->id); | |
51 | ||
52 | if (!is_pck(sys->id)) | |
53 | return 0; | |
54 | ||
55 | while (!clk_system_ready(sys->base, sys->id)) { | |
56 | debug("waiting for pck%u\n", sys->id); | |
57 | cpu_relax(); | |
58 | } | |
59 | ||
60 | return 0; | |
61 | } | |
62 | ||
63 | static int clk_system_disable(struct clk *clk) | |
64 | { | |
65 | struct clk_system *sys = to_clk_system(clk); | |
66 | ||
67 | pmc_write(sys->base, AT91_PMC_SCDR, 1 << sys->id); | |
68 | ||
69 | return 0; | |
70 | } | |
71 | ||
72 | static const struct clk_ops system_ops = { | |
73 | .enable = clk_system_enable, | |
74 | .disable = clk_system_disable, | |
75 | .get_rate = clk_generic_get_rate, | |
76 | }; | |
77 | ||
78 | struct clk *at91_clk_register_system(void __iomem *base, const char *name, | |
79 | const char *parent_name, u8 id) | |
80 | { | |
81 | struct clk_system *sys; | |
82 | struct clk *clk; | |
83 | int ret; | |
84 | ||
85 | if (!base || !name || !parent_name || id > SYSTEM_MAX_ID) | |
86 | return ERR_PTR(-EINVAL); | |
87 | ||
88 | sys = kzalloc(sizeof(*sys), GFP_KERNEL); | |
89 | if (!sys) | |
90 | return ERR_PTR(-ENOMEM); | |
91 | ||
92 | sys->id = id; | |
93 | sys->base = base; | |
94 | ||
95 | clk = &sys->clk; | |
96 | clk->flags = CLK_GET_RATE_NOCACHE; | |
97 | ret = clk_register(clk, UBOOT_DM_CLK_AT91_SYSTEM, name, parent_name); | |
98 | if (ret) { | |
99 | kfree(sys); | |
100 | clk = ERR_PTR(ret); | |
101 | } | |
102 | ||
103 | return clk; | |
104 | } | |
105 | ||
106 | U_BOOT_DRIVER(at91_system_clk) = { | |
107 | .name = UBOOT_DM_CLK_AT91_SYSTEM, | |
108 | .id = UCLASS_CLK, | |
109 | .ops = &system_ops, | |
110 | .flags = DM_FLAG_PRE_RELOC, | |
111 | }; |