1// SPDX-License-Identifier: GPL-2.0-only
2/*
3 * Toshiba Visconti clock controller
4 *
5 * Copyright (c) 2021 TOSHIBA CORPORATION
6 * Copyright (c) 2021 Toshiba Electronic Devices & Storage Corporation
7 *
8 * Nobuhiro Iwamatsu <nobuhiro1.iwamatsu@toshiba.co.jp>
9 */
10
11#include <linux/clk-provider.h>
12#include <linux/delay.h>
13#include <linux/device.h>
14#include <linux/io.h>
15#include <linux/of.h>
16#include <linux/of_address.h>
17#include <linux/regmap.h>
18#include <linux/slab.h>
19#include <linux/string.h>
20
21#include "clkc.h"
22
23static inline struct visconti_clk_gate *to_visconti_clk_gate(struct clk_hw *hw)
24{
25 return container_of(hw, struct visconti_clk_gate, hw);
26}
27
28static int visconti_gate_clk_is_enabled(struct clk_hw *hw)
29{
30 struct visconti_clk_gate *gate = to_visconti_clk_gate(hw);
31 u32 clk = BIT(gate->ck_idx);
32 u32 val;
33
34 regmap_read(map: gate->regmap, reg: gate->ckon_offset, val: &val);
35 return (val & clk) ? 1 : 0;
36}
37
38static void visconti_gate_clk_disable(struct clk_hw *hw)
39{
40 struct visconti_clk_gate *gate = to_visconti_clk_gate(hw);
41 u32 clk = BIT(gate->ck_idx);
42 unsigned long flags;
43
44 spin_lock_irqsave(gate->lock, flags);
45
46 if (!visconti_gate_clk_is_enabled(hw)) {
47 spin_unlock_irqrestore(lock: gate->lock, flags);
48 return;
49 }
50
51 regmap_update_bits(map: gate->regmap, reg: gate->ckoff_offset, mask: clk, val: clk);
52 spin_unlock_irqrestore(lock: gate->lock, flags);
53}
54
55static int visconti_gate_clk_enable(struct clk_hw *hw)
56{
57 struct visconti_clk_gate *gate = to_visconti_clk_gate(hw);
58 u32 clk = BIT(gate->ck_idx);
59 unsigned long flags;
60
61 spin_lock_irqsave(gate->lock, flags);
62 regmap_update_bits(map: gate->regmap, reg: gate->ckon_offset, mask: clk, val: clk);
63 spin_unlock_irqrestore(lock: gate->lock, flags);
64
65 return 0;
66}
67
68static const struct clk_ops visconti_clk_gate_ops = {
69 .enable = visconti_gate_clk_enable,
70 .disable = visconti_gate_clk_disable,
71 .is_enabled = visconti_gate_clk_is_enabled,
72};
73
74static struct clk_hw *visconti_clk_register_gate(struct device *dev,
75 const char *name,
76 const char *parent_name,
77 struct regmap *regmap,
78 const struct visconti_clk_gate_table *clks,
79 u32 rson_offset,
80 u32 rsoff_offset,
81 u8 rs_idx,
82 spinlock_t *lock)
83{
84 struct visconti_clk_gate *gate;
85 struct clk_parent_data *pdata;
86 struct clk_init_data init;
87 struct clk_hw *hw;
88 int ret;
89
90 pdata = devm_kzalloc(dev, size: sizeof(*pdata), GFP_KERNEL);
91 if (!pdata)
92 return ERR_PTR(error: -ENOMEM);
93
94 pdata->name = pdata->fw_name = parent_name;
95
96 gate = devm_kzalloc(dev, size: sizeof(*gate), GFP_KERNEL);
97 if (!gate)
98 return ERR_PTR(error: -ENOMEM);
99
100 init.name = name;
101 init.ops = &visconti_clk_gate_ops;
102 init.flags = clks->flags;
103 init.parent_data = pdata;
104 init.num_parents = 1;
105
106 gate->regmap = regmap;
107 gate->ckon_offset = clks->ckon_offset;
108 gate->ckoff_offset = clks->ckoff_offset;
109 gate->ck_idx = clks->ck_idx;
110 gate->rson_offset = rson_offset;
111 gate->rsoff_offset = rsoff_offset;
112 gate->rs_idx = rs_idx;
113 gate->lock = lock;
114 gate->hw.init = &init;
115
116 hw = &gate->hw;
117 ret = devm_clk_hw_register(dev, hw);
118 if (ret)
119 hw = ERR_PTR(error: ret);
120
121 return hw;
122}
123
124int visconti_clk_register_gates(struct visconti_clk_provider *ctx,
125 const struct visconti_clk_gate_table *clks,
126 int num_gate,
127 const struct visconti_reset_data *reset,
128 spinlock_t *lock)
129{
130 struct device *dev = ctx->dev;
131 int i;
132
133 for (i = 0; i < num_gate; i++) {
134 const char *parent_div_name = clks[i].parent_data[0].name;
135 struct clk_parent_data *pdata;
136 u32 rson_offset, rsoff_offset;
137 struct clk_hw *gate_clk;
138 struct clk_hw *div_clk;
139 char *dev_name;
140 u8 rs_idx;
141
142 pdata = devm_kzalloc(dev, size: sizeof(*pdata), GFP_KERNEL);
143 if (!pdata)
144 return -ENOMEM;
145
146 dev_name = devm_kasprintf(dev, GFP_KERNEL, fmt: "%s_div", clks[i].name);
147 if (!dev_name)
148 return -ENOMEM;
149
150 if (clks[i].rs_id != NO_RESET) {
151 rson_offset = reset[clks[i].rs_id].rson_offset;
152 rsoff_offset = reset[clks[i].rs_id].rsoff_offset;
153 rs_idx = reset[clks[i].rs_id].rs_idx;
154 } else {
155 rson_offset = rsoff_offset = rs_idx = -1;
156 }
157
158 div_clk = devm_clk_hw_register_fixed_factor(dev,
159 name: dev_name,
160 parent_name: parent_div_name,
161 flags: 0, mult: 1,
162 div: clks[i].div);
163 if (IS_ERR(ptr: div_clk))
164 return PTR_ERR(ptr: div_clk);
165
166 gate_clk = visconti_clk_register_gate(dev,
167 name: clks[i].name,
168 parent_name: dev_name,
169 regmap: ctx->regmap,
170 clks: &clks[i],
171 rson_offset,
172 rsoff_offset,
173 rs_idx,
174 lock);
175 if (IS_ERR(ptr: gate_clk)) {
176 dev_err(dev, "%s: failed to register clock %s\n",
177 __func__, clks[i].name);
178 return PTR_ERR(ptr: gate_clk);
179 }
180
181 ctx->clk_data.hws[clks[i].id] = gate_clk;
182 }
183
184 return 0;
185}
186
187struct visconti_clk_provider *visconti_init_clk(struct device *dev,
188 struct regmap *regmap,
189 unsigned long nr_clks)
190{
191 struct visconti_clk_provider *ctx;
192 int i;
193
194 ctx = devm_kzalloc(dev, struct_size(ctx, clk_data.hws, nr_clks), GFP_KERNEL);
195 if (!ctx)
196 return ERR_PTR(error: -ENOMEM);
197
198 for (i = 0; i < nr_clks; ++i)
199 ctx->clk_data.hws[i] = ERR_PTR(error: -ENOENT);
200 ctx->clk_data.num = nr_clks;
201
202 ctx->dev = dev;
203 ctx->regmap = regmap;
204
205 return ctx;
206}
207

source code of linux/drivers/clk/visconti/clkc.c