Merge branch 'for-3.9-cpuset' of git://git.kernel.org/pub/scm/linux/kernel/git/tj...
[~shefty/rdma-dev.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/err.h>
20 #include <linux/init.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 val_bytes;
29 };
30
31 static int regmap_mmio_gather_write(void *context,
32                                     const void *reg, size_t reg_size,
33                                     const void *val, size_t val_size)
34 {
35         struct regmap_mmio_context *ctx = context;
36         u32 offset;
37
38         BUG_ON(reg_size != 4);
39
40         offset = *(u32 *)reg;
41
42         while (val_size) {
43                 switch (ctx->val_bytes) {
44                 case 1:
45                         writeb(*(u8 *)val, ctx->regs + offset);
46                         break;
47                 case 2:
48                         writew(*(u16 *)val, ctx->regs + offset);
49                         break;
50                 case 4:
51                         writel(*(u32 *)val, ctx->regs + offset);
52                         break;
53 #ifdef CONFIG_64BIT
54                 case 8:
55                         writeq(*(u64 *)val, ctx->regs + offset);
56                         break;
57 #endif
58                 default:
59                         /* Should be caught by regmap_mmio_check_config */
60                         BUG();
61                 }
62                 val_size -= ctx->val_bytes;
63                 val += ctx->val_bytes;
64                 offset += ctx->val_bytes;
65         }
66
67         return 0;
68 }
69
70 static int regmap_mmio_write(void *context, const void *data, size_t count)
71 {
72         BUG_ON(count < 4);
73
74         return regmap_mmio_gather_write(context, data, 4, data + 4, count - 4);
75 }
76
77 static int regmap_mmio_read(void *context,
78                             const void *reg, size_t reg_size,
79                             void *val, size_t val_size)
80 {
81         struct regmap_mmio_context *ctx = context;
82         u32 offset;
83
84         BUG_ON(reg_size != 4);
85
86         offset = *(u32 *)reg;
87
88         while (val_size) {
89                 switch (ctx->val_bytes) {
90                 case 1:
91                         *(u8 *)val = readb(ctx->regs + offset);
92                         break;
93                 case 2:
94                         *(u16 *)val = readw(ctx->regs + offset);
95                         break;
96                 case 4:
97                         *(u32 *)val = readl(ctx->regs + offset);
98                         break;
99 #ifdef CONFIG_64BIT
100                 case 8:
101                         *(u64 *)val = readq(ctx->regs + offset);
102                         break;
103 #endif
104                 default:
105                         /* Should be caught by regmap_mmio_check_config */
106                         BUG();
107                 }
108                 val_size -= ctx->val_bytes;
109                 val += ctx->val_bytes;
110                 offset += ctx->val_bytes;
111         }
112
113         return 0;
114 }
115
116 static void regmap_mmio_free_context(void *context)
117 {
118         kfree(context);
119 }
120
121 static struct regmap_bus regmap_mmio = {
122         .fast_io = true,
123         .write = regmap_mmio_write,
124         .gather_write = regmap_mmio_gather_write,
125         .read = regmap_mmio_read,
126         .free_context = regmap_mmio_free_context,
127         .reg_format_endian_default = REGMAP_ENDIAN_NATIVE,
128         .val_format_endian_default = REGMAP_ENDIAN_NATIVE,
129 };
130
131 static struct regmap_mmio_context *regmap_mmio_gen_context(void __iomem *regs,
132                                         const struct regmap_config *config)
133 {
134         struct regmap_mmio_context *ctx;
135         int min_stride;
136
137         if (config->reg_bits != 32)
138                 return ERR_PTR(-EINVAL);
139
140         if (config->pad_bits)
141                 return ERR_PTR(-EINVAL);
142
143         switch (config->val_bits) {
144         case 8:
145                 /* The core treats 0 as 1 */
146                 min_stride = 0;
147                 break;
148         case 16:
149                 min_stride = 2;
150                 break;
151         case 32:
152                 min_stride = 4;
153                 break;
154 #ifdef CONFIG_64BIT
155         case 64:
156                 min_stride = 8;
157                 break;
158 #endif
159                 break;
160         default:
161                 return ERR_PTR(-EINVAL);
162         }
163
164         if (config->reg_stride < min_stride)
165                 return ERR_PTR(-EINVAL);
166
167         switch (config->reg_format_endian) {
168         case REGMAP_ENDIAN_DEFAULT:
169         case REGMAP_ENDIAN_NATIVE:
170                 break;
171         default:
172                 return ERR_PTR(-EINVAL);
173         }
174
175         ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
176         if (!ctx)
177                 return ERR_PTR(-ENOMEM);
178
179         ctx->regs = regs;
180         ctx->val_bytes = config->val_bits / 8;
181
182         return ctx;
183 }
184
185 /**
186  * regmap_init_mmio(): Initialise register map
187  *
188  * @dev: Device that will be interacted with
189  * @regs: Pointer to memory-mapped IO region
190  * @config: Configuration for register map
191  *
192  * The return value will be an ERR_PTR() on error or a valid pointer to
193  * a struct regmap.
194  */
195 struct regmap *regmap_init_mmio(struct device *dev,
196                                 void __iomem *regs,
197                                 const struct regmap_config *config)
198 {
199         struct regmap_mmio_context *ctx;
200
201         ctx = regmap_mmio_gen_context(regs, config);
202         if (IS_ERR(ctx))
203                 return ERR_CAST(ctx);
204
205         return regmap_init(dev, &regmap_mmio, ctx, config);
206 }
207 EXPORT_SYMBOL_GPL(regmap_init_mmio);
208
209 /**
210  * devm_regmap_init_mmio(): Initialise managed register map
211  *
212  * @dev: Device that will be interacted with
213  * @regs: Pointer to memory-mapped IO region
214  * @config: Configuration for register map
215  *
216  * The return value will be an ERR_PTR() on error or a valid pointer
217  * to a struct regmap.  The regmap will be automatically freed by the
218  * device management code.
219  */
220 struct regmap *devm_regmap_init_mmio(struct device *dev,
221                                      void __iomem *regs,
222                                      const struct regmap_config *config)
223 {
224         struct regmap_mmio_context *ctx;
225
226         ctx = regmap_mmio_gen_context(regs, config);
227         if (IS_ERR(ctx))
228                 return ERR_CAST(ctx);
229
230         return devm_regmap_init(dev, &regmap_mmio, ctx, config);
231 }
232 EXPORT_SYMBOL_GPL(devm_regmap_init_mmio);
233
234 MODULE_LICENSE("GPL v2");