Merge tag 'linux-kselftest-next-5.15-rc1' of git://git.kernel.org/pub/scm/linux/kerne...
[linux-2.6-microblaze.git] / drivers / clk / rockchip / clk-inverter.c
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * Copyright 2015 Heiko Stuebner <heiko@sntech.de>
4  */
5
6 #include <linux/slab.h>
7 #include <linux/clk-provider.h>
8 #include <linux/io.h>
9 #include <linux/spinlock.h>
10 #include <linux/kernel.h>
11 #include "clk.h"
12
13 struct rockchip_inv_clock {
14         struct clk_hw   hw;
15         void __iomem    *reg;
16         int             shift;
17         int             flags;
18         spinlock_t      *lock;
19 };
20
21 #define to_inv_clock(_hw) container_of(_hw, struct rockchip_inv_clock, hw)
22
23 #define INVERTER_MASK 0x1
24
25 static int rockchip_inv_get_phase(struct clk_hw *hw)
26 {
27         struct rockchip_inv_clock *inv_clock = to_inv_clock(hw);
28         u32 val;
29
30         val = readl(inv_clock->reg) >> inv_clock->shift;
31         val &= INVERTER_MASK;
32         return val ? 180 : 0;
33 }
34
35 static int rockchip_inv_set_phase(struct clk_hw *hw, int degrees)
36 {
37         struct rockchip_inv_clock *inv_clock = to_inv_clock(hw);
38         u32 val;
39
40         if (degrees % 180 == 0) {
41                 val = !!degrees;
42         } else {
43                 pr_err("%s: unsupported phase %d for %s\n",
44                        __func__, degrees, clk_hw_get_name(hw));
45                 return -EINVAL;
46         }
47
48         if (inv_clock->flags & ROCKCHIP_INVERTER_HIWORD_MASK) {
49                 writel(HIWORD_UPDATE(val, INVERTER_MASK, inv_clock->shift),
50                        inv_clock->reg);
51         } else {
52                 unsigned long flags;
53                 u32 reg;
54
55                 spin_lock_irqsave(inv_clock->lock, flags);
56
57                 reg = readl(inv_clock->reg);
58                 reg &= ~BIT(inv_clock->shift);
59                 reg |= val;
60                 writel(reg, inv_clock->reg);
61
62                 spin_unlock_irqrestore(inv_clock->lock, flags);
63         }
64
65         return 0;
66 }
67
68 static const struct clk_ops rockchip_inv_clk_ops = {
69         .get_phase      = rockchip_inv_get_phase,
70         .set_phase      = rockchip_inv_set_phase,
71 };
72
73 struct clk *rockchip_clk_register_inverter(const char *name,
74                                 const char *const *parent_names, u8 num_parents,
75                                 void __iomem *reg, int shift, int flags,
76                                 spinlock_t *lock)
77 {
78         struct clk_init_data init;
79         struct rockchip_inv_clock *inv_clock;
80         struct clk *clk;
81
82         inv_clock = kmalloc(sizeof(*inv_clock), GFP_KERNEL);
83         if (!inv_clock)
84                 return ERR_PTR(-ENOMEM);
85
86         init.name = name;
87         init.num_parents = num_parents;
88         init.flags = CLK_SET_RATE_PARENT;
89         init.parent_names = parent_names;
90         init.ops = &rockchip_inv_clk_ops;
91
92         inv_clock->hw.init = &init;
93         inv_clock->reg = reg;
94         inv_clock->shift = shift;
95         inv_clock->flags = flags;
96         inv_clock->lock = lock;
97
98         clk = clk_register(NULL, &inv_clock->hw);
99         if (IS_ERR(clk))
100                 kfree(inv_clock);
101
102         return clk;
103 }