GNU Linux-libre 6.8.9-gnu
[releases.git] / drivers / clk / mmp / clk-apbc.c
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * mmp APB clock operation source file
4  *
5  * Copyright (C) 2012 Marvell
6  * Chao Xie <xiechao.mail@gmail.com>
7  */
8
9 #include <linux/kernel.h>
10 #include <linux/io.h>
11 #include <linux/err.h>
12 #include <linux/delay.h>
13 #include <linux/slab.h>
14
15 #include "clk.h"
16
17 /* Common APB clock register bit definitions */
18 #define APBC_APBCLK     (1 << 0)  /* APB Bus Clock Enable */
19 #define APBC_FNCLK      (1 << 1)  /* Functional Clock Enable */
20 #define APBC_RST        (1 << 2)  /* Reset Generation */
21 #define APBC_POWER      (1 << 7)  /* Reset Generation */
22
23 #define to_clk_apbc(hw) container_of(hw, struct clk_apbc, hw)
24 struct clk_apbc {
25         struct clk_hw           hw;
26         void __iomem            *base;
27         unsigned int            delay;
28         unsigned int            flags;
29         spinlock_t              *lock;
30 };
31
32 static int clk_apbc_prepare(struct clk_hw *hw)
33 {
34         struct clk_apbc *apbc = to_clk_apbc(hw);
35         unsigned int data;
36         unsigned long flags = 0;
37
38         /*
39          * It may share same register as MUX clock,
40          * and it will impact FNCLK enable. Spinlock is needed
41          */
42         if (apbc->lock)
43                 spin_lock_irqsave(apbc->lock, flags);
44
45         data = readl_relaxed(apbc->base);
46         if (apbc->flags & APBC_POWER_CTRL)
47                 data |= APBC_POWER;
48         data |= APBC_FNCLK;
49         writel_relaxed(data, apbc->base);
50
51         if (apbc->lock)
52                 spin_unlock_irqrestore(apbc->lock, flags);
53
54         udelay(apbc->delay);
55
56         if (apbc->lock)
57                 spin_lock_irqsave(apbc->lock, flags);
58
59         data = readl_relaxed(apbc->base);
60         data |= APBC_APBCLK;
61         writel_relaxed(data, apbc->base);
62
63         if (apbc->lock)
64                 spin_unlock_irqrestore(apbc->lock, flags);
65
66         udelay(apbc->delay);
67
68         if (!(apbc->flags & APBC_NO_BUS_CTRL)) {
69                 if (apbc->lock)
70                         spin_lock_irqsave(apbc->lock, flags);
71
72                 data = readl_relaxed(apbc->base);
73                 data &= ~APBC_RST;
74                 writel_relaxed(data, apbc->base);
75
76                 if (apbc->lock)
77                         spin_unlock_irqrestore(apbc->lock, flags);
78         }
79
80         return 0;
81 }
82
83 static void clk_apbc_unprepare(struct clk_hw *hw)
84 {
85         struct clk_apbc *apbc = to_clk_apbc(hw);
86         unsigned long data;
87         unsigned long flags = 0;
88
89         if (apbc->lock)
90                 spin_lock_irqsave(apbc->lock, flags);
91
92         data = readl_relaxed(apbc->base);
93         if (apbc->flags & APBC_POWER_CTRL)
94                 data &= ~APBC_POWER;
95         data &= ~APBC_FNCLK;
96         writel_relaxed(data, apbc->base);
97
98         if (apbc->lock)
99                 spin_unlock_irqrestore(apbc->lock, flags);
100
101         udelay(10);
102
103         if (apbc->lock)
104                 spin_lock_irqsave(apbc->lock, flags);
105
106         data = readl_relaxed(apbc->base);
107         data &= ~APBC_APBCLK;
108         writel_relaxed(data, apbc->base);
109
110         if (apbc->lock)
111                 spin_unlock_irqrestore(apbc->lock, flags);
112 }
113
114 static const struct clk_ops clk_apbc_ops = {
115         .prepare = clk_apbc_prepare,
116         .unprepare = clk_apbc_unprepare,
117 };
118
119 struct clk *mmp_clk_register_apbc(const char *name, const char *parent_name,
120                 void __iomem *base, unsigned int delay,
121                 unsigned int apbc_flags, spinlock_t *lock)
122 {
123         struct clk_apbc *apbc;
124         struct clk *clk;
125         struct clk_init_data init;
126
127         apbc = kzalloc(sizeof(*apbc), GFP_KERNEL);
128         if (!apbc)
129                 return NULL;
130
131         init.name = name;
132         init.ops = &clk_apbc_ops;
133         init.flags = CLK_SET_RATE_PARENT;
134         init.parent_names = (parent_name ? &parent_name : NULL);
135         init.num_parents = (parent_name ? 1 : 0);
136
137         apbc->base = base;
138         apbc->delay = delay;
139         apbc->flags = apbc_flags;
140         apbc->lock = lock;
141         apbc->hw.init = &init;
142
143         clk = clk_register(NULL, &apbc->hw);
144         if (IS_ERR(clk))
145                 kfree(apbc);
146
147         return clk;
148 }