Merge tag 'sound-4.5-rc5' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai...
[cascardo/linux.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 struct regmap_mmio_context {
27         void __iomem *regs;
28         unsigned reg_bytes;
29         unsigned val_bytes;
30         unsigned pad_bytes;
31         struct clk *clk;
32 };
33
34 static inline void regmap_mmio_regsize_check(size_t reg_size)
35 {
36         switch (reg_size) {
37         case 1:
38         case 2:
39         case 4:
40 #ifdef CONFIG_64BIT
41         case 8:
42 #endif
43                 break;
44         default:
45                 BUG();
46         }
47 }
48
49 static int regmap_mmio_regbits_check(size_t reg_bits)
50 {
51         switch (reg_bits) {
52         case 8:
53         case 16:
54         case 32:
55 #ifdef CONFIG_64BIT
56         case 64:
57 #endif
58                 return 0;
59         default:
60                 return -EINVAL;
61         }
62 }
63
64 static int regmap_mmio_get_min_stride(size_t val_bits)
65 {
66         int min_stride;
67
68         switch (val_bits) {
69         case 8:
70                 /* The core treats 0 as 1 */
71                 min_stride = 0;
72                 return 0;
73         case 16:
74                 min_stride = 2;
75                 break;
76         case 32:
77                 min_stride = 4;
78                 break;
79 #ifdef CONFIG_64BIT
80         case 64:
81                 min_stride = 8;
82                 break;
83 #endif
84         default:
85                 return -EINVAL;
86         }
87
88         return min_stride;
89 }
90
91 static inline void regmap_mmio_count_check(size_t count, u32 offset)
92 {
93         BUG_ON(count <= offset);
94 }
95
96 static inline unsigned int
97 regmap_mmio_get_offset(const void *reg, size_t reg_size)
98 {
99         switch (reg_size) {
100         case 1:
101                 return *(u8 *)reg;
102         case 2:
103                 return *(u16 *)reg;
104         case 4:
105                 return *(u32 *)reg;
106 #ifdef CONFIG_64BIT
107         case 8:
108                 return *(u64 *)reg;
109 #endif
110         default:
111                 BUG();
112         }
113 }
114
115 static int regmap_mmio_gather_write(void *context,
116                                     const void *reg, size_t reg_size,
117                                     const void *val, size_t val_size)
118 {
119         struct regmap_mmio_context *ctx = context;
120         unsigned int offset;
121         int ret;
122
123         regmap_mmio_regsize_check(reg_size);
124
125         if (!IS_ERR(ctx->clk)) {
126                 ret = clk_enable(ctx->clk);
127                 if (ret < 0)
128                         return ret;
129         }
130
131         offset = regmap_mmio_get_offset(reg, reg_size);
132
133         while (val_size) {
134                 switch (ctx->val_bytes) {
135                 case 1:
136                         writeb(*(u8 *)val, ctx->regs + offset);
137                         break;
138                 case 2:
139                         writew(*(u16 *)val, ctx->regs + offset);
140                         break;
141                 case 4:
142                         writel(*(u32 *)val, ctx->regs + offset);
143                         break;
144 #ifdef CONFIG_64BIT
145                 case 8:
146                         writeq(*(u64 *)val, ctx->regs + offset);
147                         break;
148 #endif
149                 default:
150                         /* Should be caught by regmap_mmio_check_config */
151                         BUG();
152                 }
153                 val_size -= ctx->val_bytes;
154                 val += ctx->val_bytes;
155                 offset += ctx->val_bytes;
156         }
157
158         if (!IS_ERR(ctx->clk))
159                 clk_disable(ctx->clk);
160
161         return 0;
162 }
163
164 static int regmap_mmio_write(void *context, const void *data, size_t count)
165 {
166         struct regmap_mmio_context *ctx = context;
167         unsigned int offset = ctx->reg_bytes + ctx->pad_bytes;
168
169         regmap_mmio_count_check(count, offset);
170
171         return regmap_mmio_gather_write(context, data, ctx->reg_bytes,
172                                         data + offset, count - offset);
173 }
174
175 static int regmap_mmio_read(void *context,
176                             const void *reg, size_t reg_size,
177                             void *val, size_t val_size)
178 {
179         struct regmap_mmio_context *ctx = context;
180         unsigned int offset;
181         int ret;
182
183         regmap_mmio_regsize_check(reg_size);
184
185         if (!IS_ERR(ctx->clk)) {
186                 ret = clk_enable(ctx->clk);
187                 if (ret < 0)
188                         return ret;
189         }
190
191         offset = regmap_mmio_get_offset(reg, reg_size);
192
193         while (val_size) {
194                 switch (ctx->val_bytes) {
195                 case 1:
196                         *(u8 *)val = readb(ctx->regs + offset);
197                         break;
198                 case 2:
199                         *(u16 *)val = readw(ctx->regs + offset);
200                         break;
201                 case 4:
202                         *(u32 *)val = readl(ctx->regs + offset);
203                         break;
204 #ifdef CONFIG_64BIT
205                 case 8:
206                         *(u64 *)val = readq(ctx->regs + offset);
207                         break;
208 #endif
209                 default:
210                         /* Should be caught by regmap_mmio_check_config */
211                         BUG();
212                 }
213                 val_size -= ctx->val_bytes;
214                 val += ctx->val_bytes;
215                 offset += ctx->val_bytes;
216         }
217
218         if (!IS_ERR(ctx->clk))
219                 clk_disable(ctx->clk);
220
221         return 0;
222 }
223
224 static void regmap_mmio_free_context(void *context)
225 {
226         struct regmap_mmio_context *ctx = context;
227
228         if (!IS_ERR(ctx->clk)) {
229                 clk_unprepare(ctx->clk);
230                 clk_put(ctx->clk);
231         }
232         kfree(context);
233 }
234
235 static struct regmap_bus regmap_mmio = {
236         .fast_io = true,
237         .write = regmap_mmio_write,
238         .gather_write = regmap_mmio_gather_write,
239         .read = regmap_mmio_read,
240         .free_context = regmap_mmio_free_context,
241         .reg_format_endian_default = REGMAP_ENDIAN_NATIVE,
242         .val_format_endian_default = REGMAP_ENDIAN_NATIVE,
243 };
244
245 static struct regmap_mmio_context *regmap_mmio_gen_context(struct device *dev,
246                                         const char *clk_id,
247                                         void __iomem *regs,
248                                         const struct regmap_config *config)
249 {
250         struct regmap_mmio_context *ctx;
251         int min_stride;
252         int ret;
253
254         ret = regmap_mmio_regbits_check(config->reg_bits);
255         if (ret)
256                 return ERR_PTR(ret);
257
258         if (config->pad_bits)
259                 return ERR_PTR(-EINVAL);
260
261         min_stride = regmap_mmio_get_min_stride(config->val_bits);
262         if (min_stride < 0)
263                 return ERR_PTR(min_stride);
264
265         if (config->reg_stride < min_stride)
266                 return ERR_PTR(-EINVAL);
267
268         switch (config->reg_format_endian) {
269         case REGMAP_ENDIAN_DEFAULT:
270         case REGMAP_ENDIAN_NATIVE:
271                 break;
272         default:
273                 return ERR_PTR(-EINVAL);
274         }
275
276         ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
277         if (!ctx)
278                 return ERR_PTR(-ENOMEM);
279
280         ctx->regs = regs;
281         ctx->val_bytes = config->val_bits / 8;
282         ctx->reg_bytes = config->reg_bits / 8;
283         ctx->pad_bytes = config->pad_bits / 8;
284         ctx->clk = ERR_PTR(-ENODEV);
285
286         if (clk_id == NULL)
287                 return ctx;
288
289         ctx->clk = clk_get(dev, clk_id);
290         if (IS_ERR(ctx->clk)) {
291                 ret = PTR_ERR(ctx->clk);
292                 goto err_free;
293         }
294
295         ret = clk_prepare(ctx->clk);
296         if (ret < 0) {
297                 clk_put(ctx->clk);
298                 goto err_free;
299         }
300
301         return ctx;
302
303 err_free:
304         kfree(ctx);
305
306         return ERR_PTR(ret);
307 }
308
309 struct regmap *__regmap_init_mmio_clk(struct device *dev, const char *clk_id,
310                                       void __iomem *regs,
311                                       const struct regmap_config *config,
312                                       struct lock_class_key *lock_key,
313                                       const char *lock_name)
314 {
315         struct regmap_mmio_context *ctx;
316
317         ctx = regmap_mmio_gen_context(dev, clk_id, regs, config);
318         if (IS_ERR(ctx))
319                 return ERR_CAST(ctx);
320
321         return __regmap_init(dev, &regmap_mmio, ctx, config,
322                              lock_key, lock_name);
323 }
324 EXPORT_SYMBOL_GPL(__regmap_init_mmio_clk);
325
326 struct regmap *__devm_regmap_init_mmio_clk(struct device *dev,
327                                            const char *clk_id,
328                                            void __iomem *regs,
329                                            const struct regmap_config *config,
330                                            struct lock_class_key *lock_key,
331                                            const char *lock_name)
332 {
333         struct regmap_mmio_context *ctx;
334
335         ctx = regmap_mmio_gen_context(dev, clk_id, regs, config);
336         if (IS_ERR(ctx))
337                 return ERR_CAST(ctx);
338
339         return __devm_regmap_init(dev, &regmap_mmio, ctx, config,
340                                   lock_key, lock_name);
341 }
342 EXPORT_SYMBOL_GPL(__devm_regmap_init_mmio_clk);
343
344 MODULE_LICENSE("GPL v2");