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