18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (c) 2017 HiSilicon Technologies Co., Ltd. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Simple HiSilicon phase clock implementation. 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/err.h> 98c2ecf20Sopenharmony_ci#include <linux/io.h> 108c2ecf20Sopenharmony_ci#include <linux/module.h> 118c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 128c2ecf20Sopenharmony_ci#include <linux/slab.h> 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include "clk.h" 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_cistruct clk_hisi_phase { 178c2ecf20Sopenharmony_ci struct clk_hw hw; 188c2ecf20Sopenharmony_ci void __iomem *reg; 198c2ecf20Sopenharmony_ci u32 *phase_degrees; 208c2ecf20Sopenharmony_ci u32 *phase_regvals; 218c2ecf20Sopenharmony_ci u8 phase_num; 228c2ecf20Sopenharmony_ci u32 mask; 238c2ecf20Sopenharmony_ci u8 shift; 248c2ecf20Sopenharmony_ci u8 flags; 258c2ecf20Sopenharmony_ci spinlock_t *lock; 268c2ecf20Sopenharmony_ci}; 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci#define to_clk_hisi_phase(_hw) container_of(_hw, struct clk_hisi_phase, hw) 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_cistatic int hisi_phase_regval_to_degrees(struct clk_hisi_phase *phase, 318c2ecf20Sopenharmony_ci u32 regval) 328c2ecf20Sopenharmony_ci{ 338c2ecf20Sopenharmony_ci int i; 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci for (i = 0; i < phase->phase_num; i++) 368c2ecf20Sopenharmony_ci if (phase->phase_regvals[i] == regval) 378c2ecf20Sopenharmony_ci return phase->phase_degrees[i]; 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci return -EINVAL; 408c2ecf20Sopenharmony_ci} 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_cistatic int hisi_clk_get_phase(struct clk_hw *hw) 438c2ecf20Sopenharmony_ci{ 448c2ecf20Sopenharmony_ci struct clk_hisi_phase *phase = to_clk_hisi_phase(hw); 458c2ecf20Sopenharmony_ci u32 regval; 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci regval = readl(phase->reg); 488c2ecf20Sopenharmony_ci regval = (regval & phase->mask) >> phase->shift; 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci return hisi_phase_regval_to_degrees(phase, regval); 518c2ecf20Sopenharmony_ci} 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_cistatic int hisi_phase_degrees_to_regval(struct clk_hisi_phase *phase, 548c2ecf20Sopenharmony_ci int degrees) 558c2ecf20Sopenharmony_ci{ 568c2ecf20Sopenharmony_ci int i; 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci for (i = 0; i < phase->phase_num; i++) 598c2ecf20Sopenharmony_ci if (phase->phase_degrees[i] == degrees) 608c2ecf20Sopenharmony_ci return phase->phase_regvals[i]; 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci return -EINVAL; 638c2ecf20Sopenharmony_ci} 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_cistatic int hisi_clk_set_phase(struct clk_hw *hw, int degrees) 668c2ecf20Sopenharmony_ci{ 678c2ecf20Sopenharmony_ci struct clk_hisi_phase *phase = to_clk_hisi_phase(hw); 688c2ecf20Sopenharmony_ci unsigned long flags = 0; 698c2ecf20Sopenharmony_ci int regval; 708c2ecf20Sopenharmony_ci u32 val; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci regval = hisi_phase_degrees_to_regval(phase, degrees); 738c2ecf20Sopenharmony_ci if (regval < 0) 748c2ecf20Sopenharmony_ci return regval; 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci spin_lock_irqsave(phase->lock, flags); 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci val = readl(phase->reg); 798c2ecf20Sopenharmony_ci val &= ~phase->mask; 808c2ecf20Sopenharmony_ci val |= regval << phase->shift; 818c2ecf20Sopenharmony_ci writel(val, phase->reg); 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci spin_unlock_irqrestore(phase->lock, flags); 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci return 0; 868c2ecf20Sopenharmony_ci} 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_cistatic const struct clk_ops clk_phase_ops = { 898c2ecf20Sopenharmony_ci .get_phase = hisi_clk_get_phase, 908c2ecf20Sopenharmony_ci .set_phase = hisi_clk_set_phase, 918c2ecf20Sopenharmony_ci}; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_cistruct clk *clk_register_hisi_phase(struct device *dev, 948c2ecf20Sopenharmony_ci const struct hisi_phase_clock *clks, 958c2ecf20Sopenharmony_ci void __iomem *base, spinlock_t *lock) 968c2ecf20Sopenharmony_ci{ 978c2ecf20Sopenharmony_ci struct clk_hisi_phase *phase; 988c2ecf20Sopenharmony_ci struct clk_init_data init; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci phase = devm_kzalloc(dev, sizeof(struct clk_hisi_phase), GFP_KERNEL); 1018c2ecf20Sopenharmony_ci if (!phase) 1028c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci init.name = clks->name; 1058c2ecf20Sopenharmony_ci init.ops = &clk_phase_ops; 1068c2ecf20Sopenharmony_ci init.flags = clks->flags; 1078c2ecf20Sopenharmony_ci init.parent_names = clks->parent_names ? &clks->parent_names : NULL; 1088c2ecf20Sopenharmony_ci init.num_parents = clks->parent_names ? 1 : 0; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci phase->reg = base + clks->offset; 1118c2ecf20Sopenharmony_ci phase->shift = clks->shift; 1128c2ecf20Sopenharmony_ci phase->mask = (BIT(clks->width) - 1) << clks->shift; 1138c2ecf20Sopenharmony_ci phase->lock = lock; 1148c2ecf20Sopenharmony_ci phase->phase_degrees = clks->phase_degrees; 1158c2ecf20Sopenharmony_ci phase->phase_regvals = clks->phase_regvals; 1168c2ecf20Sopenharmony_ci phase->phase_num = clks->phase_num; 1178c2ecf20Sopenharmony_ci phase->hw.init = &init; 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci return devm_clk_register(dev, &phase->hw); 1208c2ecf20Sopenharmony_ci} 1218c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(clk_register_hisi_phase); 122