18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright 2015 Heiko Stuebner <heiko@sntech.de>
48c2ecf20Sopenharmony_ci */
58c2ecf20Sopenharmony_ci
68c2ecf20Sopenharmony_ci#include <linux/slab.h>
78c2ecf20Sopenharmony_ci#include <linux/clk-provider.h>
88c2ecf20Sopenharmony_ci#include <linux/io.h>
98c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
108c2ecf20Sopenharmony_ci#include <linux/kernel.h>
118c2ecf20Sopenharmony_ci#include "clk.h"
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_cistruct rockchip_inv_clock {
148c2ecf20Sopenharmony_ci	struct clk_hw	hw;
158c2ecf20Sopenharmony_ci	void __iomem	*reg;
168c2ecf20Sopenharmony_ci	int		shift;
178c2ecf20Sopenharmony_ci	int		flags;
188c2ecf20Sopenharmony_ci	spinlock_t	*lock;
198c2ecf20Sopenharmony_ci};
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci#define to_inv_clock(_hw) container_of(_hw, struct rockchip_inv_clock, hw)
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci#define INVERTER_MASK 0x1
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_cistatic int rockchip_inv_get_phase(struct clk_hw *hw)
268c2ecf20Sopenharmony_ci{
278c2ecf20Sopenharmony_ci	struct rockchip_inv_clock *inv_clock = to_inv_clock(hw);
288c2ecf20Sopenharmony_ci	u32 val;
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci	val = readl(inv_clock->reg) >> inv_clock->shift;
318c2ecf20Sopenharmony_ci	val &= INVERTER_MASK;
328c2ecf20Sopenharmony_ci	return val ? 180 : 0;
338c2ecf20Sopenharmony_ci}
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_cistatic int rockchip_inv_set_phase(struct clk_hw *hw, int degrees)
368c2ecf20Sopenharmony_ci{
378c2ecf20Sopenharmony_ci	struct rockchip_inv_clock *inv_clock = to_inv_clock(hw);
388c2ecf20Sopenharmony_ci	u32 val;
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci	if (degrees % 180 == 0) {
418c2ecf20Sopenharmony_ci		val = !!degrees;
428c2ecf20Sopenharmony_ci	} else {
438c2ecf20Sopenharmony_ci		pr_err("%s: unsupported phase %d for %s\n",
448c2ecf20Sopenharmony_ci		       __func__, degrees, clk_hw_get_name(hw));
458c2ecf20Sopenharmony_ci		return -EINVAL;
468c2ecf20Sopenharmony_ci	}
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci	if (inv_clock->flags & ROCKCHIP_INVERTER_HIWORD_MASK) {
498c2ecf20Sopenharmony_ci		writel(HIWORD_UPDATE(val, INVERTER_MASK, inv_clock->shift),
508c2ecf20Sopenharmony_ci		       inv_clock->reg);
518c2ecf20Sopenharmony_ci	} else {
528c2ecf20Sopenharmony_ci		unsigned long flags;
538c2ecf20Sopenharmony_ci		u32 reg;
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci		spin_lock_irqsave(inv_clock->lock, flags);
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci		reg = readl(inv_clock->reg);
588c2ecf20Sopenharmony_ci		reg &= ~BIT(inv_clock->shift);
598c2ecf20Sopenharmony_ci		reg |= val;
608c2ecf20Sopenharmony_ci		writel(reg, inv_clock->reg);
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(inv_clock->lock, flags);
638c2ecf20Sopenharmony_ci	}
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci	return 0;
668c2ecf20Sopenharmony_ci}
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_cistatic const struct clk_ops rockchip_inv_clk_ops = {
698c2ecf20Sopenharmony_ci	.get_phase	= rockchip_inv_get_phase,
708c2ecf20Sopenharmony_ci	.set_phase	= rockchip_inv_set_phase,
718c2ecf20Sopenharmony_ci};
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_cistruct clk *rockchip_clk_register_inverter(const char *name,
748c2ecf20Sopenharmony_ci				const char *const *parent_names, u8 num_parents,
758c2ecf20Sopenharmony_ci				void __iomem *reg, int shift, int flags,
768c2ecf20Sopenharmony_ci				spinlock_t *lock)
778c2ecf20Sopenharmony_ci{
788c2ecf20Sopenharmony_ci	struct clk_init_data init;
798c2ecf20Sopenharmony_ci	struct rockchip_inv_clock *inv_clock;
808c2ecf20Sopenharmony_ci	struct clk *clk;
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci	inv_clock = kmalloc(sizeof(*inv_clock), GFP_KERNEL);
838c2ecf20Sopenharmony_ci	if (!inv_clock)
848c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci	init.name = name;
878c2ecf20Sopenharmony_ci	init.num_parents = num_parents;
888c2ecf20Sopenharmony_ci	init.flags = CLK_SET_RATE_PARENT;
898c2ecf20Sopenharmony_ci	init.parent_names = parent_names;
908c2ecf20Sopenharmony_ci	init.ops = &rockchip_inv_clk_ops;
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci	inv_clock->hw.init = &init;
938c2ecf20Sopenharmony_ci	inv_clock->reg = reg;
948c2ecf20Sopenharmony_ci	inv_clock->shift = shift;
958c2ecf20Sopenharmony_ci	inv_clock->flags = flags;
968c2ecf20Sopenharmony_ci	inv_clock->lock = lock;
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci	clk = clk_register(NULL, &inv_clock->hw);
998c2ecf20Sopenharmony_ci	if (IS_ERR(clk))
1008c2ecf20Sopenharmony_ci		kfree(inv_clock);
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci	return clk;
1038c2ecf20Sopenharmony_ci}
104