162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright 2015 Heiko Stuebner <heiko@sntech.de>
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#include <linux/slab.h>
762306a36Sopenharmony_ci#include <linux/clk-provider.h>
862306a36Sopenharmony_ci#include <linux/io.h>
962306a36Sopenharmony_ci#include <linux/spinlock.h>
1062306a36Sopenharmony_ci#include <linux/kernel.h>
1162306a36Sopenharmony_ci#include "clk.h"
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_cistruct rockchip_inv_clock {
1462306a36Sopenharmony_ci	struct clk_hw	hw;
1562306a36Sopenharmony_ci	void __iomem	*reg;
1662306a36Sopenharmony_ci	int		shift;
1762306a36Sopenharmony_ci	int		flags;
1862306a36Sopenharmony_ci	spinlock_t	*lock;
1962306a36Sopenharmony_ci};
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci#define to_inv_clock(_hw) container_of(_hw, struct rockchip_inv_clock, hw)
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci#define INVERTER_MASK 0x1
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_cistatic int rockchip_inv_get_phase(struct clk_hw *hw)
2662306a36Sopenharmony_ci{
2762306a36Sopenharmony_ci	struct rockchip_inv_clock *inv_clock = to_inv_clock(hw);
2862306a36Sopenharmony_ci	u32 val;
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci	val = readl(inv_clock->reg) >> inv_clock->shift;
3162306a36Sopenharmony_ci	val &= INVERTER_MASK;
3262306a36Sopenharmony_ci	return val ? 180 : 0;
3362306a36Sopenharmony_ci}
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_cistatic int rockchip_inv_set_phase(struct clk_hw *hw, int degrees)
3662306a36Sopenharmony_ci{
3762306a36Sopenharmony_ci	struct rockchip_inv_clock *inv_clock = to_inv_clock(hw);
3862306a36Sopenharmony_ci	u32 val;
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci	if (degrees % 180 == 0) {
4162306a36Sopenharmony_ci		val = !!degrees;
4262306a36Sopenharmony_ci	} else {
4362306a36Sopenharmony_ci		pr_err("%s: unsupported phase %d for %s\n",
4462306a36Sopenharmony_ci		       __func__, degrees, clk_hw_get_name(hw));
4562306a36Sopenharmony_ci		return -EINVAL;
4662306a36Sopenharmony_ci	}
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci	if (inv_clock->flags & ROCKCHIP_INVERTER_HIWORD_MASK) {
4962306a36Sopenharmony_ci		writel(HIWORD_UPDATE(val, INVERTER_MASK, inv_clock->shift),
5062306a36Sopenharmony_ci		       inv_clock->reg);
5162306a36Sopenharmony_ci	} else {
5262306a36Sopenharmony_ci		unsigned long flags;
5362306a36Sopenharmony_ci		u32 reg;
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci		spin_lock_irqsave(inv_clock->lock, flags);
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci		reg = readl(inv_clock->reg);
5862306a36Sopenharmony_ci		reg &= ~BIT(inv_clock->shift);
5962306a36Sopenharmony_ci		reg |= val;
6062306a36Sopenharmony_ci		writel(reg, inv_clock->reg);
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci		spin_unlock_irqrestore(inv_clock->lock, flags);
6362306a36Sopenharmony_ci	}
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci	return 0;
6662306a36Sopenharmony_ci}
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_cistatic const struct clk_ops rockchip_inv_clk_ops = {
6962306a36Sopenharmony_ci	.get_phase	= rockchip_inv_get_phase,
7062306a36Sopenharmony_ci	.set_phase	= rockchip_inv_set_phase,
7162306a36Sopenharmony_ci};
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_cistruct clk *rockchip_clk_register_inverter(const char *name,
7462306a36Sopenharmony_ci				const char *const *parent_names, u8 num_parents,
7562306a36Sopenharmony_ci				void __iomem *reg, int shift, int flags,
7662306a36Sopenharmony_ci				spinlock_t *lock)
7762306a36Sopenharmony_ci{
7862306a36Sopenharmony_ci	struct clk_init_data init;
7962306a36Sopenharmony_ci	struct rockchip_inv_clock *inv_clock;
8062306a36Sopenharmony_ci	struct clk *clk;
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci	inv_clock = kmalloc(sizeof(*inv_clock), GFP_KERNEL);
8362306a36Sopenharmony_ci	if (!inv_clock)
8462306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci	init.name = name;
8762306a36Sopenharmony_ci	init.num_parents = num_parents;
8862306a36Sopenharmony_ci	init.flags = CLK_SET_RATE_PARENT;
8962306a36Sopenharmony_ci	init.parent_names = parent_names;
9062306a36Sopenharmony_ci	init.ops = &rockchip_inv_clk_ops;
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	inv_clock->hw.init = &init;
9362306a36Sopenharmony_ci	inv_clock->reg = reg;
9462306a36Sopenharmony_ci	inv_clock->shift = shift;
9562306a36Sopenharmony_ci	inv_clock->flags = flags;
9662306a36Sopenharmony_ci	inv_clock->lock = lock;
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	clk = clk_register(NULL, &inv_clock->hw);
9962306a36Sopenharmony_ci	if (IS_ERR(clk))
10062306a36Sopenharmony_ci		kfree(inv_clock);
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	return clk;
10362306a36Sopenharmony_ci}
104