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