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