Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * Copyright (c) 2017 HiSilicon Technologies Co., Ltd.
0004  *
0005  * Simple HiSilicon phase clock implementation.
0006  */
0007 
0008 #include <linux/err.h>
0009 #include <linux/io.h>
0010 #include <linux/module.h>
0011 #include <linux/platform_device.h>
0012 #include <linux/slab.h>
0013 
0014 #include "clk.h"
0015 
0016 struct clk_hisi_phase {
0017     struct clk_hw   hw;
0018     void __iomem    *reg;
0019     u32     *phase_degrees;
0020     u32     *phase_regvals;
0021     u8      phase_num;
0022     u32     mask;
0023     u8      shift;
0024     u8      flags;
0025     spinlock_t  *lock;
0026 };
0027 
0028 #define to_clk_hisi_phase(_hw) container_of(_hw, struct clk_hisi_phase, hw)
0029 
0030 static int hisi_phase_regval_to_degrees(struct clk_hisi_phase *phase,
0031                     u32 regval)
0032 {
0033     int i;
0034 
0035     for (i = 0; i < phase->phase_num; i++)
0036         if (phase->phase_regvals[i] == regval)
0037             return phase->phase_degrees[i];
0038 
0039     return -EINVAL;
0040 }
0041 
0042 static int hisi_clk_get_phase(struct clk_hw *hw)
0043 {
0044     struct clk_hisi_phase *phase = to_clk_hisi_phase(hw);
0045     u32 regval;
0046 
0047     regval = readl(phase->reg);
0048     regval = (regval & phase->mask) >> phase->shift;
0049 
0050     return hisi_phase_regval_to_degrees(phase, regval);
0051 }
0052 
0053 static int hisi_phase_degrees_to_regval(struct clk_hisi_phase *phase,
0054                     int degrees)
0055 {
0056     int i;
0057 
0058     for (i = 0; i < phase->phase_num; i++)
0059         if (phase->phase_degrees[i] == degrees)
0060             return phase->phase_regvals[i];
0061 
0062     return -EINVAL;
0063 }
0064 
0065 static int hisi_clk_set_phase(struct clk_hw *hw, int degrees)
0066 {
0067     struct clk_hisi_phase *phase = to_clk_hisi_phase(hw);
0068     unsigned long flags = 0;
0069     int regval;
0070     u32 val;
0071 
0072     regval = hisi_phase_degrees_to_regval(phase, degrees);
0073     if (regval < 0)
0074         return regval;
0075 
0076     spin_lock_irqsave(phase->lock, flags);
0077 
0078     val = readl(phase->reg);
0079     val &= ~phase->mask;
0080     val |= regval << phase->shift;
0081     writel(val, phase->reg);
0082 
0083     spin_unlock_irqrestore(phase->lock, flags);
0084 
0085     return 0;
0086 }
0087 
0088 static const struct clk_ops clk_phase_ops = {
0089     .get_phase = hisi_clk_get_phase,
0090     .set_phase = hisi_clk_set_phase,
0091 };
0092 
0093 struct clk *clk_register_hisi_phase(struct device *dev,
0094         const struct hisi_phase_clock *clks,
0095         void __iomem *base, spinlock_t *lock)
0096 {
0097     struct clk_hisi_phase *phase;
0098     struct clk_init_data init;
0099 
0100     phase = devm_kzalloc(dev, sizeof(struct clk_hisi_phase), GFP_KERNEL);
0101     if (!phase)
0102         return ERR_PTR(-ENOMEM);
0103 
0104     init.name = clks->name;
0105     init.ops = &clk_phase_ops;
0106     init.flags = clks->flags;
0107     init.parent_names = clks->parent_names ? &clks->parent_names : NULL;
0108     init.num_parents = clks->parent_names ? 1 : 0;
0109 
0110     phase->reg = base + clks->offset;
0111     phase->shift = clks->shift;
0112     phase->mask = (BIT(clks->width) - 1) << clks->shift;
0113     phase->lock = lock;
0114     phase->phase_degrees = clks->phase_degrees;
0115     phase->phase_regvals = clks->phase_regvals;
0116     phase->phase_num = clks->phase_num;
0117     phase->hw.init = &init;
0118 
0119     return devm_clk_register(dev, &phase->hw);
0120 }
0121 EXPORT_SYMBOL_GPL(clk_register_hisi_phase);