linux/drivers/clk/ti/gate.c
<<
>>
Prefs
   1/*
   2 * OMAP gate 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/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#include "clock.h"
  26
  27#undef pr_fmt
  28#define pr_fmt(fmt) "%s: " fmt, __func__
  29
  30static int omap36xx_gate_clk_enable_with_hsdiv_restore(struct clk_hw *clk);
  31
  32static const struct clk_ops omap_gate_clkdm_clk_ops = {
  33        .init           = &omap2_init_clk_clkdm,
  34        .enable         = &omap2_clkops_enable_clkdm,
  35        .disable        = &omap2_clkops_disable_clkdm,
  36        .restore_context = clk_gate_restore_context,
  37};
  38
  39const struct clk_ops omap_gate_clk_ops = {
  40        .init           = &omap2_init_clk_clkdm,
  41        .enable         = &omap2_dflt_clk_enable,
  42        .disable        = &omap2_dflt_clk_disable,
  43        .is_enabled     = &omap2_dflt_clk_is_enabled,
  44        .restore_context = clk_gate_restore_context,
  45};
  46
  47static const struct clk_ops omap_gate_clk_hsdiv_restore_ops = {
  48        .init           = &omap2_init_clk_clkdm,
  49        .enable         = &omap36xx_gate_clk_enable_with_hsdiv_restore,
  50        .disable        = &omap2_dflt_clk_disable,
  51        .is_enabled     = &omap2_dflt_clk_is_enabled,
  52        .restore_context = clk_gate_restore_context,
  53};
  54
  55/**
  56 * omap36xx_gate_clk_enable_with_hsdiv_restore - enable clocks suffering
  57 *         from HSDivider PWRDN problem Implements Errata ID: i556.
  58 * @hw: DPLL output struct clk_hw
  59 *
  60 * 3630 only: dpll3_m3_ck, dpll4_m2_ck, dpll4_m3_ck, dpll4_m4_ck,
  61 * dpll4_m5_ck & dpll4_m6_ck dividers gets loaded with reset
  62 * valueafter their respective PWRDN bits are set.  Any dummy write
  63 * (Any other value different from the Read value) to the
  64 * corresponding CM_CLKSEL register will refresh the dividers.
  65 */
  66static int omap36xx_gate_clk_enable_with_hsdiv_restore(struct clk_hw *hw)
  67{
  68        struct clk_omap_divider *parent;
  69        struct clk_hw *parent_hw;
  70        u32 dummy_v, orig_v;
  71        int ret;
  72
  73        /* Clear PWRDN bit of HSDIVIDER */
  74        ret = omap2_dflt_clk_enable(hw);
  75
  76        /* Parent is the x2 node, get parent of parent for the m2 div */
  77        parent_hw = clk_hw_get_parent(clk_hw_get_parent(hw));
  78        parent = to_clk_omap_divider(parent_hw);
  79
  80        /* Restore the dividers */
  81        if (!ret) {
  82                orig_v = ti_clk_ll_ops->clk_readl(&parent->reg);
  83                dummy_v = orig_v;
  84
  85                /* Write any other value different from the Read value */
  86                dummy_v ^= (1 << parent->shift);
  87                ti_clk_ll_ops->clk_writel(dummy_v, &parent->reg);
  88
  89                /* Write the original divider */
  90                ti_clk_ll_ops->clk_writel(orig_v, &parent->reg);
  91        }
  92
  93        return ret;
  94}
  95
  96static struct clk *_register_gate(struct device *dev, const char *name,
  97                                  const char *parent_name, unsigned long flags,
  98                                  struct clk_omap_reg *reg, u8 bit_idx,
  99                                  u8 clk_gate_flags, const struct clk_ops *ops,
 100                                  const struct clk_hw_omap_ops *hw_ops)
 101{
 102        struct clk_init_data init = { NULL };
 103        struct clk_hw_omap *clk_hw;
 104        struct clk *clk;
 105
 106        clk_hw = kzalloc(sizeof(*clk_hw), GFP_KERNEL);
 107        if (!clk_hw)
 108                return ERR_PTR(-ENOMEM);
 109
 110        clk_hw->hw.init = &init;
 111
 112        init.name = name;
 113        init.ops = ops;
 114
 115        memcpy(&clk_hw->enable_reg, reg, sizeof(*reg));
 116        clk_hw->enable_bit = bit_idx;
 117        clk_hw->ops = hw_ops;
 118
 119        clk_hw->flags = clk_gate_flags;
 120
 121        init.parent_names = &parent_name;
 122        init.num_parents = 1;
 123
 124        init.flags = flags;
 125
 126        clk = ti_clk_register_omap_hw(NULL, &clk_hw->hw, name);
 127
 128        if (IS_ERR(clk))
 129                kfree(clk_hw);
 130
 131        return clk;
 132}
 133
 134static void __init _of_ti_gate_clk_setup(struct device_node *node,
 135                                         const struct clk_ops *ops,
 136                                         const struct clk_hw_omap_ops *hw_ops)
 137{
 138        struct clk *clk;
 139        const char *parent_name;
 140        struct clk_omap_reg reg;
 141        u8 enable_bit = 0;
 142        u32 val;
 143        u32 flags = 0;
 144        u8 clk_gate_flags = 0;
 145
 146        if (ops != &omap_gate_clkdm_clk_ops) {
 147                if (ti_clk_get_reg_addr(node, 0, &reg))
 148                        return;
 149
 150                if (!of_property_read_u32(node, "ti,bit-shift", &val))
 151                        enable_bit = val;
 152        }
 153
 154        if (of_clk_get_parent_count(node) != 1) {
 155                pr_err("%pOFn must have 1 parent\n", node);
 156                return;
 157        }
 158
 159        parent_name = of_clk_get_parent_name(node, 0);
 160
 161        if (of_property_read_bool(node, "ti,set-rate-parent"))
 162                flags |= CLK_SET_RATE_PARENT;
 163
 164        if (of_property_read_bool(node, "ti,set-bit-to-disable"))
 165                clk_gate_flags |= INVERT_ENABLE;
 166
 167        clk = _register_gate(NULL, node->name, parent_name, flags, &reg,
 168                             enable_bit, clk_gate_flags, ops, hw_ops);
 169
 170        if (!IS_ERR(clk))
 171                of_clk_add_provider(node, of_clk_src_simple_get, clk);
 172}
 173
 174static void __init
 175_of_ti_composite_gate_clk_setup(struct device_node *node,
 176                                const struct clk_hw_omap_ops *hw_ops)
 177{
 178        struct clk_hw_omap *gate;
 179        u32 val = 0;
 180
 181        gate = kzalloc(sizeof(*gate), GFP_KERNEL);
 182        if (!gate)
 183                return;
 184
 185        if (ti_clk_get_reg_addr(node, 0, &gate->enable_reg))
 186                goto cleanup;
 187
 188        of_property_read_u32(node, "ti,bit-shift", &val);
 189
 190        gate->enable_bit = val;
 191        gate->ops = hw_ops;
 192
 193        if (!ti_clk_add_component(node, &gate->hw, CLK_COMPONENT_TYPE_GATE))
 194                return;
 195
 196cleanup:
 197        kfree(gate);
 198}
 199
 200static void __init
 201of_ti_composite_no_wait_gate_clk_setup(struct device_node *node)
 202{
 203        _of_ti_composite_gate_clk_setup(node, NULL);
 204}
 205CLK_OF_DECLARE(ti_composite_no_wait_gate_clk, "ti,composite-no-wait-gate-clock",
 206               of_ti_composite_no_wait_gate_clk_setup);
 207
 208#if defined(CONFIG_ARCH_OMAP2) || defined(CONFIG_ARCH_OMAP3)
 209static void __init of_ti_composite_interface_clk_setup(struct device_node *node)
 210{
 211        _of_ti_composite_gate_clk_setup(node, &clkhwops_iclk_wait);
 212}
 213CLK_OF_DECLARE(ti_composite_interface_clk, "ti,composite-interface-clock",
 214               of_ti_composite_interface_clk_setup);
 215#endif
 216
 217static void __init of_ti_composite_gate_clk_setup(struct device_node *node)
 218{
 219        _of_ti_composite_gate_clk_setup(node, &clkhwops_wait);
 220}
 221CLK_OF_DECLARE(ti_composite_gate_clk, "ti,composite-gate-clock",
 222               of_ti_composite_gate_clk_setup);
 223
 224
 225static void __init of_ti_clkdm_gate_clk_setup(struct device_node *node)
 226{
 227        _of_ti_gate_clk_setup(node, &omap_gate_clkdm_clk_ops, NULL);
 228}
 229CLK_OF_DECLARE(ti_clkdm_gate_clk, "ti,clkdm-gate-clock",
 230               of_ti_clkdm_gate_clk_setup);
 231
 232static void __init of_ti_hsdiv_gate_clk_setup(struct device_node *node)
 233{
 234        _of_ti_gate_clk_setup(node, &omap_gate_clk_hsdiv_restore_ops,
 235                              &clkhwops_wait);
 236}
 237CLK_OF_DECLARE(ti_hsdiv_gate_clk, "ti,hsdiv-gate-clock",
 238               of_ti_hsdiv_gate_clk_setup);
 239
 240static void __init of_ti_gate_clk_setup(struct device_node *node)
 241{
 242        _of_ti_gate_clk_setup(node, &omap_gate_clk_ops, NULL);
 243}
 244CLK_OF_DECLARE(ti_gate_clk, "ti,gate-clock", of_ti_gate_clk_setup);
 245
 246static void __init of_ti_wait_gate_clk_setup(struct device_node *node)
 247{
 248        _of_ti_gate_clk_setup(node, &omap_gate_clk_ops, &clkhwops_wait);
 249}
 250CLK_OF_DECLARE(ti_wait_gate_clk, "ti,wait-gate-clock",
 251               of_ti_wait_gate_clk_setup);
 252
 253#ifdef CONFIG_ARCH_OMAP3
 254static void __init of_ti_am35xx_gate_clk_setup(struct device_node *node)
 255{
 256        _of_ti_gate_clk_setup(node, &omap_gate_clk_ops,
 257                              &clkhwops_am35xx_ipss_module_wait);
 258}
 259CLK_OF_DECLARE(ti_am35xx_gate_clk, "ti,am35xx-gate-clock",
 260               of_ti_am35xx_gate_clk_setup);
 261
 262static void __init of_ti_dss_gate_clk_setup(struct device_node *node)
 263{
 264        _of_ti_gate_clk_setup(node, &omap_gate_clk_ops,
 265                              &clkhwops_omap3430es2_dss_usbhost_wait);
 266}
 267CLK_OF_DECLARE(ti_dss_gate_clk, "ti,dss-gate-clock",
 268               of_ti_dss_gate_clk_setup);
 269#endif
 270