diff options
author | Tero Kristo <t-kristo@ti.com> | 2013-06-18 09:27:57 -0400 |
---|---|---|
committer | Mike Turquette <mturquette@linaro.org> | 2014-01-17 15:34:59 -0500 |
commit | b1a07b478b63f0a8f971b3a82ce34a67a9324547 (patch) | |
tree | dc5d86884961efff9caaa25c277eac3b0d3c58df | |
parent | f38b0dd63f0d0cca753bf0997eefdfb23dcc9518 (diff) |
CLK: TI: add autoidle support
TI clk driver now routes some of the basic clocks through own
registration routine to allow autoidle support. This routine just
checks a couple of device node properties and adds autoidle support
if required, and just passes the registration forward to basic clocks.
Signed-off-by: Tero Kristo <t-kristo@ti.com>
Acked-by: Tony Lindgren <tony@atomide.com>
Signed-off-by: Mike Turquette <mturquette@linaro.org>
-rw-r--r-- | Documentation/devicetree/bindings/clock/ti/autoidle.txt | 39 | ||||
-rw-r--r-- | arch/arm/mach-omap2/clock.c | 6 | ||||
-rw-r--r-- | drivers/clk/ti/Makefile | 2 | ||||
-rw-r--r-- | drivers/clk/ti/autoidle.c | 133 | ||||
-rw-r--r-- | include/linux/clk/ti.h | 9 |
5 files changed, 188 insertions, 1 deletions
diff --git a/Documentation/devicetree/bindings/clock/ti/autoidle.txt b/Documentation/devicetree/bindings/clock/ti/autoidle.txt new file mode 100644 index 000000000000..7c735dde9fe9 --- /dev/null +++ b/Documentation/devicetree/bindings/clock/ti/autoidle.txt | |||
@@ -0,0 +1,39 @@ | |||
1 | Binding for Texas Instruments autoidle clock. | ||
2 | |||
3 | Binding status: Unstable - ABI compatibility may be broken in the future | ||
4 | |||
5 | This binding uses the common clock binding[1]. It assumes a register mapped | ||
6 | clock which can be put to idle automatically by hardware based on the usage | ||
7 | and a configuration bit setting. Autoidle clock is never an individual | ||
8 | clock, it is always a derivative of some basic clock like a gate, divider, | ||
9 | or fixed-factor. | ||
10 | |||
11 | [1] Documentation/devicetree/bindings/clock/clock-bindings.txt | ||
12 | |||
13 | Required properties: | ||
14 | - reg : offset for the register controlling the autoidle | ||
15 | - ti,autoidle-shift : bit shift of the autoidle enable bit | ||
16 | - ti,invert-autoidle-bit : autoidle is enabled by setting the bit to 0 | ||
17 | |||
18 | Examples: | ||
19 | dpll_core_m4_ck: dpll_core_m4_ck { | ||
20 | #clock-cells = <0>; | ||
21 | compatible = "ti,divider-clock"; | ||
22 | clocks = <&dpll_core_x2_ck>; | ||
23 | ti,max-div = <31>; | ||
24 | ti,autoidle-shift = <8>; | ||
25 | reg = <0x2d38>; | ||
26 | ti,index-starts-at-one; | ||
27 | ti,invert-autoidle-bit; | ||
28 | }; | ||
29 | |||
30 | dpll_usb_clkdcoldo_ck: dpll_usb_clkdcoldo_ck { | ||
31 | #clock-cells = <0>; | ||
32 | compatible = "ti,fixed-factor-clock"; | ||
33 | clocks = <&dpll_usb_ck>; | ||
34 | ti,clock-div = <1>; | ||
35 | ti,autoidle-shift = <8>; | ||
36 | reg = <0x01b4>; | ||
37 | ti,clock-mult = <1>; | ||
38 | ti,invert-autoidle-bit; | ||
39 | }; | ||
diff --git a/arch/arm/mach-omap2/clock.c b/arch/arm/mach-omap2/clock.c index c7c5d31e9082..238be3f1ddce 100644 --- a/arch/arm/mach-omap2/clock.c +++ b/arch/arm/mach-omap2/clock.c | |||
@@ -520,6 +520,9 @@ int omap2_clk_enable_autoidle_all(void) | |||
520 | list_for_each_entry(c, &clk_hw_omap_clocks, node) | 520 | list_for_each_entry(c, &clk_hw_omap_clocks, node) |
521 | if (c->ops && c->ops->allow_idle) | 521 | if (c->ops && c->ops->allow_idle) |
522 | c->ops->allow_idle(c); | 522 | c->ops->allow_idle(c); |
523 | |||
524 | of_ti_clk_allow_autoidle_all(); | ||
525 | |||
523 | return 0; | 526 | return 0; |
524 | } | 527 | } |
525 | 528 | ||
@@ -539,6 +542,9 @@ int omap2_clk_disable_autoidle_all(void) | |||
539 | list_for_each_entry(c, &clk_hw_omap_clocks, node) | 542 | list_for_each_entry(c, &clk_hw_omap_clocks, node) |
540 | if (c->ops && c->ops->deny_idle) | 543 | if (c->ops && c->ops->deny_idle) |
541 | c->ops->deny_idle(c); | 544 | c->ops->deny_idle(c); |
545 | |||
546 | of_ti_clk_deny_autoidle_all(); | ||
547 | |||
542 | return 0; | 548 | return 0; |
543 | } | 549 | } |
544 | 550 | ||
diff --git a/drivers/clk/ti/Makefile b/drivers/clk/ti/Makefile index 3dbb78dc9fca..7fa1a48df4f3 100644 --- a/drivers/clk/ti/Makefile +++ b/drivers/clk/ti/Makefile | |||
@@ -1,4 +1,4 @@ | |||
1 | ifneq ($(CONFIG_OF),) | 1 | ifneq ($(CONFIG_OF),) |
2 | obj-y += clk.o | 2 | obj-y += clk.o autoidle.o |
3 | clk-common = dpll.o | 3 | clk-common = dpll.o |
4 | endif | 4 | endif |
diff --git a/drivers/clk/ti/autoidle.c b/drivers/clk/ti/autoidle.c new file mode 100644 index 000000000000..8912ff80af34 --- /dev/null +++ b/drivers/clk/ti/autoidle.c | |||
@@ -0,0 +1,133 @@ | |||
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 | } | ||
diff --git a/include/linux/clk/ti.h b/include/linux/clk/ti.h index 3f9de3973582..ca38ee3620b1 100644 --- a/include/linux/clk/ti.h +++ b/include/linux/clk/ti.h | |||
@@ -242,6 +242,15 @@ void ti_dt_clocks_register(struct ti_dt_clk *oclks); | |||
242 | void ti_dt_clk_init_provider(struct device_node *np, int index); | 242 | void ti_dt_clk_init_provider(struct device_node *np, int index); |
243 | int ti_clk_retry_init(struct device_node *node, struct clk_hw *hw, | 243 | int ti_clk_retry_init(struct device_node *node, struct clk_hw *hw, |
244 | ti_of_clk_init_cb_t func); | 244 | ti_of_clk_init_cb_t func); |
245 | int of_ti_clk_autoidle_setup(struct device_node *node); | ||
246 | |||
247 | #ifdef CONFIG_OF | ||
248 | void of_ti_clk_allow_autoidle_all(void); | ||
249 | void of_ti_clk_deny_autoidle_all(void); | ||
250 | #else | ||
251 | static inline void of_ti_clk_allow_autoidle_all(void) { } | ||
252 | static inline void of_ti_clk_deny_autoidle_all(void) { } | ||
253 | #endif | ||
245 | 254 | ||
246 | extern const struct clk_hw_omap_ops clkhwops_omap3_dpll; | 255 | extern const struct clk_hw_omap_ops clkhwops_omap3_dpll; |
247 | extern const struct clk_hw_omap_ops clkhwops_omap4_dpllmx; | 256 | extern const struct clk_hw_omap_ops clkhwops_omap4_dpllmx; |