Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Tero Kristo and committed by
Mike Turquette
b1a07b47 f38b0dd6

+188 -1
+39
Documentation/devicetree/bindings/clock/ti/autoidle.txt
··· 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 + };
+6
arch/arm/mach-omap2/clock.c
··· 520 520 list_for_each_entry(c, &clk_hw_omap_clocks, node) 521 521 if (c->ops && c->ops->allow_idle) 522 522 c->ops->allow_idle(c); 523 + 524 + of_ti_clk_allow_autoidle_all(); 525 + 523 526 return 0; 524 527 } 525 528 ··· 542 539 list_for_each_entry(c, &clk_hw_omap_clocks, node) 543 540 if (c->ops && c->ops->deny_idle) 544 541 c->ops->deny_idle(c); 542 + 543 + of_ti_clk_deny_autoidle_all(); 544 + 545 545 return 0; 546 546 } 547 547
+1 -1
drivers/clk/ti/Makefile
··· 1 1 ifneq ($(CONFIG_OF),) 2 - obj-y += clk.o 2 + obj-y += clk.o autoidle.o 3 3 clk-common = dpll.o 4 4 endif
+133
drivers/clk/ti/autoidle.c
··· 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 + }
+9
include/linux/clk/ti.h
··· 242 242 void ti_dt_clk_init_provider(struct device_node *np, int index); 243 243 int ti_clk_retry_init(struct device_node *node, struct clk_hw *hw, 244 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 255 extern const struct clk_hw_omap_ops clkhwops_omap3_dpll; 247 256 extern const struct clk_hw_omap_ops clkhwops_omap4_dpllmx;