Skip to content

Commit

Permalink
CLK: TI: add autoidle support
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Tero Kristo authored and Mike Turquette committed Jan 17, 2014
1 parent f38b0dd commit b1a07b4
Show file tree
Hide file tree
Showing 5 changed files with 188 additions and 1 deletion.
39 changes: 39 additions & 0 deletions Documentation/devicetree/bindings/clock/ti/autoidle.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
Binding for Texas Instruments autoidle clock.

Binding status: Unstable - ABI compatibility may be broken in the future

This binding uses the common clock binding[1]. It assumes a register mapped
clock which can be put to idle automatically by hardware based on the usage
and a configuration bit setting. Autoidle clock is never an individual
clock, it is always a derivative of some basic clock like a gate, divider,
or fixed-factor.

[1] Documentation/devicetree/bindings/clock/clock-bindings.txt

Required properties:
- reg : offset for the register controlling the autoidle
- ti,autoidle-shift : bit shift of the autoidle enable bit
- ti,invert-autoidle-bit : autoidle is enabled by setting the bit to 0

Examples:
dpll_core_m4_ck: dpll_core_m4_ck {
#clock-cells = <0>;
compatible = "ti,divider-clock";
clocks = <&dpll_core_x2_ck>;
ti,max-div = <31>;
ti,autoidle-shift = <8>;
reg = <0x2d38>;
ti,index-starts-at-one;
ti,invert-autoidle-bit;
};

dpll_usb_clkdcoldo_ck: dpll_usb_clkdcoldo_ck {
#clock-cells = <0>;
compatible = "ti,fixed-factor-clock";
clocks = <&dpll_usb_ck>;
ti,clock-div = <1>;
ti,autoidle-shift = <8>;
reg = <0x01b4>;
ti,clock-mult = <1>;
ti,invert-autoidle-bit;
};
6 changes: 6 additions & 0 deletions arch/arm/mach-omap2/clock.c
Original file line number Diff line number Diff line change
Expand Up @@ -520,6 +520,9 @@ int omap2_clk_enable_autoidle_all(void)
list_for_each_entry(c, &clk_hw_omap_clocks, node)
if (c->ops && c->ops->allow_idle)
c->ops->allow_idle(c);

of_ti_clk_allow_autoidle_all();

return 0;
}

Expand All @@ -539,6 +542,9 @@ int omap2_clk_disable_autoidle_all(void)
list_for_each_entry(c, &clk_hw_omap_clocks, node)
if (c->ops && c->ops->deny_idle)
c->ops->deny_idle(c);

of_ti_clk_deny_autoidle_all();

return 0;
}

Expand Down
2 changes: 1 addition & 1 deletion drivers/clk/ti/Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
ifneq ($(CONFIG_OF),)
obj-y += clk.o
obj-y += clk.o autoidle.o
clk-common = dpll.o
endif
133 changes: 133 additions & 0 deletions drivers/clk/ti/autoidle.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
/*
* TI clock autoidle support
*
* Copyright (C) 2013 Texas Instruments, Inc.
*
* Tero Kristo <t-kristo@ti.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed "as is" WITHOUT ANY WARRANTY of any
* kind, whether express or implied; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/

#include <linux/clk-provider.h>
#include <linux/slab.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/clk/ti.h>

struct clk_ti_autoidle {
void __iomem *reg;
u8 shift;
u8 flags;
const char *name;
struct list_head node;
};

#define AUTOIDLE_LOW 0x1

static LIST_HEAD(autoidle_clks);

static void ti_allow_autoidle(struct clk_ti_autoidle *clk)
{
u32 val;

val = ti_clk_ll_ops->clk_readl(clk->reg);

if (clk->flags & AUTOIDLE_LOW)
val &= ~(1 << clk->shift);
else
val |= (1 << clk->shift);

ti_clk_ll_ops->clk_writel(val, clk->reg);
}

static void ti_deny_autoidle(struct clk_ti_autoidle *clk)
{
u32 val;

val = ti_clk_ll_ops->clk_readl(clk->reg);

if (clk->flags & AUTOIDLE_LOW)
val |= (1 << clk->shift);
else
val &= ~(1 << clk->shift);

ti_clk_ll_ops->clk_writel(val, clk->reg);
}

/**
* of_ti_clk_allow_autoidle_all - enable autoidle for all clocks
*
* Enables hardware autoidle for all registered DT clocks, which have
* the feature.
*/
void of_ti_clk_allow_autoidle_all(void)
{
struct clk_ti_autoidle *c;

list_for_each_entry(c, &autoidle_clks, node)
ti_allow_autoidle(c);
}

/**
* of_ti_clk_deny_autoidle_all - disable autoidle for all clocks
*
* Disables hardware autoidle for all registered DT clocks, which have
* the feature.
*/
void of_ti_clk_deny_autoidle_all(void)
{
struct clk_ti_autoidle *c;

list_for_each_entry(c, &autoidle_clks, node)
ti_deny_autoidle(c);
}

/**
* of_ti_clk_autoidle_setup - sets up hardware autoidle for a clock
* @node: pointer to the clock device node
*
* Checks if a clock has hardware autoidle support or not (check
* for presence of 'ti,autoidle-shift' property in the device tree
* node) and sets up the hardware autoidle feature for the clock
* if available. If autoidle is available, the clock is also added
* to the autoidle list for later processing. Returns 0 on success,
* negative error value on failure.
*/
int __init of_ti_clk_autoidle_setup(struct device_node *node)
{
u32 shift;
struct clk_ti_autoidle *clk;

/* Check if this clock has autoidle support or not */
if (of_property_read_u32(node, "ti,autoidle-shift", &shift))
return 0;

clk = kzalloc(sizeof(*clk), GFP_KERNEL);

if (!clk)
return -ENOMEM;

clk->shift = shift;
clk->name = node->name;
clk->reg = ti_clk_get_reg_addr(node, 0);

if (!clk->reg) {
kfree(clk);
return -EINVAL;
}

if (of_property_read_bool(node, "ti,invert-autoidle-bit"))
clk->flags |= AUTOIDLE_LOW;

list_add(&clk->node, &autoidle_clks);

return 0;
}
9 changes: 9 additions & 0 deletions include/linux/clk/ti.h
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,15 @@ void ti_dt_clocks_register(struct ti_dt_clk *oclks);
void ti_dt_clk_init_provider(struct device_node *np, int index);
int ti_clk_retry_init(struct device_node *node, struct clk_hw *hw,
ti_of_clk_init_cb_t func);
int of_ti_clk_autoidle_setup(struct device_node *node);

#ifdef CONFIG_OF
void of_ti_clk_allow_autoidle_all(void);
void of_ti_clk_deny_autoidle_all(void);
#else
static inline void of_ti_clk_allow_autoidle_all(void) { }
static inline void of_ti_clk_deny_autoidle_all(void) { }
#endif

extern const struct clk_hw_omap_ops clkhwops_omap3_dpll;
extern const struct clk_hw_omap_ops clkhwops_omap4_dpllmx;
Expand Down

0 comments on commit b1a07b4

Please sign in to comment.