xref: /linux/drivers/base/regmap/regmap-mmio.c (revision f9bff0e31881d03badf191d3b0005839391f5f2b)
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 #include <linux/swab.h>
14 
15 #include "internal.h"
16 
17 struct regmap_mmio_context {
18 	void __iomem *regs;
19 	unsigned int val_bytes;
20 	bool big_endian;
21 
22 	bool attached_clk;
23 	struct clk *clk;
24 
25 	void (*reg_write)(struct regmap_mmio_context *ctx,
26 			  unsigned int reg, unsigned int val);
27 	unsigned int (*reg_read)(struct regmap_mmio_context *ctx,
28 			         unsigned int reg);
29 };
30 
31 static int regmap_mmio_regbits_check(size_t reg_bits)
32 {
33 	switch (reg_bits) {
34 	case 8:
35 	case 16:
36 	case 32:
37 		return 0;
38 	default:
39 		return -EINVAL;
40 	}
41 }
42 
43 static int regmap_mmio_get_min_stride(size_t val_bits)
44 {
45 	int min_stride;
46 
47 	switch (val_bits) {
48 	case 8:
49 		/* The core treats 0 as 1 */
50 		min_stride = 0;
51 		break;
52 	case 16:
53 		min_stride = 2;
54 		break;
55 	case 32:
56 		min_stride = 4;
57 		break;
58 	default:
59 		return -EINVAL;
60 	}
61 
62 	return min_stride;
63 }
64 
65 static void regmap_mmio_write8(struct regmap_mmio_context *ctx,
66 				unsigned int reg,
67 				unsigned int val)
68 {
69 	writeb(val, ctx->regs + reg);
70 }
71 
72 static void regmap_mmio_write8_relaxed(struct regmap_mmio_context *ctx,
73 				unsigned int reg,
74 				unsigned int val)
75 {
76 	writeb_relaxed(val, ctx->regs + reg);
77 }
78 
79 static void regmap_mmio_iowrite8(struct regmap_mmio_context *ctx,
80 				 unsigned int reg, unsigned int val)
81 {
82 	iowrite8(val, ctx->regs + reg);
83 }
84 
85 static void regmap_mmio_write16le(struct regmap_mmio_context *ctx,
86 				  unsigned int reg,
87 				  unsigned int val)
88 {
89 	writew(val, ctx->regs + reg);
90 }
91 
92 static void regmap_mmio_write16le_relaxed(struct regmap_mmio_context *ctx,
93 				  unsigned int reg,
94 				  unsigned int val)
95 {
96 	writew_relaxed(val, ctx->regs + reg);
97 }
98 
99 static void regmap_mmio_iowrite16le(struct regmap_mmio_context *ctx,
100 				    unsigned int reg, unsigned int val)
101 {
102 	iowrite16(val, ctx->regs + reg);
103 }
104 
105 static void regmap_mmio_write16be(struct regmap_mmio_context *ctx,
106 				  unsigned int reg,
107 				  unsigned int val)
108 {
109 	writew(swab16(val), ctx->regs + reg);
110 }
111 
112 static void regmap_mmio_iowrite16be(struct regmap_mmio_context *ctx,
113 				    unsigned int reg, unsigned int val)
114 {
115 	iowrite16be(val, ctx->regs + reg);
116 }
117 
118 static void regmap_mmio_write32le(struct regmap_mmio_context *ctx,
119 				  unsigned int reg,
120 				  unsigned int val)
121 {
122 	writel(val, ctx->regs + reg);
123 }
124 
125 static void regmap_mmio_write32le_relaxed(struct regmap_mmio_context *ctx,
126 				  unsigned int reg,
127 				  unsigned int val)
128 {
129 	writel_relaxed(val, ctx->regs + reg);
130 }
131 
132 static void regmap_mmio_iowrite32le(struct regmap_mmio_context *ctx,
133 				    unsigned int reg, unsigned int val)
134 {
135 	iowrite32(val, ctx->regs + reg);
136 }
137 
138 static void regmap_mmio_write32be(struct regmap_mmio_context *ctx,
139 				  unsigned int reg,
140 				  unsigned int val)
141 {
142 	writel(swab32(val), ctx->regs + reg);
143 }
144 
145 static void regmap_mmio_iowrite32be(struct regmap_mmio_context *ctx,
146 				    unsigned int reg, unsigned int val)
147 {
148 	iowrite32be(val, ctx->regs + reg);
149 }
150 
151 static int regmap_mmio_write(void *context, unsigned int reg, unsigned int val)
152 {
153 	struct regmap_mmio_context *ctx = context;
154 	int ret;
155 
156 	if (!IS_ERR(ctx->clk)) {
157 		ret = clk_enable(ctx->clk);
158 		if (ret < 0)
159 			return ret;
160 	}
161 
162 	ctx->reg_write(ctx, reg, val);
163 
164 	if (!IS_ERR(ctx->clk))
165 		clk_disable(ctx->clk);
166 
167 	return 0;
168 }
169 
170 static int regmap_mmio_noinc_write(void *context, unsigned int reg,
171 				   const void *val, size_t val_count)
172 {
173 	struct regmap_mmio_context *ctx = context;
174 	int ret = 0;
175 	int i;
176 
177 	if (!IS_ERR(ctx->clk)) {
178 		ret = clk_enable(ctx->clk);
179 		if (ret < 0)
180 			return ret;
181 	}
182 
183 	/*
184 	 * There are no native, assembly-optimized write single register
185 	 * operations for big endian, so fall back to emulation if this
186 	 * is needed. (Single bytes are fine, they are not affected by
187 	 * endianness.)
188 	 */
189 	if (ctx->big_endian && (ctx->val_bytes > 1)) {
190 		switch (ctx->val_bytes) {
191 		case 2:
192 		{
193 			const u16 *valp = (const u16 *)val;
194 			for (i = 0; i < val_count; i++)
195 				writew(swab16(valp[i]), ctx->regs + reg);
196 			goto out_clk;
197 		}
198 		case 4:
199 		{
200 			const u32 *valp = (const u32 *)val;
201 			for (i = 0; i < val_count; i++)
202 				writel(swab32(valp[i]), ctx->regs + reg);
203 			goto out_clk;
204 		}
205 #ifdef CONFIG_64BIT
206 		case 8:
207 		{
208 			const u64 *valp = (const u64 *)val;
209 			for (i = 0; i < val_count; i++)
210 				writeq(swab64(valp[i]), ctx->regs + reg);
211 			goto out_clk;
212 		}
213 #endif
214 		default:
215 			ret = -EINVAL;
216 			goto out_clk;
217 		}
218 	}
219 
220 	switch (ctx->val_bytes) {
221 	case 1:
222 		writesb(ctx->regs + reg, (const u8 *)val, val_count);
223 		break;
224 	case 2:
225 		writesw(ctx->regs + reg, (const u16 *)val, val_count);
226 		break;
227 	case 4:
228 		writesl(ctx->regs + reg, (const u32 *)val, val_count);
229 		break;
230 #ifdef CONFIG_64BIT
231 	case 8:
232 		writesq(ctx->regs + reg, (const u64 *)val, val_count);
233 		break;
234 #endif
235 	default:
236 		ret = -EINVAL;
237 		break;
238 	}
239 
240 out_clk:
241 	if (!IS_ERR(ctx->clk))
242 		clk_disable(ctx->clk);
243 
244 	return ret;
245 }
246 
247 static unsigned int regmap_mmio_read8(struct regmap_mmio_context *ctx,
248 				      unsigned int reg)
249 {
250 	return readb(ctx->regs + reg);
251 }
252 
253 static unsigned int regmap_mmio_read8_relaxed(struct regmap_mmio_context *ctx,
254 				      unsigned int reg)
255 {
256 	return readb_relaxed(ctx->regs + reg);
257 }
258 
259 static unsigned int regmap_mmio_ioread8(struct regmap_mmio_context *ctx,
260 					unsigned int reg)
261 {
262 	return ioread8(ctx->regs + reg);
263 }
264 
265 static unsigned int regmap_mmio_read16le(struct regmap_mmio_context *ctx,
266 				         unsigned int reg)
267 {
268 	return readw(ctx->regs + reg);
269 }
270 
271 static unsigned int regmap_mmio_read16le_relaxed(struct regmap_mmio_context *ctx,
272 						 unsigned int reg)
273 {
274 	return readw_relaxed(ctx->regs + reg);
275 }
276 
277 static unsigned int regmap_mmio_ioread16le(struct regmap_mmio_context *ctx,
278 					   unsigned int reg)
279 {
280 	return ioread16(ctx->regs + reg);
281 }
282 
283 static unsigned int regmap_mmio_read16be(struct regmap_mmio_context *ctx,
284 				         unsigned int reg)
285 {
286 	return swab16(readw(ctx->regs + reg));
287 }
288 
289 static unsigned int regmap_mmio_ioread16be(struct regmap_mmio_context *ctx,
290 					   unsigned int reg)
291 {
292 	return ioread16be(ctx->regs + reg);
293 }
294 
295 static unsigned int regmap_mmio_read32le(struct regmap_mmio_context *ctx,
296 				         unsigned int reg)
297 {
298 	return readl(ctx->regs + reg);
299 }
300 
301 static unsigned int regmap_mmio_read32le_relaxed(struct regmap_mmio_context *ctx,
302 						 unsigned int reg)
303 {
304 	return readl_relaxed(ctx->regs + reg);
305 }
306 
307 static unsigned int regmap_mmio_ioread32le(struct regmap_mmio_context *ctx,
308 					   unsigned int reg)
309 {
310 	return ioread32(ctx->regs + reg);
311 }
312 
313 static unsigned int regmap_mmio_read32be(struct regmap_mmio_context *ctx,
314 				         unsigned int reg)
315 {
316 	return swab32(readl(ctx->regs + reg));
317 }
318 
319 static unsigned int regmap_mmio_ioread32be(struct regmap_mmio_context *ctx,
320 					   unsigned int reg)
321 {
322 	return ioread32be(ctx->regs + reg);
323 }
324 
325 static int regmap_mmio_read(void *context, unsigned int reg, unsigned int *val)
326 {
327 	struct regmap_mmio_context *ctx = context;
328 	int ret;
329 
330 	if (!IS_ERR(ctx->clk)) {
331 		ret = clk_enable(ctx->clk);
332 		if (ret < 0)
333 			return ret;
334 	}
335 
336 	*val = ctx->reg_read(ctx, reg);
337 
338 	if (!IS_ERR(ctx->clk))
339 		clk_disable(ctx->clk);
340 
341 	return 0;
342 }
343 
344 static int regmap_mmio_noinc_read(void *context, unsigned int reg,
345 				  void *val, size_t val_count)
346 {
347 	struct regmap_mmio_context *ctx = context;
348 	int ret = 0;
349 
350 	if (!IS_ERR(ctx->clk)) {
351 		ret = clk_enable(ctx->clk);
352 		if (ret < 0)
353 			return ret;
354 	}
355 
356 	switch (ctx->val_bytes) {
357 	case 1:
358 		readsb(ctx->regs + reg, (u8 *)val, val_count);
359 		break;
360 	case 2:
361 		readsw(ctx->regs + reg, (u16 *)val, val_count);
362 		break;
363 	case 4:
364 		readsl(ctx->regs + reg, (u32 *)val, val_count);
365 		break;
366 #ifdef CONFIG_64BIT
367 	case 8:
368 		readsq(ctx->regs + reg, (u64 *)val, val_count);
369 		break;
370 #endif
371 	default:
372 		ret = -EINVAL;
373 		goto out_clk;
374 	}
375 
376 	/*
377 	 * There are no native, assembly-optimized write single register
378 	 * operations for big endian, so fall back to emulation if this
379 	 * is needed. (Single bytes are fine, they are not affected by
380 	 * endianness.)
381 	 */
382 	if (ctx->big_endian && (ctx->val_bytes > 1)) {
383 		switch (ctx->val_bytes) {
384 		case 2:
385 			swab16_array(val, val_count);
386 			break;
387 		case 4:
388 			swab32_array(val, val_count);
389 			break;
390 #ifdef CONFIG_64BIT
391 		case 8:
392 			swab64_array(val, val_count);
393 			break;
394 #endif
395 		default:
396 			ret = -EINVAL;
397 			break;
398 		}
399 	}
400 
401 out_clk:
402 	if (!IS_ERR(ctx->clk))
403 		clk_disable(ctx->clk);
404 
405 	return ret;
406 }
407 
408 
409 static void regmap_mmio_free_context(void *context)
410 {
411 	struct regmap_mmio_context *ctx = context;
412 
413 	if (!IS_ERR(ctx->clk)) {
414 		clk_unprepare(ctx->clk);
415 		if (!ctx->attached_clk)
416 			clk_put(ctx->clk);
417 	}
418 	kfree(context);
419 }
420 
421 static const struct regmap_bus regmap_mmio = {
422 	.fast_io = true,
423 	.reg_write = regmap_mmio_write,
424 	.reg_read = regmap_mmio_read,
425 	.reg_noinc_write = regmap_mmio_noinc_write,
426 	.reg_noinc_read = regmap_mmio_noinc_read,
427 	.free_context = regmap_mmio_free_context,
428 	.val_format_endian_default = REGMAP_ENDIAN_LITTLE,
429 };
430 
431 static struct regmap_mmio_context *regmap_mmio_gen_context(struct device *dev,
432 					const char *clk_id,
433 					void __iomem *regs,
434 					const struct regmap_config *config)
435 {
436 	struct regmap_mmio_context *ctx;
437 	int min_stride;
438 	int ret;
439 
440 	ret = regmap_mmio_regbits_check(config->reg_bits);
441 	if (ret)
442 		return ERR_PTR(ret);
443 
444 	if (config->pad_bits)
445 		return ERR_PTR(-EINVAL);
446 
447 	min_stride = regmap_mmio_get_min_stride(config->val_bits);
448 	if (min_stride < 0)
449 		return ERR_PTR(min_stride);
450 
451 	if (config->reg_stride && config->reg_stride < min_stride)
452 		return ERR_PTR(-EINVAL);
453 
454 	if (config->use_relaxed_mmio && config->io_port)
455 		return ERR_PTR(-EINVAL);
456 
457 	ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
458 	if (!ctx)
459 		return ERR_PTR(-ENOMEM);
460 
461 	ctx->regs = regs;
462 	ctx->val_bytes = config->val_bits / 8;
463 	ctx->clk = ERR_PTR(-ENODEV);
464 
465 	switch (regmap_get_val_endian(dev, &regmap_mmio, config)) {
466 	case REGMAP_ENDIAN_DEFAULT:
467 	case REGMAP_ENDIAN_LITTLE:
468 #ifdef __LITTLE_ENDIAN
469 	case REGMAP_ENDIAN_NATIVE:
470 #endif
471 		switch (config->val_bits) {
472 		case 8:
473 			if (config->io_port) {
474 				ctx->reg_read = regmap_mmio_ioread8;
475 				ctx->reg_write = regmap_mmio_iowrite8;
476 			} else if (config->use_relaxed_mmio) {
477 				ctx->reg_read = regmap_mmio_read8_relaxed;
478 				ctx->reg_write = regmap_mmio_write8_relaxed;
479 			} else {
480 				ctx->reg_read = regmap_mmio_read8;
481 				ctx->reg_write = regmap_mmio_write8;
482 			}
483 			break;
484 		case 16:
485 			if (config->io_port) {
486 				ctx->reg_read = regmap_mmio_ioread16le;
487 				ctx->reg_write = regmap_mmio_iowrite16le;
488 			} else if (config->use_relaxed_mmio) {
489 				ctx->reg_read = regmap_mmio_read16le_relaxed;
490 				ctx->reg_write = regmap_mmio_write16le_relaxed;
491 			} else {
492 				ctx->reg_read = regmap_mmio_read16le;
493 				ctx->reg_write = regmap_mmio_write16le;
494 			}
495 			break;
496 		case 32:
497 			if (config->io_port) {
498 				ctx->reg_read = regmap_mmio_ioread32le;
499 				ctx->reg_write = regmap_mmio_iowrite32le;
500 			} else if (config->use_relaxed_mmio) {
501 				ctx->reg_read = regmap_mmio_read32le_relaxed;
502 				ctx->reg_write = regmap_mmio_write32le_relaxed;
503 			} else {
504 				ctx->reg_read = regmap_mmio_read32le;
505 				ctx->reg_write = regmap_mmio_write32le;
506 			}
507 			break;
508 		default:
509 			ret = -EINVAL;
510 			goto err_free;
511 		}
512 		break;
513 	case REGMAP_ENDIAN_BIG:
514 #ifdef __BIG_ENDIAN
515 	case REGMAP_ENDIAN_NATIVE:
516 #endif
517 		ctx->big_endian = true;
518 		switch (config->val_bits) {
519 		case 8:
520 			if (config->io_port) {
521 				ctx->reg_read = regmap_mmio_ioread8;
522 				ctx->reg_write = regmap_mmio_iowrite8;
523 			} else {
524 				ctx->reg_read = regmap_mmio_read8;
525 				ctx->reg_write = regmap_mmio_write8;
526 			}
527 			break;
528 		case 16:
529 			if (config->io_port) {
530 				ctx->reg_read = regmap_mmio_ioread16be;
531 				ctx->reg_write = regmap_mmio_iowrite16be;
532 			} else {
533 				ctx->reg_read = regmap_mmio_read16be;
534 				ctx->reg_write = regmap_mmio_write16be;
535 			}
536 			break;
537 		case 32:
538 			if (config->io_port) {
539 				ctx->reg_read = regmap_mmio_ioread32be;
540 				ctx->reg_write = regmap_mmio_iowrite32be;
541 			} else {
542 				ctx->reg_read = regmap_mmio_read32be;
543 				ctx->reg_write = regmap_mmio_write32be;
544 			}
545 			break;
546 		default:
547 			ret = -EINVAL;
548 			goto err_free;
549 		}
550 		break;
551 	default:
552 		ret = -EINVAL;
553 		goto err_free;
554 	}
555 
556 	if (clk_id == NULL)
557 		return ctx;
558 
559 	ctx->clk = clk_get(dev, clk_id);
560 	if (IS_ERR(ctx->clk)) {
561 		ret = PTR_ERR(ctx->clk);
562 		goto err_free;
563 	}
564 
565 	ret = clk_prepare(ctx->clk);
566 	if (ret < 0) {
567 		clk_put(ctx->clk);
568 		goto err_free;
569 	}
570 
571 	return ctx;
572 
573 err_free:
574 	kfree(ctx);
575 
576 	return ERR_PTR(ret);
577 }
578 
579 struct regmap *__regmap_init_mmio_clk(struct device *dev, const char *clk_id,
580 				      void __iomem *regs,
581 				      const struct regmap_config *config,
582 				      struct lock_class_key *lock_key,
583 				      const char *lock_name)
584 {
585 	struct regmap_mmio_context *ctx;
586 
587 	ctx = regmap_mmio_gen_context(dev, clk_id, regs, config);
588 	if (IS_ERR(ctx))
589 		return ERR_CAST(ctx);
590 
591 	return __regmap_init(dev, &regmap_mmio, ctx, config,
592 			     lock_key, lock_name);
593 }
594 EXPORT_SYMBOL_GPL(__regmap_init_mmio_clk);
595 
596 struct regmap *__devm_regmap_init_mmio_clk(struct device *dev,
597 					   const char *clk_id,
598 					   void __iomem *regs,
599 					   const struct regmap_config *config,
600 					   struct lock_class_key *lock_key,
601 					   const char *lock_name)
602 {
603 	struct regmap_mmio_context *ctx;
604 
605 	ctx = regmap_mmio_gen_context(dev, clk_id, regs, config);
606 	if (IS_ERR(ctx))
607 		return ERR_CAST(ctx);
608 
609 	return __devm_regmap_init(dev, &regmap_mmio, ctx, config,
610 				  lock_key, lock_name);
611 }
612 EXPORT_SYMBOL_GPL(__devm_regmap_init_mmio_clk);
613 
614 int regmap_mmio_attach_clk(struct regmap *map, struct clk *clk)
615 {
616 	struct regmap_mmio_context *ctx = map->bus_context;
617 
618 	ctx->clk = clk;
619 	ctx->attached_clk = true;
620 
621 	return clk_prepare(ctx->clk);
622 }
623 EXPORT_SYMBOL_GPL(regmap_mmio_attach_clk);
624 
625 void regmap_mmio_detach_clk(struct regmap *map)
626 {
627 	struct regmap_mmio_context *ctx = map->bus_context;
628 
629 	clk_unprepare(ctx->clk);
630 
631 	ctx->attached_clk = false;
632 	ctx->clk = NULL;
633 }
634 EXPORT_SYMBOL_GPL(regmap_mmio_detach_clk);
635 
636 MODULE_LICENSE("GPL v2");
637