1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Copyright (C) 2014 Google, Inc. |
4 | */ |
5 | |
6 | #include <linux/clk.h> |
7 | #include <linux/clk-provider.h> |
8 | #include <linux/kernel.h> |
9 | #include <linux/of.h> |
10 | #include <linux/of_address.h> |
11 | #include <linux/slab.h> |
12 | |
13 | #include "clk.h" |
14 | |
15 | struct pistachio_clk_provider * |
16 | pistachio_clk_alloc_provider(struct device_node *node, unsigned int num_clks) |
17 | { |
18 | struct pistachio_clk_provider *p; |
19 | |
20 | p = kzalloc(size: sizeof(*p), GFP_KERNEL); |
21 | if (!p) |
22 | return p; |
23 | |
24 | p->clk_data.clks = kcalloc(n: num_clks, size: sizeof(struct clk *), GFP_KERNEL); |
25 | if (!p->clk_data.clks) |
26 | goto free_provider; |
27 | p->clk_data.clk_num = num_clks; |
28 | p->node = node; |
29 | p->base = of_iomap(node, index: 0); |
30 | if (!p->base) { |
31 | pr_err("Failed to map clock provider registers\n" ); |
32 | goto free_clks; |
33 | } |
34 | |
35 | return p; |
36 | |
37 | free_clks: |
38 | kfree(objp: p->clk_data.clks); |
39 | free_provider: |
40 | kfree(objp: p); |
41 | return NULL; |
42 | } |
43 | |
44 | void pistachio_clk_register_provider(struct pistachio_clk_provider *p) |
45 | { |
46 | unsigned int i; |
47 | |
48 | for (i = 0; i < p->clk_data.clk_num; i++) { |
49 | if (IS_ERR(ptr: p->clk_data.clks[i])) |
50 | pr_warn("Failed to register clock %d: %ld\n" , i, |
51 | PTR_ERR(p->clk_data.clks[i])); |
52 | } |
53 | |
54 | of_clk_add_provider(np: p->node, clk_src_get: of_clk_src_onecell_get, data: &p->clk_data); |
55 | } |
56 | |
57 | void pistachio_clk_register_gate(struct pistachio_clk_provider *p, |
58 | struct pistachio_gate *gate, |
59 | unsigned int num) |
60 | { |
61 | struct clk *clk; |
62 | unsigned int i; |
63 | |
64 | for (i = 0; i < num; i++) { |
65 | clk = clk_register_gate(NULL, name: gate[i].name, parent_name: gate[i].parent, |
66 | CLK_SET_RATE_PARENT, |
67 | reg: p->base + gate[i].reg, bit_idx: gate[i].shift, |
68 | clk_gate_flags: 0, NULL); |
69 | p->clk_data.clks[gate[i].id] = clk; |
70 | } |
71 | } |
72 | |
73 | void pistachio_clk_register_mux(struct pistachio_clk_provider *p, |
74 | struct pistachio_mux *mux, |
75 | unsigned int num) |
76 | { |
77 | struct clk *clk; |
78 | unsigned int i; |
79 | |
80 | for (i = 0; i < num; i++) { |
81 | clk = clk_register_mux(NULL, mux[i].name, mux[i].parents, |
82 | mux[i].num_parents, |
83 | CLK_SET_RATE_NO_REPARENT, |
84 | p->base + mux[i].reg, mux[i].shift, |
85 | get_count_order(mux[i].num_parents), |
86 | 0, NULL); |
87 | p->clk_data.clks[mux[i].id] = clk; |
88 | } |
89 | } |
90 | |
91 | void pistachio_clk_register_div(struct pistachio_clk_provider *p, |
92 | struct pistachio_div *div, |
93 | unsigned int num) |
94 | { |
95 | struct clk *clk; |
96 | unsigned int i; |
97 | |
98 | for (i = 0; i < num; i++) { |
99 | clk = clk_register_divider(NULL, div[i].name, div[i].parent, |
100 | 0, p->base + div[i].reg, 0, |
101 | div[i].width, div[i].div_flags, |
102 | NULL); |
103 | p->clk_data.clks[div[i].id] = clk; |
104 | } |
105 | } |
106 | |
107 | void pistachio_clk_register_fixed_factor(struct pistachio_clk_provider *p, |
108 | struct pistachio_fixed_factor *ff, |
109 | unsigned int num) |
110 | { |
111 | struct clk *clk; |
112 | unsigned int i; |
113 | |
114 | for (i = 0; i < num; i++) { |
115 | clk = clk_register_fixed_factor(NULL, name: ff[i].name, parent_name: ff[i].parent, |
116 | flags: 0, mult: 1, div: ff[i].div); |
117 | p->clk_data.clks[ff[i].id] = clk; |
118 | } |
119 | } |
120 | |
121 | void pistachio_clk_force_enable(struct pistachio_clk_provider *p, |
122 | unsigned int *clk_ids, unsigned int num) |
123 | { |
124 | unsigned int i; |
125 | int err; |
126 | |
127 | for (i = 0; i < num; i++) { |
128 | struct clk *clk = p->clk_data.clks[clk_ids[i]]; |
129 | |
130 | if (IS_ERR(ptr: clk)) |
131 | continue; |
132 | |
133 | err = clk_prepare_enable(clk); |
134 | if (err) |
135 | pr_err("Failed to enable clock %s: %d\n" , |
136 | __clk_get_name(clk), err); |
137 | } |
138 | } |
139 | |