1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * clkgen-mux.c: ST GEN-MUX Clock driver |
4 | * |
5 | * Copyright (C) 2014 STMicroelectronics (R&D) Limited |
6 | * |
7 | * Authors: Stephen Gallimore <stephen.gallimore@st.com> |
8 | * Pankaj Dev <pankaj.dev@st.com> |
9 | */ |
10 | |
11 | #include <linux/slab.h> |
12 | #include <linux/io.h> |
13 | #include <linux/of_address.h> |
14 | #include <linux/clk.h> |
15 | #include <linux/clk-provider.h> |
16 | #include "clkgen.h" |
17 | |
18 | static const char ** __init clkgen_mux_get_parents(struct device_node *np, |
19 | int *num_parents) |
20 | { |
21 | const char **parents; |
22 | unsigned int nparents; |
23 | |
24 | nparents = of_clk_get_parent_count(np); |
25 | if (WARN_ON(!nparents)) |
26 | return ERR_PTR(error: -EINVAL); |
27 | |
28 | parents = kcalloc(n: nparents, size: sizeof(const char *), GFP_KERNEL); |
29 | if (!parents) |
30 | return ERR_PTR(error: -ENOMEM); |
31 | |
32 | *num_parents = of_clk_parent_fill(np, parents, size: nparents); |
33 | return parents; |
34 | } |
35 | |
36 | struct clkgen_mux_data { |
37 | u32 offset; |
38 | u8 shift; |
39 | u8 width; |
40 | spinlock_t *lock; |
41 | unsigned long clk_flags; |
42 | u8 mux_flags; |
43 | }; |
44 | |
45 | static struct clkgen_mux_data stih407_a9_mux_data = { |
46 | .offset = 0x1a4, |
47 | .shift = 0, |
48 | .width = 2, |
49 | .lock = &clkgen_a9_lock, |
50 | }; |
51 | |
52 | static void __init st_of_clkgen_mux_setup(struct device_node *np, |
53 | struct clkgen_mux_data *data) |
54 | { |
55 | struct clk *clk; |
56 | void __iomem *reg; |
57 | const char **parents; |
58 | int num_parents = 0; |
59 | struct device_node *parent_np; |
60 | |
61 | /* |
62 | * First check for reg property within the node to keep backward |
63 | * compatibility, then if reg doesn't exist look at the parent node |
64 | */ |
65 | reg = of_iomap(node: np, index: 0); |
66 | if (!reg) { |
67 | parent_np = of_get_parent(node: np); |
68 | reg = of_iomap(node: parent_np, index: 0); |
69 | of_node_put(node: parent_np); |
70 | if (!reg) { |
71 | pr_err("%s: Failed to get base address\n" , __func__); |
72 | return; |
73 | } |
74 | } |
75 | |
76 | parents = clkgen_mux_get_parents(np, num_parents: &num_parents); |
77 | if (IS_ERR(ptr: parents)) { |
78 | pr_err("%s: Failed to get parents (%ld)\n" , |
79 | __func__, PTR_ERR(parents)); |
80 | goto err_parents; |
81 | } |
82 | |
83 | clk = clk_register_mux(NULL, np->name, parents, num_parents, |
84 | data->clk_flags | CLK_SET_RATE_PARENT, |
85 | reg + data->offset, |
86 | data->shift, data->width, data->mux_flags, |
87 | data->lock); |
88 | if (IS_ERR(ptr: clk)) |
89 | goto err; |
90 | |
91 | pr_debug("%s: parent %s rate %u\n" , |
92 | __clk_get_name(clk), |
93 | __clk_get_name(clk_get_parent(clk)), |
94 | (unsigned int)clk_get_rate(clk)); |
95 | |
96 | kfree(objp: parents); |
97 | of_clk_add_provider(np, clk_src_get: of_clk_src_simple_get, data: clk); |
98 | return; |
99 | |
100 | err: |
101 | kfree(objp: parents); |
102 | err_parents: |
103 | iounmap(addr: reg); |
104 | } |
105 | |
106 | static void __init st_of_clkgen_a9_mux_setup(struct device_node *np) |
107 | { |
108 | st_of_clkgen_mux_setup(np, data: &stih407_a9_mux_data); |
109 | } |
110 | CLK_OF_DECLARE(clkgen_a9mux, "st,stih407-clkgen-a9-mux" , |
111 | st_of_clkgen_a9_mux_setup); |
112 | |