Commit | Line | Data |
---|---|---|
b1a07b47 TK |
1 | /* |
2 | * TI clock autoidle 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/slab.h> | |
20 | #include <linux/io.h> | |
21 | #include <linux/of.h> | |
22 | #include <linux/of_address.h> | |
23 | #include <linux/clk/ti.h> | |
24 | ||
25 | struct clk_ti_autoidle { | |
26 | void __iomem *reg; | |
27 | u8 shift; | |
28 | u8 flags; | |
29 | const char *name; | |
30 | struct list_head node; | |
31 | }; | |
32 | ||
33 | #define AUTOIDLE_LOW 0x1 | |
34 | ||
35 | static LIST_HEAD(autoidle_clks); | |
36 | ||
37 | static void ti_allow_autoidle(struct clk_ti_autoidle *clk) | |
38 | { | |
39 | u32 val; | |
40 | ||
41 | val = ti_clk_ll_ops->clk_readl(clk->reg); | |
42 | ||
43 | if (clk->flags & AUTOIDLE_LOW) | |
44 | val &= ~(1 << clk->shift); | |
45 | else | |
46 | val |= (1 << clk->shift); | |
47 | ||
48 | ti_clk_ll_ops->clk_writel(val, clk->reg); | |
49 | } | |
50 | ||
51 | static void ti_deny_autoidle(struct clk_ti_autoidle *clk) | |
52 | { | |
53 | u32 val; | |
54 | ||
55 | val = ti_clk_ll_ops->clk_readl(clk->reg); | |
56 | ||
57 | if (clk->flags & AUTOIDLE_LOW) | |
58 | val |= (1 << clk->shift); | |
59 | else | |
60 | val &= ~(1 << clk->shift); | |
61 | ||
62 | ti_clk_ll_ops->clk_writel(val, clk->reg); | |
63 | } | |
64 | ||
65 | /** | |
66 | * of_ti_clk_allow_autoidle_all - enable autoidle for all clocks | |
67 | * | |
68 | * Enables hardware autoidle for all registered DT clocks, which have | |
69 | * the feature. | |
70 | */ | |
71 | void of_ti_clk_allow_autoidle_all(void) | |
72 | { | |
73 | struct clk_ti_autoidle *c; | |
74 | ||
75 | list_for_each_entry(c, &autoidle_clks, node) | |
76 | ti_allow_autoidle(c); | |
77 | } | |
78 | ||
79 | /** | |
80 | * of_ti_clk_deny_autoidle_all - disable autoidle for all clocks | |
81 | * | |
82 | * Disables hardware autoidle for all registered DT clocks, which have | |
83 | * the feature. | |
84 | */ | |
85 | void of_ti_clk_deny_autoidle_all(void) | |
86 | { | |
87 | struct clk_ti_autoidle *c; | |
88 | ||
89 | list_for_each_entry(c, &autoidle_clks, node) | |
90 | ti_deny_autoidle(c); | |
91 | } | |
92 | ||
93 | /** | |
94 | * of_ti_clk_autoidle_setup - sets up hardware autoidle for a clock | |
95 | * @node: pointer to the clock device node | |
96 | * | |
97 | * Checks if a clock has hardware autoidle support or not (check | |
98 | * for presence of 'ti,autoidle-shift' property in the device tree | |
99 | * node) and sets up the hardware autoidle feature for the clock | |
100 | * if available. If autoidle is available, the clock is also added | |
101 | * to the autoidle list for later processing. Returns 0 on success, | |
102 | * negative error value on failure. | |
103 | */ | |
104 | int __init of_ti_clk_autoidle_setup(struct device_node *node) | |
105 | { | |
106 | u32 shift; | |
107 | struct clk_ti_autoidle *clk; | |
108 | ||
109 | /* Check if this clock has autoidle support or not */ | |
110 | if (of_property_read_u32(node, "ti,autoidle-shift", &shift)) | |
111 | return 0; | |
112 | ||
113 | clk = kzalloc(sizeof(*clk), GFP_KERNEL); | |
114 | ||
115 | if (!clk) | |
116 | return -ENOMEM; | |
117 | ||
118 | clk->shift = shift; | |
119 | clk->name = node->name; | |
120 | clk->reg = ti_clk_get_reg_addr(node, 0); | |
121 | ||
122 | if (!clk->reg) { | |
123 | kfree(clk); | |
124 | return -EINVAL; | |
125 | } | |
126 | ||
127 | if (of_property_read_bool(node, "ti,invert-autoidle-bit")) | |
128 | clk->flags |= AUTOIDLE_LOW; | |
129 | ||
130 | list_add(&clk->node, &autoidle_clks); | |
131 | ||
132 | return 0; | |
133 | } |