Commit | Line | Data |
---|---|---|
a8aceccb TK |
1 | /* |
2 | * TI clock support | |
3 | * | |
4 | * Copyright (C) 2013 Texas Instruments, Inc. | |
5 | * | |
6 | * Tero Kristo <t-kristo@ti.com> | |
7 | * | |
8 | * This program is free software; you can redistribute it and/or modify | |
9 | * it under the terms of the GNU General Public License version 2 as | |
10 | * published by the Free Software Foundation. | |
11 | * | |
12 | * This program is distributed "as is" WITHOUT ANY WARRANTY of any | |
13 | * kind, whether express or implied; without even the implied warranty | |
14 | * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
15 | * GNU General Public License for more details. | |
16 | */ | |
17 | ||
18 | #include <linux/clk-provider.h> | |
19 | #include <linux/clkdev.h> | |
20 | #include <linux/clk/ti.h> | |
21 | #include <linux/of.h> | |
819b4861 TK |
22 | #include <linux/of_address.h> |
23 | #include <linux/list.h> | |
a8aceccb TK |
24 | |
25 | #undef pr_fmt | |
26 | #define pr_fmt(fmt) "%s: " fmt, __func__ | |
27 | ||
819b4861 TK |
28 | static int ti_dt_clk_memmap_index; |
29 | struct ti_clk_ll_ops *ti_clk_ll_ops; | |
30 | ||
a8aceccb TK |
31 | /** |
32 | * ti_dt_clocks_register - register DT alias clocks during boot | |
33 | * @oclks: list of clocks to register | |
34 | * | |
35 | * Register alias or non-standard DT clock entries during boot. By | |
36 | * default, DT clocks are found based on their node name. If any | |
37 | * additional con-id / dev-id -> clock mapping is required, use this | |
38 | * function to list these. | |
39 | */ | |
40 | void __init ti_dt_clocks_register(struct ti_dt_clk oclks[]) | |
41 | { | |
42 | struct ti_dt_clk *c; | |
43 | struct device_node *node; | |
44 | struct clk *clk; | |
45 | struct of_phandle_args clkspec; | |
46 | ||
47 | for (c = oclks; c->node_name != NULL; c++) { | |
48 | node = of_find_node_by_name(NULL, c->node_name); | |
49 | clkspec.np = node; | |
50 | clk = of_clk_get_from_provider(&clkspec); | |
51 | ||
52 | if (!IS_ERR(clk)) { | |
53 | c->lk.clk = clk; | |
54 | clkdev_add(&c->lk); | |
55 | } else { | |
56 | pr_warn("failed to lookup clock node %s\n", | |
57 | c->node_name); | |
58 | } | |
59 | } | |
60 | } | |
819b4861 TK |
61 | |
62 | struct clk_init_item { | |
63 | struct device_node *node; | |
64 | struct clk_hw *hw; | |
65 | ti_of_clk_init_cb_t func; | |
66 | struct list_head link; | |
67 | }; | |
68 | ||
69 | static LIST_HEAD(retry_list); | |
70 | ||
71 | /** | |
72 | * ti_clk_retry_init - retries a failed clock init at later phase | |
73 | * @node: device not for the clock | |
74 | * @hw: partially initialized clk_hw struct for the clock | |
75 | * @func: init function to be called for the clock | |
76 | * | |
77 | * Adds a failed clock init to the retry list. The retry list is parsed | |
78 | * once all the other clocks have been initialized. | |
79 | */ | |
80 | int __init ti_clk_retry_init(struct device_node *node, struct clk_hw *hw, | |
81 | ti_of_clk_init_cb_t func) | |
82 | { | |
83 | struct clk_init_item *retry; | |
84 | ||
85 | pr_debug("%s: adding to retry list...\n", node->name); | |
86 | retry = kzalloc(sizeof(*retry), GFP_KERNEL); | |
87 | if (!retry) | |
88 | return -ENOMEM; | |
89 | ||
90 | retry->node = node; | |
91 | retry->func = func; | |
92 | retry->hw = hw; | |
93 | list_add(&retry->link, &retry_list); | |
94 | ||
95 | return 0; | |
96 | } | |
97 | ||
98 | /** | |
99 | * ti_clk_get_reg_addr - get register address for a clock register | |
100 | * @node: device node for the clock | |
101 | * @index: register index from the clock node | |
102 | * | |
103 | * Builds clock register address from device tree information. This | |
104 | * is a struct of type clk_omap_reg. | |
105 | */ | |
106 | void __iomem *ti_clk_get_reg_addr(struct device_node *node, int index) | |
107 | { | |
108 | struct clk_omap_reg *reg; | |
109 | u32 val; | |
110 | u32 tmp; | |
111 | ||
112 | reg = (struct clk_omap_reg *)&tmp; | |
113 | reg->index = ti_dt_clk_memmap_index; | |
114 | ||
115 | if (of_property_read_u32_index(node, "reg", index, &val)) { | |
116 | pr_err("%s must have reg[%d]!\n", node->name, index); | |
117 | return NULL; | |
118 | } | |
119 | ||
120 | reg->offset = val; | |
121 | ||
122 | return (void __iomem *)tmp; | |
123 | } | |
124 | ||
125 | /** | |
126 | * ti_dt_clk_init_provider - init master clock provider | |
127 | * @parent: master node | |
128 | * @index: internal index for clk_reg_ops | |
129 | * | |
130 | * Initializes a master clock IP block and its child clock nodes. | |
131 | * Regmap is provided for accessing the register space for the | |
132 | * IP block and all the clocks under it. | |
133 | */ | |
134 | void ti_dt_clk_init_provider(struct device_node *parent, int index) | |
135 | { | |
136 | const struct of_device_id *match; | |
137 | struct device_node *np; | |
138 | struct device_node *clocks; | |
139 | of_clk_init_cb_t clk_init_cb; | |
140 | struct clk_init_item *retry; | |
141 | struct clk_init_item *tmp; | |
142 | ||
143 | ti_dt_clk_memmap_index = index; | |
144 | ||
145 | /* get clocks for this parent */ | |
146 | clocks = of_get_child_by_name(parent, "clocks"); | |
147 | if (!clocks) { | |
148 | pr_err("%s missing 'clocks' child node.\n", parent->name); | |
149 | return; | |
150 | } | |
151 | ||
152 | for_each_child_of_node(clocks, np) { | |
153 | match = of_match_node(&__clk_of_table, np); | |
154 | if (!match) | |
155 | continue; | |
156 | clk_init_cb = (of_clk_init_cb_t)match->data; | |
157 | pr_debug("%s: initializing: %s\n", __func__, np->name); | |
158 | clk_init_cb(np); | |
159 | } | |
160 | ||
161 | list_for_each_entry_safe(retry, tmp, &retry_list, link) { | |
162 | pr_debug("retry-init: %s\n", retry->node->name); | |
163 | retry->func(retry->hw, retry->node); | |
164 | list_del(&retry->link); | |
165 | kfree(retry); | |
166 | } | |
167 | } |