linux/drivers/clk/rockchip/clk-ddr.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 * Copyright (c) 2016 Rockchip Electronics Co. Ltd.
   4 * Author: Lin Huang <hl@rock-chips.com>
   5 */
   6
   7#include <linux/arm-smccc.h>
   8#include <linux/clk.h>
   9#include <linux/clk-provider.h>
  10#include <linux/io.h>
  11#include <linux/slab.h>
  12#include <soc/rockchip/rockchip_sip.h>
  13#include "clk.h"
  14
  15struct rockchip_ddrclk {
  16        struct clk_hw   hw;
  17        void __iomem    *reg_base;
  18        int             mux_offset;
  19        int             mux_shift;
  20        int             mux_width;
  21        int             div_shift;
  22        int             div_width;
  23        int             ddr_flag;
  24        spinlock_t      *lock;
  25};
  26
  27#define to_rockchip_ddrclk_hw(hw) container_of(hw, struct rockchip_ddrclk, hw)
  28
  29static int rockchip_ddrclk_sip_set_rate(struct clk_hw *hw, unsigned long drate,
  30                                        unsigned long prate)
  31{
  32        struct rockchip_ddrclk *ddrclk = to_rockchip_ddrclk_hw(hw);
  33        unsigned long flags;
  34        struct arm_smccc_res res;
  35
  36        spin_lock_irqsave(ddrclk->lock, flags);
  37        arm_smccc_smc(ROCKCHIP_SIP_DRAM_FREQ, drate, 0,
  38                      ROCKCHIP_SIP_CONFIG_DRAM_SET_RATE,
  39                      0, 0, 0, 0, &res);
  40        spin_unlock_irqrestore(ddrclk->lock, flags);
  41
  42        return res.a0;
  43}
  44
  45static unsigned long
  46rockchip_ddrclk_sip_recalc_rate(struct clk_hw *hw,
  47                                unsigned long parent_rate)
  48{
  49        struct arm_smccc_res res;
  50
  51        arm_smccc_smc(ROCKCHIP_SIP_DRAM_FREQ, 0, 0,
  52                      ROCKCHIP_SIP_CONFIG_DRAM_GET_RATE,
  53                      0, 0, 0, 0, &res);
  54
  55        return res.a0;
  56}
  57
  58static long rockchip_ddrclk_sip_round_rate(struct clk_hw *hw,
  59                                           unsigned long rate,
  60                                           unsigned long *prate)
  61{
  62        struct arm_smccc_res res;
  63
  64        arm_smccc_smc(ROCKCHIP_SIP_DRAM_FREQ, rate, 0,
  65                      ROCKCHIP_SIP_CONFIG_DRAM_ROUND_RATE,
  66                      0, 0, 0, 0, &res);
  67
  68        return res.a0;
  69}
  70
  71static u8 rockchip_ddrclk_get_parent(struct clk_hw *hw)
  72{
  73        struct rockchip_ddrclk *ddrclk = to_rockchip_ddrclk_hw(hw);
  74        u32 val;
  75
  76        val = readl(ddrclk->reg_base +
  77                        ddrclk->mux_offset) >> ddrclk->mux_shift;
  78        val &= GENMASK(ddrclk->mux_width - 1, 0);
  79
  80        return val;
  81}
  82
  83static const struct clk_ops rockchip_ddrclk_sip_ops = {
  84        .recalc_rate = rockchip_ddrclk_sip_recalc_rate,
  85        .set_rate = rockchip_ddrclk_sip_set_rate,
  86        .round_rate = rockchip_ddrclk_sip_round_rate,
  87        .get_parent = rockchip_ddrclk_get_parent,
  88};
  89
  90struct clk *rockchip_clk_register_ddrclk(const char *name, int flags,
  91                                         const char *const *parent_names,
  92                                         u8 num_parents, int mux_offset,
  93                                         int mux_shift, int mux_width,
  94                                         int div_shift, int div_width,
  95                                         int ddr_flag, void __iomem *reg_base,
  96                                         spinlock_t *lock)
  97{
  98        struct rockchip_ddrclk *ddrclk;
  99        struct clk_init_data init;
 100        struct clk *clk;
 101
 102        ddrclk = kzalloc(sizeof(*ddrclk), GFP_KERNEL);
 103        if (!ddrclk)
 104                return ERR_PTR(-ENOMEM);
 105
 106        init.name = name;
 107        init.parent_names = parent_names;
 108        init.num_parents = num_parents;
 109
 110        init.flags = flags;
 111        init.flags |= CLK_SET_RATE_NO_REPARENT;
 112
 113        switch (ddr_flag) {
 114        case ROCKCHIP_DDRCLK_SIP:
 115                init.ops = &rockchip_ddrclk_sip_ops;
 116                break;
 117        default:
 118                pr_err("%s: unsupported ddrclk type %d\n", __func__, ddr_flag);
 119                kfree(ddrclk);
 120                return ERR_PTR(-EINVAL);
 121        }
 122
 123        ddrclk->reg_base = reg_base;
 124        ddrclk->lock = lock;
 125        ddrclk->hw.init = &init;
 126        ddrclk->mux_offset = mux_offset;
 127        ddrclk->mux_shift = mux_shift;
 128        ddrclk->mux_width = mux_width;
 129        ddrclk->div_shift = div_shift;
 130        ddrclk->div_width = div_width;
 131        ddrclk->ddr_flag = ddr_flag;
 132
 133        clk = clk_register(NULL, &ddrclk->hw);
 134        if (IS_ERR(clk))
 135                kfree(ddrclk);
 136
 137        return clk;
 138}
 139EXPORT_SYMBOL_GPL(rockchip_clk_register_ddrclk);
 140