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