Commit | Line | Data |
---|---|---|
ee38b269 MR |
1 | /* |
2 | * Copyright 2015 Maxime Ripard | |
3 | * | |
4 | * Maxime Ripard <maxime.ripard@free-electrons.com> | |
5 | * | |
6 | * This program is free software; you can redistribute it and/or modify | |
7 | * it under the terms of the GNU General Public License as published by | |
8 | * the Free Software Foundation; either version 2 of the License, or | |
9 | * (at your option) any later version. | |
10 | * | |
11 | * This program is distributed in the hope that it will be useful, | |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | * GNU General Public License for more details. | |
15 | */ | |
16 | ||
17 | #include <linux/clk.h> | |
18 | #include <linux/clk-provider.h> | |
19 | #include <linux/of.h> | |
20 | #include <linux/of_address.h> | |
21 | #include <linux/slab.h> | |
22 | #include <linux/spinlock.h> | |
23 | ||
24 | static DEFINE_SPINLOCK(gates_lock); | |
25 | ||
26 | static void __init sunxi_simple_gates_setup(struct device_node *node, | |
27 | const int protected[], | |
28 | int nprotected) | |
29 | { | |
30 | struct clk_onecell_data *clk_data; | |
31 | const char *clk_parent, *clk_name; | |
32 | struct property *prop; | |
33 | struct resource res; | |
34 | void __iomem *clk_reg; | |
35 | void __iomem *reg; | |
36 | const __be32 *p; | |
37 | int number, i = 0, j; | |
38 | u8 clk_bit; | |
39 | u32 index; | |
40 | ||
41 | reg = of_io_request_and_map(node, 0, of_node_full_name(node)); | |
42 | if (IS_ERR(reg)) | |
43 | return; | |
44 | ||
45 | clk_parent = of_clk_get_parent_name(node, 0); | |
46 | ||
47 | clk_data = kmalloc(sizeof(struct clk_onecell_data), GFP_KERNEL); | |
48 | if (!clk_data) | |
49 | goto err_unmap; | |
50 | ||
51 | number = of_property_count_u32_elems(node, "clock-indices"); | |
52 | of_property_read_u32_index(node, "clock-indices", number - 1, &number); | |
53 | ||
54 | clk_data->clks = kcalloc(number + 1, sizeof(struct clk *), GFP_KERNEL); | |
55 | if (!clk_data->clks) | |
56 | goto err_free_data; | |
57 | ||
58 | of_property_for_each_u32(node, "clock-indices", prop, p, index) { | |
59 | of_property_read_string_index(node, "clock-output-names", | |
60 | i, &clk_name); | |
61 | ||
62 | clk_reg = reg + 4 * (index / 32); | |
63 | clk_bit = index % 32; | |
64 | ||
65 | clk_data->clks[index] = clk_register_gate(NULL, clk_name, | |
66 | clk_parent, 0, | |
67 | clk_reg, | |
68 | clk_bit, | |
69 | 0, &gates_lock); | |
70 | i++; | |
71 | ||
72 | if (IS_ERR(clk_data->clks[index])) { | |
73 | WARN_ON(true); | |
74 | continue; | |
75 | } | |
76 | ||
77 | for (j = 0; j < nprotected; j++) | |
78 | if (protected[j] == index) | |
79 | clk_prepare_enable(clk_data->clks[index]); | |
80 | ||
81 | } | |
82 | ||
83 | clk_data->clk_num = number + 1; | |
84 | of_clk_add_provider(node, of_clk_src_onecell_get, clk_data); | |
85 | ||
86 | return; | |
87 | ||
88 | err_free_data: | |
89 | kfree(clk_data); | |
90 | err_unmap: | |
91 | iounmap(reg); | |
92 | of_address_to_resource(node, 0, &res); | |
93 | release_mem_region(res.start, resource_size(&res)); | |
94 | } | |
95 | ||
96 | static void __init sunxi_simple_gates_init(struct device_node *node) | |
97 | { | |
98 | sunxi_simple_gates_setup(node, NULL, 0); | |
99 | } | |
100 | ||
101 | CLK_OF_DECLARE(sun4i_a10_apb0, "allwinner,sun4i-a10-apb0-gates-clk", | |
102 | sunxi_simple_gates_init); | |
103 | CLK_OF_DECLARE(sun4i_a10_apb1, "allwinner,sun4i-a10-apb1-gates-clk", | |
104 | sunxi_simple_gates_init); | |
105 | CLK_OF_DECLARE(sun4i_a10_axi, "allwinner,sun4i-a10-axi-gates-clk", | |
106 | sunxi_simple_gates_init); | |
107 | CLK_OF_DECLARE(sun5i_a10s_apb0, "allwinner,sun5i-a10s-apb0-gates-clk", | |
108 | sunxi_simple_gates_init); | |
109 | CLK_OF_DECLARE(sun5i_a10s_apb1, "allwinner,sun5i-a10s-apb1-gates-clk", | |
110 | sunxi_simple_gates_init); | |
111 | CLK_OF_DECLARE(sun5i_a13_apb0, "allwinner,sun5i-a13-apb0-gates-clk", | |
112 | sunxi_simple_gates_init); | |
113 | CLK_OF_DECLARE(sun5i_a13_apb1, "allwinner,sun5i-a13-apb1-gates-clk", | |
114 | sunxi_simple_gates_init); | |
115 | CLK_OF_DECLARE(sun6i_a31_ahb1, "allwinner,sun6i-a31-ahb1-gates-clk", | |
116 | sunxi_simple_gates_init); | |
117 | CLK_OF_DECLARE(sun6i_a31_apb1, "allwinner,sun6i-a31-apb1-gates-clk", | |
118 | sunxi_simple_gates_init); | |
119 | CLK_OF_DECLARE(sun6i_a31_apb2, "allwinner,sun6i-a31-apb2-gates-clk", | |
120 | sunxi_simple_gates_init); | |
121 | CLK_OF_DECLARE(sun7i_a20_apb0, "allwinner,sun7i-a20-apb0-gates-clk", | |
122 | sunxi_simple_gates_init); | |
123 | CLK_OF_DECLARE(sun7i_a20_apb1, "allwinner,sun7i-a20-apb1-gates-clk", | |
124 | sunxi_simple_gates_init); | |
125 | CLK_OF_DECLARE(sun8i_a23_ahb1, "allwinner,sun8i-a23-ahb1-gates-clk", | |
126 | sunxi_simple_gates_init); | |
127 | CLK_OF_DECLARE(sun8i_a23_apb1, "allwinner,sun8i-a23-apb1-gates-clk", | |
128 | sunxi_simple_gates_init); | |
129 | CLK_OF_DECLARE(sun8i_a23_apb2, "allwinner,sun8i-a23-apb2-gates-clk", | |
130 | sunxi_simple_gates_init); | |
7d6ddad6 MR |
131 | CLK_OF_DECLARE(sun8i_a33_ahb1, "allwinner,sun8i-a33-ahb1-gates-clk", |
132 | sunxi_simple_gates_init); | |
ee38b269 MR |
133 | CLK_OF_DECLARE(sun9i_a80_ahb0, "allwinner,sun9i-a80-ahb0-gates-clk", |
134 | sunxi_simple_gates_init); | |
135 | CLK_OF_DECLARE(sun9i_a80_ahb1, "allwinner,sun9i-a80-ahb1-gates-clk", | |
136 | sunxi_simple_gates_init); | |
137 | CLK_OF_DECLARE(sun9i_a80_ahb2, "allwinner,sun9i-a80-ahb2-gates-clk", | |
138 | sunxi_simple_gates_init); | |
139 | CLK_OF_DECLARE(sun9i_a80_apb0, "allwinner,sun9i-a80-apb0-gates-clk", | |
140 | sunxi_simple_gates_init); | |
141 | CLK_OF_DECLARE(sun9i_a80_apb1, "allwinner,sun9i-a80-apb1-gates-clk", | |
142 | sunxi_simple_gates_init); | |
bfcba2ed CYT |
143 | CLK_OF_DECLARE(sun9i_a80_apbs, "allwinner,sun9i-a80-apbs-gates-clk", |
144 | sunxi_simple_gates_init); | |
ee38b269 MR |
145 | |
146 | static const int sun4i_a10_ahb_critical_clocks[] __initconst = { | |
147 | 14, /* ahb_sdram */ | |
148 | }; | |
149 | ||
150 | static void __init sun4i_a10_ahb_init(struct device_node *node) | |
151 | { | |
152 | sunxi_simple_gates_setup(node, sun4i_a10_ahb_critical_clocks, | |
153 | ARRAY_SIZE(sun4i_a10_ahb_critical_clocks)); | |
154 | } | |
155 | CLK_OF_DECLARE(sun4i_a10_ahb, "allwinner,sun4i-a10-ahb-gates-clk", | |
156 | sun4i_a10_ahb_init); | |
157 | CLK_OF_DECLARE(sun5i_a10s_ahb, "allwinner,sun5i-a10s-ahb-gates-clk", | |
158 | sun4i_a10_ahb_init); | |
159 | CLK_OF_DECLARE(sun5i_a13_ahb, "allwinner,sun5i-a13-ahb-gates-clk", | |
160 | sun4i_a10_ahb_init); | |
161 | CLK_OF_DECLARE(sun7i_a20_ahb, "allwinner,sun7i-a20-ahb-gates-clk", | |
162 | sun4i_a10_ahb_init); |