1 // SPDX-License-Identifier: GPL-2.0
3 * Copyright (c) 2017 HiSilicon Technologies Co., Ltd.
5 * Simple HiSilicon phase clock implementation.
10 #include <linux/module.h>
11 #include <linux/platform_device.h>
12 #include <linux/slab.h>
16 struct clk_hisi_phase
{
28 #define to_clk_hisi_phase(_hw) container_of(_hw, struct clk_hisi_phase, hw)
30 static int hisi_phase_regval_to_degrees(struct clk_hisi_phase
*phase
,
35 for (i
= 0; i
< phase
->phase_num
; i
++)
36 if (phase
->phase_regvals
[i
] == regval
)
37 return phase
->phase_degrees
[i
];
42 static int hisi_clk_get_phase(struct clk_hw
*hw
)
44 struct clk_hisi_phase
*phase
= to_clk_hisi_phase(hw
);
47 regval
= readl(phase
->reg
);
48 regval
= (regval
& phase
->mask
) >> phase
->shift
;
50 return hisi_phase_regval_to_degrees(phase
, regval
);
53 static int hisi_phase_degrees_to_regval(struct clk_hisi_phase
*phase
,
58 for (i
= 0; i
< phase
->phase_num
; i
++)
59 if (phase
->phase_degrees
[i
] == degrees
)
60 return phase
->phase_regvals
[i
];
65 static int hisi_clk_set_phase(struct clk_hw
*hw
, int degrees
)
67 struct clk_hisi_phase
*phase
= to_clk_hisi_phase(hw
);
68 unsigned long flags
= 0;
72 regval
= hisi_phase_degrees_to_regval(phase
, degrees
);
76 spin_lock_irqsave(phase
->lock
, flags
);
78 val
= readl(phase
->reg
);
80 val
|= regval
<< phase
->shift
;
81 writel(val
, phase
->reg
);
83 spin_unlock_irqrestore(phase
->lock
, flags
);
88 static const struct clk_ops clk_phase_ops
= {
89 .get_phase
= hisi_clk_get_phase
,
90 .set_phase
= hisi_clk_set_phase
,
93 struct clk
*clk_register_hisi_phase(struct device
*dev
,
94 const struct hisi_phase_clock
*clks
,
95 void __iomem
*base
, spinlock_t
*lock
)
97 struct clk_hisi_phase
*phase
;
98 struct clk_init_data init
;
100 phase
= devm_kzalloc(dev
, sizeof(struct clk_hisi_phase
), GFP_KERNEL
);
102 return ERR_PTR(-ENOMEM
);
104 init
.name
= clks
->name
;
105 init
.ops
= &clk_phase_ops
;
106 init
.flags
= clks
->flags
;
107 init
.parent_names
= clks
->parent_names
? &clks
->parent_names
: NULL
;
108 init
.num_parents
= clks
->parent_names
? 1 : 0;
110 phase
->reg
= base
+ clks
->offset
;
111 phase
->shift
= clks
->shift
;
112 phase
->mask
= (BIT(clks
->width
) - 1) << clks
->shift
;
114 phase
->phase_degrees
= clks
->phase_degrees
;
115 phase
->phase_regvals
= clks
->phase_regvals
;
116 phase
->phase_num
= clks
->phase_num
;
117 phase
->hw
.init
= &init
;
119 return devm_clk_register(dev
, &phase
->hw
);
121 EXPORT_SYMBOL_GPL(clk_register_hisi_phase
);