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
  38static const 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_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_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                                  void __iomem *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        clk_hw->enable_reg = reg;
 113        clk_hw->enable_bit = bit_idx;
 114        clk_hw->ops = hw_ops;
 115
 116        clk_hw->flags = MEMMAP_ADDRESSING | clk_gate_flags;
 117
 118        init.parent_names = &parent_name;
 119        init.num_parents = 1;
 120
 121        init.flags = flags;
 122
 123        clk = clk_register(NULL, &clk_hw->hw);
 124
 125        if (IS_ERR(clk))
 126                kfree(clk_hw);
 127
 128        return clk;
 129}
 130
 131#if defined(CONFIG_ARCH_OMAP3) && defined(CONFIG_ATAGS)
 132struct clk *ti_clk_register_gate(struct ti_clk *setup)
 133{
 134        const struct clk_ops *ops = &omap_gate_clk_ops;
 135        const struct clk_hw_omap_ops *hw_ops = NULL;
 136        u32 reg;
 137        struct clk_omap_reg *reg_setup;
 138        u32 flags = 0;
 139        u8 clk_gate_flags = 0;
 140        struct ti_clk_gate *gate;
 141
 142        gate = setup->data;
 143
 144        if (gate->flags & CLKF_INTERFACE)
 145                return ti_clk_register_interface(setup);
 146
 147        reg_setup = (struct clk_omap_reg *)&reg;
 148
 149        if (gate->flags & CLKF_SET_RATE_PARENT)
 150                flags |= CLK_SET_RATE_PARENT;
 151
 152        if (gate->flags & CLKF_SET_BIT_TO_DISABLE)
 153                clk_gate_flags |= INVERT_ENABLE;
 154
 155        if (gate->flags & CLKF_HSDIV) {
 156                ops = &omap_gate_clk_hsdiv_restore_ops;
 157                hw_ops = &clkhwops_wait;
 158        }
 159
 160        if (gate->flags & CLKF_DSS)
 161                hw_ops = &clkhwops_omap3430es2_dss_usbhost_wait;
 162
 163        if (gate->flags & CLKF_WAIT)
 164                hw_ops = &clkhwops_wait;
 165
 166        if (gate->flags & CLKF_CLKDM)
 167                ops = &omap_gate_clkdm_clk_ops;
 168
 169        if (gate->flags & CLKF_AM35XX)
 170                hw_ops = &clkhwops_am35xx_ipss_module_wait;
 171
 172        reg_setup->index = gate->module;
 173        reg_setup->offset = gate->reg;
 174
 175        return _register_gate(NULL, setup->name, gate->parent, flags,
 176                              (void __iomem *)reg, gate->bit_shift,
 177                              clk_gate_flags, ops, hw_ops);
 178}
 179
 180struct clk_hw *ti_clk_build_component_gate(struct ti_clk_gate *setup)
 181{
 182        struct clk_hw_omap *gate;
 183        struct clk_omap_reg *reg;
 184        const struct clk_hw_omap_ops *ops = &clkhwops_wait;
 185
 186        if (!setup)
 187                return NULL;
 188
 189        gate = kzalloc(sizeof(*gate), GFP_KERNEL);
 190        if (!gate)
 191                return ERR_PTR(-ENOMEM);
 192
 193        reg = (struct clk_omap_reg *)&gate->enable_reg;
 194        reg->index = setup->module;
 195        reg->offset = setup->reg;
 196
 197        gate->enable_bit = setup->bit_shift;
 198
 199        if (setup->flags & CLKF_NO_WAIT)
 200                ops = NULL;
 201
 202        if (setup->flags & CLKF_INTERFACE)
 203                ops = &clkhwops_iclk_wait;
 204
 205        gate->ops = ops;
 206        gate->flags = MEMMAP_ADDRESSING;
 207
 208        return &gate->hw;
 209}
 210#endif
 211
 212static void __init _of_ti_gate_clk_setup(struct device_node *node,
 213                                         const struct clk_ops *ops,
 214                                         const struct clk_hw_omap_ops *hw_ops)
 215{
 216        struct clk *clk;
 217        const char *parent_name;
 218        void __iomem *reg = NULL;
 219        u8 enable_bit = 0;
 220        u32 val;
 221        u32 flags = 0;
 222        u8 clk_gate_flags = 0;
 223
 224        if (ops != &omap_gate_clkdm_clk_ops) {
 225                reg = ti_clk_get_reg_addr(node, 0);
 226                if (IS_ERR(reg))
 227                        return;
 228
 229                if (!of_property_read_u32(node, "ti,bit-shift", &val))
 230                        enable_bit = val;
 231        }
 232
 233        if (of_clk_get_parent_count(node) != 1) {
 234                pr_err("%s must have 1 parent\n", node->name);
 235                return;
 236        }
 237
 238        parent_name = of_clk_get_parent_name(node, 0);
 239
 240        if (of_property_read_bool(node, "ti,set-rate-parent"))
 241                flags |= CLK_SET_RATE_PARENT;
 242
 243        if (of_property_read_bool(node, "ti,set-bit-to-disable"))
 244                clk_gate_flags |= INVERT_ENABLE;
 245
 246        clk = _register_gate(NULL, node->name, parent_name, flags, reg,
 247                             enable_bit, clk_gate_flags, ops, hw_ops);
 248
 249        if (!IS_ERR(clk))
 250                of_clk_add_provider(node, of_clk_src_simple_get, clk);
 251}
 252
 253static void __init
 254_of_ti_composite_gate_clk_setup(struct device_node *node,
 255                                const struct clk_hw_omap_ops *hw_ops)
 256{
 257        struct clk_hw_omap *gate;
 258        u32 val = 0;
 259
 260        gate = kzalloc(sizeof(*gate), GFP_KERNEL);
 261        if (!gate)
 262                return;
 263
 264        gate->enable_reg = ti_clk_get_reg_addr(node, 0);
 265        if (IS_ERR(gate->enable_reg))
 266                goto cleanup;
 267
 268        of_property_read_u32(node, "ti,bit-shift", &val);
 269
 270        gate->enable_bit = val;
 271        gate->ops = hw_ops;
 272        gate->flags = MEMMAP_ADDRESSING;
 273
 274        if (!ti_clk_add_component(node, &gate->hw, CLK_COMPONENT_TYPE_GATE))
 275                return;
 276
 277cleanup:
 278        kfree(gate);
 279}
 280
 281static void __init
 282of_ti_composite_no_wait_gate_clk_setup(struct device_node *node)
 283{
 284        _of_ti_composite_gate_clk_setup(node, NULL);
 285}
 286CLK_OF_DECLARE(ti_composite_no_wait_gate_clk, "ti,composite-no-wait-gate-clock",
 287               of_ti_composite_no_wait_gate_clk_setup);
 288
 289#if defined(CONFIG_ARCH_OMAP2) || defined(CONFIG_ARCH_OMAP3)
 290static void __init of_ti_composite_interface_clk_setup(struct device_node *node)
 291{
 292        _of_ti_composite_gate_clk_setup(node, &clkhwops_iclk_wait);
 293}
 294CLK_OF_DECLARE(ti_composite_interface_clk, "ti,composite-interface-clock",
 295               of_ti_composite_interface_clk_setup);
 296#endif
 297
 298static void __init of_ti_composite_gate_clk_setup(struct device_node *node)
 299{
 300        _of_ti_composite_gate_clk_setup(node, &clkhwops_wait);
 301}
 302CLK_OF_DECLARE(ti_composite_gate_clk, "ti,composite-gate-clock",
 303               of_ti_composite_gate_clk_setup);
 304
 305
 306static void __init of_ti_clkdm_gate_clk_setup(struct device_node *node)
 307{
 308        _of_ti_gate_clk_setup(node, &omap_gate_clkdm_clk_ops, NULL);
 309}
 310CLK_OF_DECLARE(ti_clkdm_gate_clk, "ti,clkdm-gate-clock",
 311               of_ti_clkdm_gate_clk_setup);
 312
 313static void __init of_ti_hsdiv_gate_clk_setup(struct device_node *node)
 314{
 315        _of_ti_gate_clk_setup(node, &omap_gate_clk_hsdiv_restore_ops,
 316                              &clkhwops_wait);
 317}
 318CLK_OF_DECLARE(ti_hsdiv_gate_clk, "ti,hsdiv-gate-clock",
 319               of_ti_hsdiv_gate_clk_setup);
 320
 321static void __init of_ti_gate_clk_setup(struct device_node *node)
 322{
 323        _of_ti_gate_clk_setup(node, &omap_gate_clk_ops, NULL);
 324}
 325CLK_OF_DECLARE(ti_gate_clk, "ti,gate-clock", of_ti_gate_clk_setup);
 326
 327static void __init of_ti_wait_gate_clk_setup(struct device_node *node)
 328{
 329        _of_ti_gate_clk_setup(node, &omap_gate_clk_ops, &clkhwops_wait);
 330}
 331CLK_OF_DECLARE(ti_wait_gate_clk, "ti,wait-gate-clock",
 332               of_ti_wait_gate_clk_setup);
 333
 334#ifdef CONFIG_ARCH_OMAP3
 335static void __init of_ti_am35xx_gate_clk_setup(struct device_node *node)
 336{
 337        _of_ti_gate_clk_setup(node, &omap_gate_clk_ops,
 338                              &clkhwops_am35xx_ipss_module_wait);
 339}
 340CLK_OF_DECLARE(ti_am35xx_gate_clk, "ti,am35xx-gate-clock",
 341               of_ti_am35xx_gate_clk_setup);
 342
 343static void __init of_ti_dss_gate_clk_setup(struct device_node *node)
 344{
 345        _of_ti_gate_clk_setup(node, &omap_gate_clk_ops,
 346                              &clkhwops_omap3430es2_dss_usbhost_wait);
 347}
 348CLK_OF_DECLARE(ti_dss_gate_clk, "ti,dss-gate-clock",
 349               of_ti_dss_gate_clk_setup);
 350#endif
 351