xref: /openbmc/linux/drivers/clk/visconti/clkc.c (revision c5601e07)
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 
to_visconti_clk_gate(struct clk_hw * hw)23 static 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 
visconti_gate_clk_is_enabled(struct clk_hw * hw)28 static 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(gate->regmap, gate->ckon_offset, &val);
35 	return (val & clk) ? 1 : 0;
36 }
37 
visconti_gate_clk_disable(struct clk_hw * hw)38 static 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(gate->lock, flags);
48 		return;
49 	}
50 
51 	regmap_update_bits(gate->regmap, gate->ckoff_offset, clk, clk);
52 	spin_unlock_irqrestore(gate->lock, flags);
53 }
54 
visconti_gate_clk_enable(struct clk_hw * hw)55 static 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(gate->regmap, gate->ckon_offset, clk, clk);
63 	spin_unlock_irqrestore(gate->lock, flags);
64 
65 	return 0;
66 }
67 
68 static 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 
visconti_clk_register_gate(struct device * dev,const char * name,const char * parent_name,struct regmap * regmap,const struct visconti_clk_gate_table * clks,u32 rson_offset,u32 rsoff_offset,u8 rs_idx,spinlock_t * lock)74 static 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, sizeof(*pdata), GFP_KERNEL);
91 	if (!pdata)
92 		return ERR_PTR(-ENOMEM);
93 
94 	pdata->name = pdata->fw_name = parent_name;
95 
96 	gate = devm_kzalloc(dev, sizeof(*gate), GFP_KERNEL);
97 	if (!gate)
98 		return ERR_PTR(-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(ret);
120 
121 	return hw;
122 }
123 
visconti_clk_register_gates(struct visconti_clk_provider * ctx,const struct visconti_clk_gate_table * clks,int num_gate,const struct visconti_reset_data * reset,spinlock_t * lock)124 int 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, sizeof(*pdata), GFP_KERNEL);
143 		if (!pdata)
144 			return -ENOMEM;
145 
146 		dev_name = devm_kasprintf(dev, GFP_KERNEL, "%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 							    dev_name,
160 							    parent_div_name,
161 							    0, 1,
162 							    clks[i].div);
163 		if (IS_ERR(div_clk))
164 			return PTR_ERR(div_clk);
165 
166 		gate_clk = visconti_clk_register_gate(dev,
167 						      clks[i].name,
168 						      dev_name,
169 						      ctx->regmap,
170 						      &clks[i],
171 						      rson_offset,
172 						      rsoff_offset,
173 						      rs_idx,
174 						      lock);
175 		if (IS_ERR(gate_clk)) {
176 			dev_err(dev, "%s: failed to register clock %s\n",
177 				__func__, clks[i].name);
178 			return PTR_ERR(gate_clk);
179 		}
180 
181 		ctx->clk_data.hws[clks[i].id] = gate_clk;
182 	}
183 
184 	return 0;
185 }
186 
visconti_init_clk(struct device * dev,struct regmap * regmap,unsigned long nr_clks)187 struct 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(-ENOMEM);
197 
198 	for (i = 0; i < nr_clks; ++i)
199 		ctx->clk_data.hws[i] = ERR_PTR(-ENOENT);
200 	ctx->clk_data.num = nr_clks;
201 
202 	ctx->dev = dev;
203 	ctx->regmap = regmap;
204 
205 	return ctx;
206 }
207