linux/drivers/clk/hisilicon/clkgate-separated.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 * Hisilicon clock separated gate driver
   4 *
   5 * Copyright (c) 2012-2013 Hisilicon Limited.
   6 * Copyright (c) 2012-2013 Linaro Limited.
   7 *
   8 * Author: Haojian Zhuang <haojian.zhuang@linaro.org>
   9 *         Xin Li <li.xin@linaro.org>
  10 */
  11
  12#include <linux/kernel.h>
  13#include <linux/clk-provider.h>
  14#include <linux/io.h>
  15#include <linux/slab.h>
  16
  17#include "clk.h"
  18
  19/* clock separated gate register offset */
  20#define CLKGATE_SEPERATED_ENABLE                0x0
  21#define CLKGATE_SEPERATED_DISABLE               0x4
  22#define CLKGATE_SEPERATED_STATUS                0x8
  23
  24struct clkgate_separated {
  25        struct clk_hw   hw;
  26        void __iomem    *enable;        /* enable register */
  27        u8              bit_idx;        /* bits in enable/disable register */
  28        u8              flags;
  29        spinlock_t      *lock;
  30};
  31
  32static int clkgate_separated_enable(struct clk_hw *hw)
  33{
  34        struct clkgate_separated *sclk;
  35        unsigned long flags = 0;
  36        u32 reg;
  37
  38        sclk = container_of(hw, struct clkgate_separated, hw);
  39        if (sclk->lock)
  40                spin_lock_irqsave(sclk->lock, flags);
  41        reg = BIT(sclk->bit_idx);
  42        writel_relaxed(reg, sclk->enable);
  43        readl_relaxed(sclk->enable + CLKGATE_SEPERATED_STATUS);
  44        if (sclk->lock)
  45                spin_unlock_irqrestore(sclk->lock, flags);
  46        return 0;
  47}
  48
  49static void clkgate_separated_disable(struct clk_hw *hw)
  50{
  51        struct clkgate_separated *sclk;
  52        unsigned long flags = 0;
  53        u32 reg;
  54
  55        sclk = container_of(hw, struct clkgate_separated, hw);
  56        if (sclk->lock)
  57                spin_lock_irqsave(sclk->lock, flags);
  58        reg = BIT(sclk->bit_idx);
  59        writel_relaxed(reg, sclk->enable + CLKGATE_SEPERATED_DISABLE);
  60        readl_relaxed(sclk->enable + CLKGATE_SEPERATED_STATUS);
  61        if (sclk->lock)
  62                spin_unlock_irqrestore(sclk->lock, flags);
  63}
  64
  65static int clkgate_separated_is_enabled(struct clk_hw *hw)
  66{
  67        struct clkgate_separated *sclk;
  68        u32 reg;
  69
  70        sclk = container_of(hw, struct clkgate_separated, hw);
  71        reg = readl_relaxed(sclk->enable + CLKGATE_SEPERATED_STATUS);
  72        reg &= BIT(sclk->bit_idx);
  73
  74        return reg ? 1 : 0;
  75}
  76
  77static const struct clk_ops clkgate_separated_ops = {
  78        .enable         = clkgate_separated_enable,
  79        .disable        = clkgate_separated_disable,
  80        .is_enabled     = clkgate_separated_is_enabled,
  81};
  82
  83struct clk *hisi_register_clkgate_sep(struct device *dev, const char *name,
  84                                      const char *parent_name,
  85                                      unsigned long flags,
  86                                      void __iomem *reg, u8 bit_idx,
  87                                      u8 clk_gate_flags, spinlock_t *lock)
  88{
  89        struct clkgate_separated *sclk;
  90        struct clk *clk;
  91        struct clk_init_data init;
  92
  93        sclk = kzalloc(sizeof(*sclk), GFP_KERNEL);
  94        if (!sclk)
  95                return ERR_PTR(-ENOMEM);
  96
  97        init.name = name;
  98        init.ops = &clkgate_separated_ops;
  99        init.flags = flags;
 100        init.parent_names = (parent_name ? &parent_name : NULL);
 101        init.num_parents = (parent_name ? 1 : 0);
 102
 103        sclk->enable = reg + CLKGATE_SEPERATED_ENABLE;
 104        sclk->bit_idx = bit_idx;
 105        sclk->flags = clk_gate_flags;
 106        sclk->hw.init = &init;
 107        sclk->lock = lock;
 108
 109        clk = clk_register(dev, &sclk->hw);
 110        if (IS_ERR(clk))
 111                kfree(sclk);
 112        return clk;
 113}
 114