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 int val_bytes;
19 	bool relaxed_mmio;
20 
21 	bool attached_clk;
22 	struct clk *clk;
23 
24 	void (*reg_write)(struct regmap_mmio_context *ctx,
25 			  unsigned int reg, unsigned int val);
26 	unsigned int (*reg_read)(struct regmap_mmio_context *ctx,
27 			         unsigned int reg);
28 };
29 
30 static int regmap_mmio_regbits_check(size_t reg_bits)
31 {
32 	switch (reg_bits) {
33 	case 8:
34 	case 16:
35 	case 32:
36 #ifdef CONFIG_64BIT
37 	case 64:
38 #endif
39 		return 0;
40 	default:
41 		return -EINVAL;
42 	}
43 }
44 
45 static int regmap_mmio_get_min_stride(size_t val_bits)
46 {
47 	int min_stride;
48 
49 	switch (val_bits) {
50 	case 8:
51 		/* The core treats 0 as 1 */
52 		min_stride = 0;
53 		return 0;
54 	case 16:
55 		min_stride = 2;
56 		break;
57 	case 32:
58 		min_stride = 4;
59 		break;
60 #ifdef CONFIG_64BIT
61 	case 64:
62 		min_stride = 8;
63 		break;
64 #endif
65 	default:
66 		return -EINVAL;
67 	}
68 
69 	return min_stride;
70 }
71 
72 static void regmap_mmio_write8(struct regmap_mmio_context *ctx,
73 				unsigned int reg,
74 				unsigned int val)
75 {
76 	writeb(val, ctx->regs + reg);
77 }
78 
79 static void regmap_mmio_write8_relaxed(struct regmap_mmio_context *ctx,
80 				unsigned int reg,
81 				unsigned int val)
82 {
83 	writeb_relaxed(val, ctx->regs + reg);
84 }
85 
86 static void regmap_mmio_write16le(struct regmap_mmio_context *ctx,
87 				  unsigned int reg,
88 				  unsigned int val)
89 {
90 	writew(val, ctx->regs + reg);
91 }
92 
93 static void regmap_mmio_write16le_relaxed(struct regmap_mmio_context *ctx,
94 				  unsigned int reg,
95 				  unsigned int val)
96 {
97 	writew_relaxed(val, ctx->regs + reg);
98 }
99 
100 static void regmap_mmio_write16be(struct regmap_mmio_context *ctx,
101 				  unsigned int reg,
102 				  unsigned int val)
103 {
104 	iowrite16be(val, ctx->regs + reg);
105 }
106 
107 static void regmap_mmio_write32le(struct regmap_mmio_context *ctx,
108 				  unsigned int reg,
109 				  unsigned int val)
110 {
111 	writel(val, ctx->regs + reg);
112 }
113 
114 static void regmap_mmio_write32le_relaxed(struct regmap_mmio_context *ctx,
115 				  unsigned int reg,
116 				  unsigned int val)
117 {
118 	writel_relaxed(val, ctx->regs + reg);
119 }
120 
121 static void regmap_mmio_write32be(struct regmap_mmio_context *ctx,
122 				  unsigned int reg,
123 				  unsigned int val)
124 {
125 	iowrite32be(val, ctx->regs + reg);
126 }
127 
128 #ifdef CONFIG_64BIT
129 static void regmap_mmio_write64le(struct regmap_mmio_context *ctx,
130 				  unsigned int reg,
131 				  unsigned int val)
132 {
133 	writeq(val, ctx->regs + reg);
134 }
135 
136 static void regmap_mmio_write64le_relaxed(struct regmap_mmio_context *ctx,
137 				  unsigned int reg,
138 				  unsigned int val)
139 {
140 	writeq_relaxed(val, ctx->regs + reg);
141 }
142 #endif
143 
144 static int regmap_mmio_write(void *context, unsigned int reg, unsigned int val)
145 {
146 	struct regmap_mmio_context *ctx = context;
147 	int ret;
148 
149 	if (!IS_ERR(ctx->clk)) {
150 		ret = clk_enable(ctx->clk);
151 		if (ret < 0)
152 			return ret;
153 	}
154 
155 	ctx->reg_write(ctx, reg, val);
156 
157 	if (!IS_ERR(ctx->clk))
158 		clk_disable(ctx->clk);
159 
160 	return 0;
161 }
162 
163 static unsigned int regmap_mmio_read8(struct regmap_mmio_context *ctx,
164 				      unsigned int reg)
165 {
166 	return readb(ctx->regs + reg);
167 }
168 
169 static unsigned int regmap_mmio_read8_relaxed(struct regmap_mmio_context *ctx,
170 				      unsigned int reg)
171 {
172 	return readb_relaxed(ctx->regs + reg);
173 }
174 
175 static unsigned int regmap_mmio_read16le(struct regmap_mmio_context *ctx,
176 				         unsigned int reg)
177 {
178 	return readw(ctx->regs + reg);
179 }
180 
181 static unsigned int regmap_mmio_read16le_relaxed(struct regmap_mmio_context *ctx,
182 						 unsigned int reg)
183 {
184 	return readw_relaxed(ctx->regs + reg);
185 }
186 
187 static unsigned int regmap_mmio_read16be(struct regmap_mmio_context *ctx,
188 				         unsigned int reg)
189 {
190 	return ioread16be(ctx->regs + reg);
191 }
192 
193 static unsigned int regmap_mmio_read32le(struct regmap_mmio_context *ctx,
194 				         unsigned int reg)
195 {
196 	return readl(ctx->regs + reg);
197 }
198 
199 static unsigned int regmap_mmio_read32le_relaxed(struct regmap_mmio_context *ctx,
200 						 unsigned int reg)
201 {
202 	return readl_relaxed(ctx->regs + reg);
203 }
204 
205 static unsigned int regmap_mmio_read32be(struct regmap_mmio_context *ctx,
206 				         unsigned int reg)
207 {
208 	return ioread32be(ctx->regs + reg);
209 }
210 
211 #ifdef CONFIG_64BIT
212 static unsigned int regmap_mmio_read64le(struct regmap_mmio_context *ctx,
213 				         unsigned int reg)
214 {
215 	return readq(ctx->regs + reg);
216 }
217 
218 static unsigned int regmap_mmio_read64le_relaxed(struct regmap_mmio_context *ctx,
219 						 unsigned int reg)
220 {
221 	return readq_relaxed(ctx->regs + reg);
222 }
223 #endif
224 
225 static int regmap_mmio_read(void *context, unsigned int reg, unsigned int *val)
226 {
227 	struct regmap_mmio_context *ctx = context;
228 	int ret;
229 
230 	if (!IS_ERR(ctx->clk)) {
231 		ret = clk_enable(ctx->clk);
232 		if (ret < 0)
233 			return ret;
234 	}
235 
236 	*val = ctx->reg_read(ctx, reg);
237 
238 	if (!IS_ERR(ctx->clk))
239 		clk_disable(ctx->clk);
240 
241 	return 0;
242 }
243 
244 static void regmap_mmio_free_context(void *context)
245 {
246 	struct regmap_mmio_context *ctx = context;
247 
248 	if (!IS_ERR(ctx->clk)) {
249 		clk_unprepare(ctx->clk);
250 		if (!ctx->attached_clk)
251 			clk_put(ctx->clk);
252 	}
253 	kfree(context);
254 }
255 
256 static const struct regmap_bus regmap_mmio = {
257 	.fast_io = true,
258 	.reg_write = regmap_mmio_write,
259 	.reg_read = regmap_mmio_read,
260 	.free_context = regmap_mmio_free_context,
261 	.val_format_endian_default = REGMAP_ENDIAN_LITTLE,
262 };
263 
264 static struct regmap_mmio_context *regmap_mmio_gen_context(struct device *dev,
265 					const char *clk_id,
266 					void __iomem *regs,
267 					const struct regmap_config *config)
268 {
269 	struct regmap_mmio_context *ctx;
270 	int min_stride;
271 	int ret;
272 
273 	ret = regmap_mmio_regbits_check(config->reg_bits);
274 	if (ret)
275 		return ERR_PTR(ret);
276 
277 	if (config->pad_bits)
278 		return ERR_PTR(-EINVAL);
279 
280 	min_stride = regmap_mmio_get_min_stride(config->val_bits);
281 	if (min_stride < 0)
282 		return ERR_PTR(min_stride);
283 
284 	if (config->reg_stride < min_stride)
285 		return ERR_PTR(-EINVAL);
286 
287 	ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
288 	if (!ctx)
289 		return ERR_PTR(-ENOMEM);
290 
291 	ctx->regs = regs;
292 	ctx->val_bytes = config->val_bits / 8;
293 	ctx->relaxed_mmio = config->use_relaxed_mmio;
294 	ctx->clk = ERR_PTR(-ENODEV);
295 
296 	switch (regmap_get_val_endian(dev, &regmap_mmio, config)) {
297 	case REGMAP_ENDIAN_DEFAULT:
298 	case REGMAP_ENDIAN_LITTLE:
299 #ifdef __LITTLE_ENDIAN
300 	case REGMAP_ENDIAN_NATIVE:
301 #endif
302 		switch (config->val_bits) {
303 		case 8:
304 			if (ctx->relaxed_mmio) {
305 				ctx->reg_read = regmap_mmio_read8_relaxed;
306 				ctx->reg_write = regmap_mmio_write8_relaxed;
307 			} else {
308 				ctx->reg_read = regmap_mmio_read8;
309 				ctx->reg_write = regmap_mmio_write8;
310 			}
311 			break;
312 		case 16:
313 			if (ctx->relaxed_mmio) {
314 				ctx->reg_read = regmap_mmio_read16le_relaxed;
315 				ctx->reg_write = regmap_mmio_write16le_relaxed;
316 			} else {
317 				ctx->reg_read = regmap_mmio_read16le;
318 				ctx->reg_write = regmap_mmio_write16le;
319 			}
320 			break;
321 		case 32:
322 			if (ctx->relaxed_mmio) {
323 				ctx->reg_read = regmap_mmio_read32le_relaxed;
324 				ctx->reg_write = regmap_mmio_write32le_relaxed;
325 			} else {
326 				ctx->reg_read = regmap_mmio_read32le;
327 				ctx->reg_write = regmap_mmio_write32le;
328 			}
329 			break;
330 #ifdef CONFIG_64BIT
331 		case 64:
332 			if (ctx->relaxed_mmio) {
333 				ctx->reg_read = regmap_mmio_read64le_relaxed;
334 				ctx->reg_write = regmap_mmio_write64le_relaxed;
335 			} else {
336 				ctx->reg_read = regmap_mmio_read64le;
337 				ctx->reg_write = regmap_mmio_write64le;
338 			}
339 			break;
340 #endif
341 		default:
342 			ret = -EINVAL;
343 			goto err_free;
344 		}
345 		break;
346 	case REGMAP_ENDIAN_BIG:
347 #ifdef __BIG_ENDIAN
348 	case REGMAP_ENDIAN_NATIVE:
349 #endif
350 		switch (config->val_bits) {
351 		case 8:
352 			ctx->reg_read = regmap_mmio_read8;
353 			ctx->reg_write = regmap_mmio_write8;
354 			break;
355 		case 16:
356 			ctx->reg_read = regmap_mmio_read16be;
357 			ctx->reg_write = regmap_mmio_write16be;
358 			break;
359 		case 32:
360 			ctx->reg_read = regmap_mmio_read32be;
361 			ctx->reg_write = regmap_mmio_write32be;
362 			break;
363 		default:
364 			ret = -EINVAL;
365 			goto err_free;
366 		}
367 		break;
368 	default:
369 		ret = -EINVAL;
370 		goto err_free;
371 	}
372 
373 	if (clk_id == NULL)
374 		return ctx;
375 
376 	ctx->clk = clk_get(dev, clk_id);
377 	if (IS_ERR(ctx->clk)) {
378 		ret = PTR_ERR(ctx->clk);
379 		goto err_free;
380 	}
381 
382 	ret = clk_prepare(ctx->clk);
383 	if (ret < 0) {
384 		clk_put(ctx->clk);
385 		goto err_free;
386 	}
387 
388 	return ctx;
389 
390 err_free:
391 	kfree(ctx);
392 
393 	return ERR_PTR(ret);
394 }
395 
396 struct regmap *__regmap_init_mmio_clk(struct device *dev, const char *clk_id,
397 				      void __iomem *regs,
398 				      const struct regmap_config *config,
399 				      struct lock_class_key *lock_key,
400 				      const char *lock_name)
401 {
402 	struct regmap_mmio_context *ctx;
403 
404 	ctx = regmap_mmio_gen_context(dev, clk_id, regs, config);
405 	if (IS_ERR(ctx))
406 		return ERR_CAST(ctx);
407 
408 	return __regmap_init(dev, &regmap_mmio, ctx, config,
409 			     lock_key, lock_name);
410 }
411 EXPORT_SYMBOL_GPL(__regmap_init_mmio_clk);
412 
413 struct regmap *__devm_regmap_init_mmio_clk(struct device *dev,
414 					   const char *clk_id,
415 					   void __iomem *regs,
416 					   const struct regmap_config *config,
417 					   struct lock_class_key *lock_key,
418 					   const char *lock_name)
419 {
420 	struct regmap_mmio_context *ctx;
421 
422 	ctx = regmap_mmio_gen_context(dev, clk_id, regs, config);
423 	if (IS_ERR(ctx))
424 		return ERR_CAST(ctx);
425 
426 	return __devm_regmap_init(dev, &regmap_mmio, ctx, config,
427 				  lock_key, lock_name);
428 }
429 EXPORT_SYMBOL_GPL(__devm_regmap_init_mmio_clk);
430 
431 int regmap_mmio_attach_clk(struct regmap *map, struct clk *clk)
432 {
433 	struct regmap_mmio_context *ctx = map->bus_context;
434 
435 	ctx->clk = clk;
436 	ctx->attached_clk = true;
437 
438 	return clk_prepare(ctx->clk);
439 }
440 EXPORT_SYMBOL_GPL(regmap_mmio_attach_clk);
441 
442 void regmap_mmio_detach_clk(struct regmap *map)
443 {
444 	struct regmap_mmio_context *ctx = map->bus_context;
445 
446 	clk_unprepare(ctx->clk);
447 
448 	ctx->attached_clk = false;
449 	ctx->clk = NULL;
450 }
451 EXPORT_SYMBOL_GPL(regmap_mmio_detach_clk);
452 
453 MODULE_LICENSE("GPL v2");
454