Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/drivers/base/regmap/regmap-mmio.c
26427 views
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
default:
206
ret = -EINVAL;
207
goto out_clk;
208
}
209
}
210
211
switch (ctx->val_bytes) {
212
case 1:
213
writesb(ctx->regs + reg, (const u8 *)val, val_count);
214
break;
215
case 2:
216
writesw(ctx->regs + reg, (const u16 *)val, val_count);
217
break;
218
case 4:
219
writesl(ctx->regs + reg, (const u32 *)val, val_count);
220
break;
221
default:
222
ret = -EINVAL;
223
break;
224
}
225
226
out_clk:
227
if (!IS_ERR(ctx->clk))
228
clk_disable(ctx->clk);
229
230
return ret;
231
}
232
233
static unsigned int regmap_mmio_read8(struct regmap_mmio_context *ctx,
234
unsigned int reg)
235
{
236
return readb(ctx->regs + reg);
237
}
238
239
static unsigned int regmap_mmio_read8_relaxed(struct regmap_mmio_context *ctx,
240
unsigned int reg)
241
{
242
return readb_relaxed(ctx->regs + reg);
243
}
244
245
static unsigned int regmap_mmio_ioread8(struct regmap_mmio_context *ctx,
246
unsigned int reg)
247
{
248
return ioread8(ctx->regs + reg);
249
}
250
251
static unsigned int regmap_mmio_read16le(struct regmap_mmio_context *ctx,
252
unsigned int reg)
253
{
254
return readw(ctx->regs + reg);
255
}
256
257
static unsigned int regmap_mmio_read16le_relaxed(struct regmap_mmio_context *ctx,
258
unsigned int reg)
259
{
260
return readw_relaxed(ctx->regs + reg);
261
}
262
263
static unsigned int regmap_mmio_ioread16le(struct regmap_mmio_context *ctx,
264
unsigned int reg)
265
{
266
return ioread16(ctx->regs + reg);
267
}
268
269
static unsigned int regmap_mmio_read16be(struct regmap_mmio_context *ctx,
270
unsigned int reg)
271
{
272
return swab16(readw(ctx->regs + reg));
273
}
274
275
static unsigned int regmap_mmio_ioread16be(struct regmap_mmio_context *ctx,
276
unsigned int reg)
277
{
278
return ioread16be(ctx->regs + reg);
279
}
280
281
static unsigned int regmap_mmio_read32le(struct regmap_mmio_context *ctx,
282
unsigned int reg)
283
{
284
return readl(ctx->regs + reg);
285
}
286
287
static unsigned int regmap_mmio_read32le_relaxed(struct regmap_mmio_context *ctx,
288
unsigned int reg)
289
{
290
return readl_relaxed(ctx->regs + reg);
291
}
292
293
static unsigned int regmap_mmio_ioread32le(struct regmap_mmio_context *ctx,
294
unsigned int reg)
295
{
296
return ioread32(ctx->regs + reg);
297
}
298
299
static unsigned int regmap_mmio_read32be(struct regmap_mmio_context *ctx,
300
unsigned int reg)
301
{
302
return swab32(readl(ctx->regs + reg));
303
}
304
305
static unsigned int regmap_mmio_ioread32be(struct regmap_mmio_context *ctx,
306
unsigned int reg)
307
{
308
return ioread32be(ctx->regs + reg);
309
}
310
311
static int regmap_mmio_read(void *context, unsigned int reg, unsigned int *val)
312
{
313
struct regmap_mmio_context *ctx = context;
314
int ret;
315
316
if (!IS_ERR(ctx->clk)) {
317
ret = clk_enable(ctx->clk);
318
if (ret < 0)
319
return ret;
320
}
321
322
*val = ctx->reg_read(ctx, reg);
323
324
if (!IS_ERR(ctx->clk))
325
clk_disable(ctx->clk);
326
327
return 0;
328
}
329
330
static int regmap_mmio_noinc_read(void *context, unsigned int reg,
331
void *val, size_t val_count)
332
{
333
struct regmap_mmio_context *ctx = context;
334
int ret = 0;
335
336
if (!IS_ERR(ctx->clk)) {
337
ret = clk_enable(ctx->clk);
338
if (ret < 0)
339
return ret;
340
}
341
342
switch (ctx->val_bytes) {
343
case 1:
344
readsb(ctx->regs + reg, (u8 *)val, val_count);
345
break;
346
case 2:
347
readsw(ctx->regs + reg, (u16 *)val, val_count);
348
break;
349
case 4:
350
readsl(ctx->regs + reg, (u32 *)val, val_count);
351
break;
352
default:
353
ret = -EINVAL;
354
goto out_clk;
355
}
356
357
/*
358
* There are no native, assembly-optimized write single register
359
* operations for big endian, so fall back to emulation if this
360
* is needed. (Single bytes are fine, they are not affected by
361
* endianness.)
362
*/
363
if (ctx->big_endian && (ctx->val_bytes > 1)) {
364
switch (ctx->val_bytes) {
365
case 2:
366
swab16_array(val, val_count);
367
break;
368
case 4:
369
swab32_array(val, val_count);
370
break;
371
default:
372
ret = -EINVAL;
373
break;
374
}
375
}
376
377
out_clk:
378
if (!IS_ERR(ctx->clk))
379
clk_disable(ctx->clk);
380
381
return ret;
382
}
383
384
385
static void regmap_mmio_free_context(void *context)
386
{
387
struct regmap_mmio_context *ctx = context;
388
389
if (!IS_ERR(ctx->clk)) {
390
clk_unprepare(ctx->clk);
391
if (!ctx->attached_clk)
392
clk_put(ctx->clk);
393
}
394
kfree(context);
395
}
396
397
static const struct regmap_bus regmap_mmio = {
398
.fast_io = true,
399
.reg_write = regmap_mmio_write,
400
.reg_read = regmap_mmio_read,
401
.reg_noinc_write = regmap_mmio_noinc_write,
402
.reg_noinc_read = regmap_mmio_noinc_read,
403
.free_context = regmap_mmio_free_context,
404
.val_format_endian_default = REGMAP_ENDIAN_LITTLE,
405
};
406
407
static struct regmap_mmio_context *regmap_mmio_gen_context(struct device *dev,
408
const char *clk_id,
409
void __iomem *regs,
410
const struct regmap_config *config)
411
{
412
struct regmap_mmio_context *ctx;
413
int min_stride;
414
int ret;
415
416
ret = regmap_mmio_regbits_check(config->reg_bits);
417
if (ret)
418
return ERR_PTR(ret);
419
420
if (config->pad_bits)
421
return ERR_PTR(-EINVAL);
422
423
min_stride = regmap_mmio_get_min_stride(config->val_bits);
424
if (min_stride < 0)
425
return ERR_PTR(min_stride);
426
427
if (config->reg_stride && config->reg_stride < min_stride)
428
return ERR_PTR(-EINVAL);
429
430
if (config->use_relaxed_mmio && config->io_port)
431
return ERR_PTR(-EINVAL);
432
433
ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
434
if (!ctx)
435
return ERR_PTR(-ENOMEM);
436
437
ctx->regs = regs;
438
ctx->val_bytes = config->val_bits / 8;
439
ctx->clk = ERR_PTR(-ENODEV);
440
441
switch (regmap_get_val_endian(dev, &regmap_mmio, config)) {
442
case REGMAP_ENDIAN_DEFAULT:
443
case REGMAP_ENDIAN_LITTLE:
444
#ifdef __LITTLE_ENDIAN
445
case REGMAP_ENDIAN_NATIVE:
446
#endif
447
switch (config->val_bits) {
448
case 8:
449
if (config->io_port) {
450
ctx->reg_read = regmap_mmio_ioread8;
451
ctx->reg_write = regmap_mmio_iowrite8;
452
} else if (config->use_relaxed_mmio) {
453
ctx->reg_read = regmap_mmio_read8_relaxed;
454
ctx->reg_write = regmap_mmio_write8_relaxed;
455
} else {
456
ctx->reg_read = regmap_mmio_read8;
457
ctx->reg_write = regmap_mmio_write8;
458
}
459
break;
460
case 16:
461
if (config->io_port) {
462
ctx->reg_read = regmap_mmio_ioread16le;
463
ctx->reg_write = regmap_mmio_iowrite16le;
464
} else if (config->use_relaxed_mmio) {
465
ctx->reg_read = regmap_mmio_read16le_relaxed;
466
ctx->reg_write = regmap_mmio_write16le_relaxed;
467
} else {
468
ctx->reg_read = regmap_mmio_read16le;
469
ctx->reg_write = regmap_mmio_write16le;
470
}
471
break;
472
case 32:
473
if (config->io_port) {
474
ctx->reg_read = regmap_mmio_ioread32le;
475
ctx->reg_write = regmap_mmio_iowrite32le;
476
} else if (config->use_relaxed_mmio) {
477
ctx->reg_read = regmap_mmio_read32le_relaxed;
478
ctx->reg_write = regmap_mmio_write32le_relaxed;
479
} else {
480
ctx->reg_read = regmap_mmio_read32le;
481
ctx->reg_write = regmap_mmio_write32le;
482
}
483
break;
484
default:
485
ret = -EINVAL;
486
goto err_free;
487
}
488
break;
489
case REGMAP_ENDIAN_BIG:
490
#ifdef __BIG_ENDIAN
491
case REGMAP_ENDIAN_NATIVE:
492
#endif
493
ctx->big_endian = true;
494
switch (config->val_bits) {
495
case 8:
496
if (config->io_port) {
497
ctx->reg_read = regmap_mmio_ioread8;
498
ctx->reg_write = regmap_mmio_iowrite8;
499
} else {
500
ctx->reg_read = regmap_mmio_read8;
501
ctx->reg_write = regmap_mmio_write8;
502
}
503
break;
504
case 16:
505
if (config->io_port) {
506
ctx->reg_read = regmap_mmio_ioread16be;
507
ctx->reg_write = regmap_mmio_iowrite16be;
508
} else {
509
ctx->reg_read = regmap_mmio_read16be;
510
ctx->reg_write = regmap_mmio_write16be;
511
}
512
break;
513
case 32:
514
if (config->io_port) {
515
ctx->reg_read = regmap_mmio_ioread32be;
516
ctx->reg_write = regmap_mmio_iowrite32be;
517
} else {
518
ctx->reg_read = regmap_mmio_read32be;
519
ctx->reg_write = regmap_mmio_write32be;
520
}
521
break;
522
default:
523
ret = -EINVAL;
524
goto err_free;
525
}
526
break;
527
default:
528
ret = -EINVAL;
529
goto err_free;
530
}
531
532
if (clk_id == NULL)
533
return ctx;
534
535
ctx->clk = clk_get(dev, clk_id);
536
if (IS_ERR(ctx->clk)) {
537
ret = PTR_ERR(ctx->clk);
538
goto err_free;
539
}
540
541
ret = clk_prepare(ctx->clk);
542
if (ret < 0) {
543
clk_put(ctx->clk);
544
goto err_free;
545
}
546
547
return ctx;
548
549
err_free:
550
kfree(ctx);
551
552
return ERR_PTR(ret);
553
}
554
555
struct regmap *__regmap_init_mmio_clk(struct device *dev, const char *clk_id,
556
void __iomem *regs,
557
const struct regmap_config *config,
558
struct lock_class_key *lock_key,
559
const char *lock_name)
560
{
561
struct regmap_mmio_context *ctx;
562
563
ctx = regmap_mmio_gen_context(dev, clk_id, regs, config);
564
if (IS_ERR(ctx))
565
return ERR_CAST(ctx);
566
567
return __regmap_init(dev, &regmap_mmio, ctx, config,
568
lock_key, lock_name);
569
}
570
EXPORT_SYMBOL_GPL(__regmap_init_mmio_clk);
571
572
struct regmap *__devm_regmap_init_mmio_clk(struct device *dev,
573
const char *clk_id,
574
void __iomem *regs,
575
const struct regmap_config *config,
576
struct lock_class_key *lock_key,
577
const char *lock_name)
578
{
579
struct regmap_mmio_context *ctx;
580
581
ctx = regmap_mmio_gen_context(dev, clk_id, regs, config);
582
if (IS_ERR(ctx))
583
return ERR_CAST(ctx);
584
585
return __devm_regmap_init(dev, &regmap_mmio, ctx, config,
586
lock_key, lock_name);
587
}
588
EXPORT_SYMBOL_GPL(__devm_regmap_init_mmio_clk);
589
590
int regmap_mmio_attach_clk(struct regmap *map, struct clk *clk)
591
{
592
struct regmap_mmio_context *ctx = map->bus_context;
593
594
ctx->clk = clk;
595
ctx->attached_clk = true;
596
597
return clk_prepare(ctx->clk);
598
}
599
EXPORT_SYMBOL_GPL(regmap_mmio_attach_clk);
600
601
void regmap_mmio_detach_clk(struct regmap *map)
602
{
603
struct regmap_mmio_context *ctx = map->bus_context;
604
605
clk_unprepare(ctx->clk);
606
607
ctx->attached_clk = false;
608
ctx->clk = NULL;
609
}
610
EXPORT_SYMBOL_GPL(regmap_mmio_detach_clk);
611
612
MODULE_LICENSE("GPL v2");
613
614