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