• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // SPDX-License-Identifier: GPL-2.0
2 //
3 // Register map access API - MMIO support
4 //
5 // Copyright (c) 2012, NVIDIA CORPORATION.  All rights reserved.
6 
7 #include <linux/clk.h>
8 #include <linux/err.h>
9 #include <linux/io.h>
10 #include <linux/module.h>
11 #include <linux/regmap.h>
12 #include <linux/slab.h>
13 
14 #include "internal.h"
15 
16 struct regmap_mmio_context {
17 	void __iomem *regs;
18 	unsigned val_bytes;
19 
20 	bool attached_clk;
21 	struct clk *clk;
22 
23 	void (*reg_write)(struct regmap_mmio_context *ctx,
24 			  unsigned int reg, unsigned int val);
25 	unsigned int (*reg_read)(struct regmap_mmio_context *ctx,
26 			         unsigned int reg);
27 };
28 
regmap_mmio_regbits_check(size_t reg_bits)29 static int regmap_mmio_regbits_check(size_t reg_bits)
30 {
31 	switch (reg_bits) {
32 	case 8:
33 	case 16:
34 	case 32:
35 #ifdef CONFIG_64BIT
36 	case 64:
37 #endif
38 		return 0;
39 	default:
40 		return -EINVAL;
41 	}
42 }
43 
regmap_mmio_get_min_stride(size_t val_bits)44 static int regmap_mmio_get_min_stride(size_t val_bits)
45 {
46 	int min_stride;
47 
48 	switch (val_bits) {
49 	case 8:
50 		/* The core treats 0 as 1 */
51 		min_stride = 0;
52 		return 0;
53 	case 16:
54 		min_stride = 2;
55 		break;
56 	case 32:
57 		min_stride = 4;
58 		break;
59 #ifdef CONFIG_64BIT
60 	case 64:
61 		min_stride = 8;
62 		break;
63 #endif
64 	default:
65 		return -EINVAL;
66 	}
67 
68 	return min_stride;
69 }
70 
regmap_mmio_write8(struct regmap_mmio_context * ctx,unsigned int reg,unsigned int val)71 static void regmap_mmio_write8(struct regmap_mmio_context *ctx,
72 				unsigned int reg,
73 				unsigned int val)
74 {
75 	writeb(val, ctx->regs + reg);
76 }
77 
regmap_mmio_write16le(struct regmap_mmio_context * ctx,unsigned int reg,unsigned int val)78 static void regmap_mmio_write16le(struct regmap_mmio_context *ctx,
79 				  unsigned int reg,
80 				  unsigned int val)
81 {
82 	writew(val, ctx->regs + reg);
83 }
84 
regmap_mmio_write16be(struct regmap_mmio_context * ctx,unsigned int reg,unsigned int val)85 static void regmap_mmio_write16be(struct regmap_mmio_context *ctx,
86 				  unsigned int reg,
87 				  unsigned int val)
88 {
89 	iowrite16be(val, ctx->regs + reg);
90 }
91 
regmap_mmio_write32le(struct regmap_mmio_context * ctx,unsigned int reg,unsigned int val)92 static void regmap_mmio_write32le(struct regmap_mmio_context *ctx,
93 				  unsigned int reg,
94 				  unsigned int val)
95 {
96 	writel(val, ctx->regs + reg);
97 }
98 
regmap_mmio_write32be(struct regmap_mmio_context * ctx,unsigned int reg,unsigned int val)99 static void regmap_mmio_write32be(struct regmap_mmio_context *ctx,
100 				  unsigned int reg,
101 				  unsigned int val)
102 {
103 	iowrite32be(val, ctx->regs + reg);
104 }
105 
106 #ifdef CONFIG_64BIT
regmap_mmio_write64le(struct regmap_mmio_context * ctx,unsigned int reg,unsigned int val)107 static void regmap_mmio_write64le(struct regmap_mmio_context *ctx,
108 				  unsigned int reg,
109 				  unsigned int val)
110 {
111 	writeq(val, ctx->regs + reg);
112 }
113 #endif
114 
regmap_mmio_write(void * context,unsigned int reg,unsigned int val)115 static int regmap_mmio_write(void *context, unsigned int reg, unsigned int val)
116 {
117 	struct regmap_mmio_context *ctx = context;
118 	int ret;
119 
120 	if (!IS_ERR(ctx->clk)) {
121 		ret = clk_enable(ctx->clk);
122 		if (ret < 0)
123 			return ret;
124 	}
125 
126 	ctx->reg_write(ctx, reg, val);
127 
128 	if (!IS_ERR(ctx->clk))
129 		clk_disable(ctx->clk);
130 
131 	return 0;
132 }
133 
regmap_mmio_read8(struct regmap_mmio_context * ctx,unsigned int reg)134 static unsigned int regmap_mmio_read8(struct regmap_mmio_context *ctx,
135 				      unsigned int reg)
136 {
137 	return readb(ctx->regs + reg);
138 }
139 
regmap_mmio_read16le(struct regmap_mmio_context * ctx,unsigned int reg)140 static unsigned int regmap_mmio_read16le(struct regmap_mmio_context *ctx,
141 				         unsigned int reg)
142 {
143 	return readw(ctx->regs + reg);
144 }
145 
regmap_mmio_read16be(struct regmap_mmio_context * ctx,unsigned int reg)146 static unsigned int regmap_mmio_read16be(struct regmap_mmio_context *ctx,
147 				         unsigned int reg)
148 {
149 	return ioread16be(ctx->regs + reg);
150 }
151 
regmap_mmio_read32le(struct regmap_mmio_context * ctx,unsigned int reg)152 static unsigned int regmap_mmio_read32le(struct regmap_mmio_context *ctx,
153 				         unsigned int reg)
154 {
155 	return readl(ctx->regs + reg);
156 }
157 
regmap_mmio_read32be(struct regmap_mmio_context * ctx,unsigned int reg)158 static unsigned int regmap_mmio_read32be(struct regmap_mmio_context *ctx,
159 				         unsigned int reg)
160 {
161 	return ioread32be(ctx->regs + reg);
162 }
163 
164 #ifdef CONFIG_64BIT
regmap_mmio_read64le(struct regmap_mmio_context * ctx,unsigned int reg)165 static unsigned int regmap_mmio_read64le(struct regmap_mmio_context *ctx,
166 				         unsigned int reg)
167 {
168 	return readq(ctx->regs + reg);
169 }
170 #endif
171 
regmap_mmio_read(void * context,unsigned int reg,unsigned int * val)172 static int regmap_mmio_read(void *context, unsigned int reg, unsigned int *val)
173 {
174 	struct regmap_mmio_context *ctx = context;
175 	int ret;
176 
177 	if (!IS_ERR(ctx->clk)) {
178 		ret = clk_enable(ctx->clk);
179 		if (ret < 0)
180 			return ret;
181 	}
182 
183 	*val = ctx->reg_read(ctx, reg);
184 
185 	if (!IS_ERR(ctx->clk))
186 		clk_disable(ctx->clk);
187 
188 	return 0;
189 }
190 
regmap_mmio_free_context(void * context)191 static void regmap_mmio_free_context(void *context)
192 {
193 	struct regmap_mmio_context *ctx = context;
194 
195 	if (!IS_ERR(ctx->clk)) {
196 		clk_unprepare(ctx->clk);
197 		if (!ctx->attached_clk)
198 			clk_put(ctx->clk);
199 	}
200 	kfree(context);
201 }
202 
203 static const struct regmap_bus regmap_mmio = {
204 	.fast_io = true,
205 	.reg_write = regmap_mmio_write,
206 	.reg_read = regmap_mmio_read,
207 	.free_context = regmap_mmio_free_context,
208 	.val_format_endian_default = REGMAP_ENDIAN_LITTLE,
209 };
210 
regmap_mmio_gen_context(struct device * dev,const char * clk_id,void __iomem * regs,const struct regmap_config * config)211 static struct regmap_mmio_context *regmap_mmio_gen_context(struct device *dev,
212 					const char *clk_id,
213 					void __iomem *regs,
214 					const struct regmap_config *config)
215 {
216 	struct regmap_mmio_context *ctx;
217 	int min_stride;
218 	int ret;
219 
220 	ret = regmap_mmio_regbits_check(config->reg_bits);
221 	if (ret)
222 		return ERR_PTR(ret);
223 
224 	if (config->pad_bits)
225 		return ERR_PTR(-EINVAL);
226 
227 	min_stride = regmap_mmio_get_min_stride(config->val_bits);
228 	if (min_stride < 0)
229 		return ERR_PTR(min_stride);
230 
231 	if (config->reg_stride < min_stride)
232 		return ERR_PTR(-EINVAL);
233 
234 	ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
235 	if (!ctx)
236 		return ERR_PTR(-ENOMEM);
237 
238 	ctx->regs = regs;
239 	ctx->val_bytes = config->val_bits / 8;
240 	ctx->clk = ERR_PTR(-ENODEV);
241 
242 	switch (regmap_get_val_endian(dev, &regmap_mmio, config)) {
243 	case REGMAP_ENDIAN_DEFAULT:
244 	case REGMAP_ENDIAN_LITTLE:
245 #ifdef __LITTLE_ENDIAN
246 	case REGMAP_ENDIAN_NATIVE:
247 #endif
248 		switch (config->val_bits) {
249 		case 8:
250 			ctx->reg_read = regmap_mmio_read8;
251 			ctx->reg_write = regmap_mmio_write8;
252 			break;
253 		case 16:
254 			ctx->reg_read = regmap_mmio_read16le;
255 			ctx->reg_write = regmap_mmio_write16le;
256 			break;
257 		case 32:
258 			ctx->reg_read = regmap_mmio_read32le;
259 			ctx->reg_write = regmap_mmio_write32le;
260 			break;
261 #ifdef CONFIG_64BIT
262 		case 64:
263 			ctx->reg_read = regmap_mmio_read64le;
264 			ctx->reg_write = regmap_mmio_write64le;
265 			break;
266 #endif
267 		default:
268 			ret = -EINVAL;
269 			goto err_free;
270 		}
271 		break;
272 	case REGMAP_ENDIAN_BIG:
273 #ifdef __BIG_ENDIAN
274 	case REGMAP_ENDIAN_NATIVE:
275 #endif
276 		switch (config->val_bits) {
277 		case 8:
278 			ctx->reg_read = regmap_mmio_read8;
279 			ctx->reg_write = regmap_mmio_write8;
280 			break;
281 		case 16:
282 			ctx->reg_read = regmap_mmio_read16be;
283 			ctx->reg_write = regmap_mmio_write16be;
284 			break;
285 		case 32:
286 			ctx->reg_read = regmap_mmio_read32be;
287 			ctx->reg_write = regmap_mmio_write32be;
288 			break;
289 		default:
290 			ret = -EINVAL;
291 			goto err_free;
292 		}
293 		break;
294 	default:
295 		ret = -EINVAL;
296 		goto err_free;
297 	}
298 
299 	if (clk_id == NULL)
300 		return ctx;
301 
302 	ctx->clk = clk_get(dev, clk_id);
303 	if (IS_ERR(ctx->clk)) {
304 		ret = PTR_ERR(ctx->clk);
305 		goto err_free;
306 	}
307 
308 	ret = clk_prepare(ctx->clk);
309 	if (ret < 0) {
310 		clk_put(ctx->clk);
311 		goto err_free;
312 	}
313 
314 	return ctx;
315 
316 err_free:
317 	kfree(ctx);
318 
319 	return ERR_PTR(ret);
320 }
321 
__regmap_init_mmio_clk(struct device * dev,const char * clk_id,void __iomem * regs,const struct regmap_config * config,struct lock_class_key * lock_key,const char * lock_name)322 struct regmap *__regmap_init_mmio_clk(struct device *dev, const char *clk_id,
323 				      void __iomem *regs,
324 				      const struct regmap_config *config,
325 				      struct lock_class_key *lock_key,
326 				      const char *lock_name)
327 {
328 	struct regmap_mmio_context *ctx;
329 
330 	ctx = regmap_mmio_gen_context(dev, clk_id, regs, config);
331 	if (IS_ERR(ctx))
332 		return ERR_CAST(ctx);
333 
334 	return __regmap_init(dev, &regmap_mmio, ctx, config,
335 			     lock_key, lock_name);
336 }
337 EXPORT_SYMBOL_GPL(__regmap_init_mmio_clk);
338 
__devm_regmap_init_mmio_clk(struct device * dev,const char * clk_id,void __iomem * regs,const struct regmap_config * config,struct lock_class_key * lock_key,const char * lock_name)339 struct regmap *__devm_regmap_init_mmio_clk(struct device *dev,
340 					   const char *clk_id,
341 					   void __iomem *regs,
342 					   const struct regmap_config *config,
343 					   struct lock_class_key *lock_key,
344 					   const char *lock_name)
345 {
346 	struct regmap_mmio_context *ctx;
347 
348 	ctx = regmap_mmio_gen_context(dev, clk_id, regs, config);
349 	if (IS_ERR(ctx))
350 		return ERR_CAST(ctx);
351 
352 	return __devm_regmap_init(dev, &regmap_mmio, ctx, config,
353 				  lock_key, lock_name);
354 }
355 EXPORT_SYMBOL_GPL(__devm_regmap_init_mmio_clk);
356 
regmap_mmio_attach_clk(struct regmap * map,struct clk * clk)357 int regmap_mmio_attach_clk(struct regmap *map, struct clk *clk)
358 {
359 	struct regmap_mmio_context *ctx = map->bus_context;
360 
361 	ctx->clk = clk;
362 	ctx->attached_clk = true;
363 
364 	return clk_prepare(ctx->clk);
365 }
366 EXPORT_SYMBOL_GPL(regmap_mmio_attach_clk);
367 
regmap_mmio_detach_clk(struct regmap * map)368 void regmap_mmio_detach_clk(struct regmap *map)
369 {
370 	struct regmap_mmio_context *ctx = map->bus_context;
371 
372 	clk_unprepare(ctx->clk);
373 
374 	ctx->attached_clk = false;
375 	ctx->clk = NULL;
376 }
377 EXPORT_SYMBOL_GPL(regmap_mmio_detach_clk);
378 
379 MODULE_LICENSE("GPL v2");
380