9
9
#include <linux/io.h>
10
10
#include <linux/module.h>
11
11
#include <linux/of.h>
12
+ #include <linux/of_address.h>
12
13
#include <linux/of_device.h>
13
14
#include <linux/platform_device.h>
14
15
#include <linux/slab.h>
@@ -157,6 +158,122 @@ static const struct imx8qxp_ss_lpcg imx8qxp_ss_lsio = {
157
158
.num_max = IMX_LSIO_LPCG_CLK_END ,
158
159
};
159
160
161
+ #define IMX_LPCG_MAX_CLKS 8
162
+
163
+ static struct clk_hw * imx_lpcg_of_clk_src_get (struct of_phandle_args * clkspec ,
164
+ void * data )
165
+ {
166
+ struct clk_hw_onecell_data * hw_data = data ;
167
+ unsigned int idx = clkspec -> args [0 ] / 4 ;
168
+
169
+ if (idx >= hw_data -> num ) {
170
+ pr_err ("%s: invalid index %u\n" , __func__ , idx );
171
+ return ERR_PTR (- EINVAL );
172
+ }
173
+
174
+ return hw_data -> hws [idx ];
175
+ }
176
+
177
+ static int imx_lpcg_parse_clks_from_dt (struct platform_device * pdev ,
178
+ struct device_node * np )
179
+ {
180
+ const char * output_names [IMX_LPCG_MAX_CLKS ];
181
+ const char * parent_names [IMX_LPCG_MAX_CLKS ];
182
+ unsigned int bit_offset [IMX_LPCG_MAX_CLKS ];
183
+ struct clk_hw_onecell_data * clk_data ;
184
+ struct clk_hw * * clk_hws ;
185
+ struct resource * res ;
186
+ void __iomem * base ;
187
+ int count ;
188
+ int idx ;
189
+ int ret ;
190
+ int i ;
191
+
192
+ if (!of_device_is_compatible (np , "fsl,imx8qxp-lpcg" ))
193
+ return - EINVAL ;
194
+
195
+ res = platform_get_resource (pdev , IORESOURCE_MEM , 0 );
196
+ base = devm_ioremap_resource (& pdev -> dev , res );
197
+ if (IS_ERR (base ))
198
+ return PTR_ERR (base );
199
+
200
+ count = of_property_count_u32_elems (np , "clock-indices" );
201
+ if (count < 0 ) {
202
+ dev_err (& pdev -> dev , "failed to count clocks\n" );
203
+ return - EINVAL ;
204
+ }
205
+
206
+ /*
207
+ * A trick here is that we set the num of clks to the MAX instead
208
+ * of the count from clock-indices because one LPCG supports up to
209
+ * 8 clock outputs which each of them is fixed to 4 bits. Then we can
210
+ * easily get the clock by clk-indices (bit-offset) / 4.
211
+ * And the cost is very limited few pointers.
212
+ */
213
+
214
+ clk_data = devm_kzalloc (& pdev -> dev , struct_size (clk_data , hws ,
215
+ IMX_LPCG_MAX_CLKS ), GFP_KERNEL );
216
+ if (!clk_data )
217
+ return - ENOMEM ;
218
+
219
+ clk_data -> num = IMX_LPCG_MAX_CLKS ;
220
+ clk_hws = clk_data -> hws ;
221
+
222
+ ret = of_property_read_u32_array (np , "clock-indices" , bit_offset ,
223
+ count );
224
+ if (ret < 0 ) {
225
+ dev_err (& pdev -> dev , "failed to read clock-indices\n" );
226
+ return - EINVAL ;
227
+ }
228
+
229
+ ret = of_clk_parent_fill (np , parent_names , count );
230
+ if (ret != count ) {
231
+ dev_err (& pdev -> dev , "failed to get clock parent names\n" );
232
+ return count ;
233
+ }
234
+
235
+ ret = of_property_read_string_array (np , "clock-output-names" ,
236
+ output_names , count );
237
+ if (ret != count ) {
238
+ dev_err (& pdev -> dev , "failed to read clock-output-names\n" );
239
+ return - EINVAL ;
240
+ }
241
+
242
+ for (i = 0 ; i < count ; i ++ ) {
243
+ idx = bit_offset [i ] / 4 ;
244
+ if (idx > IMX_LPCG_MAX_CLKS ) {
245
+ dev_warn (& pdev -> dev , "invalid bit offset of clock %d\n" ,
246
+ i );
247
+ ret = - EINVAL ;
248
+ goto unreg ;
249
+ }
250
+
251
+ clk_hws [idx ] = imx_clk_lpcg_scu (output_names [i ],
252
+ parent_names [i ], 0 , base ,
253
+ bit_offset [i ], false);
254
+ if (IS_ERR (clk_hws [idx ])) {
255
+ dev_warn (& pdev -> dev , "failed to register clock %d\n" ,
256
+ idx );
257
+ ret = PTR_ERR (clk_hws [idx ]);
258
+ goto unreg ;
259
+ }
260
+ }
261
+
262
+ ret = devm_of_clk_add_hw_provider (& pdev -> dev , imx_lpcg_of_clk_src_get ,
263
+ clk_data );
264
+ if (!ret )
265
+ return 0 ;
266
+
267
+ unreg :
268
+ while (-- i >= 0 ) {
269
+ idx = bit_offset [i ] / 4 ;
270
+ if (clk_hws [idx ])
271
+ imx_clk_lpcg_scu_unregister (clk_hws [idx ]);
272
+ }
273
+
274
+ return ret ;
275
+ }
276
+
160
277
static int imx8qxp_lpcg_clk_probe (struct platform_device * pdev )
161
278
{
162
279
struct device * dev = & pdev -> dev ;
@@ -167,8 +284,14 @@ static int imx8qxp_lpcg_clk_probe(struct platform_device *pdev)
167
284
struct resource * res ;
168
285
struct clk_hw * * clks ;
169
286
void __iomem * base ;
287
+ int ret ;
170
288
int i ;
171
289
290
+ /* try new binding to parse clocks from device tree first */
291
+ ret = imx_lpcg_parse_clks_from_dt (pdev , np );
292
+ if (!ret )
293
+ return 0 ;
294
+
172
295
ss_lpcg = of_device_get_match_data (dev );
173
296
if (!ss_lpcg )
174
297
return - ENODEV ;
@@ -219,6 +342,7 @@ static const struct of_device_id imx8qxp_lpcg_match[] = {
219
342
{ .compatible = "fsl,imx8qxp-lpcg-adma" , & imx8qxp_ss_adma , },
220
343
{ .compatible = "fsl,imx8qxp-lpcg-conn" , & imx8qxp_ss_conn , },
221
344
{ .compatible = "fsl,imx8qxp-lpcg-lsio" , & imx8qxp_ss_lsio , },
345
+ { .compatible = "fsl,imx8qxp-lpcg" , NULL },
222
346
{ /* sentinel */ }
223
347
};
224
348
0 commit comments