]>
Commit | Line | Data |
---|---|---|
45f5ff81 SW |
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 | ||
878ec67b | 19 | #include <linux/clk.h> |
45f5ff81 | 20 | #include <linux/err.h> |
45f5ff81 SW |
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; | |
93258040 | 28 | unsigned reg_bytes; |
45f5ff81 | 29 | unsigned val_bytes; |
93258040 | 30 | unsigned pad_bytes; |
878ec67b | 31 | struct clk *clk; |
45f5ff81 SW |
32 | }; |
33 | ||
41b0c2c9 XL |
34 | static inline void regmap_mmio_regsize_check(size_t reg_size) |
35 | { | |
93258040 XL |
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 | } | |
41b0c2c9 XL |
47 | } |
48 | ||
451485ba XL |
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 | ||
75fb0aae XL |
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 | ||
2e804b7c | 91 | static inline void regmap_mmio_count_check(size_t count, u32 offset) |
41b0c2c9 | 92 | { |
2e804b7c | 93 | BUG_ON(count <= offset); |
41b0c2c9 XL |
94 | } |
95 | ||
88cb32c6 XL |
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 | } | |
41b0c2c9 XL |
113 | } |
114 | ||
45f5ff81 SW |
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; | |
88cb32c6 | 120 | unsigned int offset; |
878ec67b | 121 | int ret; |
45f5ff81 | 122 | |
41b0c2c9 | 123 | regmap_mmio_regsize_check(reg_size); |
40606dba | 124 | |
6b8e090e | 125 | if (!IS_ERR(ctx->clk)) { |
878ec67b PZ |
126 | ret = clk_enable(ctx->clk); |
127 | if (ret < 0) | |
128 | return ret; | |
129 | } | |
130 | ||
88cb32c6 | 131 | offset = regmap_mmio_get_offset(reg, reg_size); |
45f5ff81 SW |
132 | |
133 | while (val_size) { | |
134 | switch (ctx->val_bytes) { | |
135 | case 1: | |
29bb45f2 | 136 | __raw_writeb(*(u8 *)val, ctx->regs + offset); |
45f5ff81 SW |
137 | break; |
138 | case 2: | |
29bb45f2 | 139 | __raw_writew(*(u16 *)val, ctx->regs + offset); |
45f5ff81 SW |
140 | break; |
141 | case 4: | |
29bb45f2 | 142 | __raw_writel(*(u32 *)val, ctx->regs + offset); |
45f5ff81 SW |
143 | break; |
144 | #ifdef CONFIG_64BIT | |
145 | case 8: | |
29bb45f2 | 146 | __raw_writeq(*(u64 *)val, ctx->regs + offset); |
45f5ff81 SW |
147 | break; |
148 | #endif | |
149 | default: | |
150 | /* Should be caught by regmap_mmio_check_config */ | |
40606dba | 151 | BUG(); |
45f5ff81 SW |
152 | } |
153 | val_size -= ctx->val_bytes; | |
154 | val += ctx->val_bytes; | |
155 | offset += ctx->val_bytes; | |
156 | } | |
157 | ||
6b8e090e | 158 | if (!IS_ERR(ctx->clk)) |
878ec67b PZ |
159 | clk_disable(ctx->clk); |
160 | ||
45f5ff81 SW |
161 | return 0; |
162 | } | |
163 | ||
164 | static int regmap_mmio_write(void *context, const void *data, size_t count) | |
165 | { | |
93258040 | 166 | struct regmap_mmio_context *ctx = context; |
88cb32c6 | 167 | unsigned int offset = ctx->reg_bytes + ctx->pad_bytes; |
93258040 | 168 | |
2e804b7c | 169 | regmap_mmio_count_check(count, offset); |
40606dba | 170 | |
93258040 XL |
171 | return regmap_mmio_gather_write(context, data, ctx->reg_bytes, |
172 | data + offset, count - offset); | |
45f5ff81 SW |
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; | |
88cb32c6 | 180 | unsigned int offset; |
878ec67b | 181 | int ret; |
45f5ff81 | 182 | |
41b0c2c9 | 183 | regmap_mmio_regsize_check(reg_size); |
40606dba | 184 | |
6b8e090e | 185 | if (!IS_ERR(ctx->clk)) { |
878ec67b PZ |
186 | ret = clk_enable(ctx->clk); |
187 | if (ret < 0) | |
188 | return ret; | |
189 | } | |
190 | ||
88cb32c6 | 191 | offset = regmap_mmio_get_offset(reg, reg_size); |
45f5ff81 SW |
192 | |
193 | while (val_size) { | |
194 | switch (ctx->val_bytes) { | |
195 | case 1: | |
29bb45f2 | 196 | *(u8 *)val = __raw_readb(ctx->regs + offset); |
45f5ff81 SW |
197 | break; |
198 | case 2: | |
29bb45f2 | 199 | *(u16 *)val = __raw_readw(ctx->regs + offset); |
45f5ff81 SW |
200 | break; |
201 | case 4: | |
29bb45f2 | 202 | *(u32 *)val = __raw_readl(ctx->regs + offset); |
45f5ff81 SW |
203 | break; |
204 | #ifdef CONFIG_64BIT | |
205 | case 8: | |
29bb45f2 | 206 | *(u64 *)val = __raw_readq(ctx->regs + offset); |
45f5ff81 SW |
207 | break; |
208 | #endif | |
209 | default: | |
210 | /* Should be caught by regmap_mmio_check_config */ | |
40606dba | 211 | BUG(); |
45f5ff81 SW |
212 | } |
213 | val_size -= ctx->val_bytes; | |
214 | val += ctx->val_bytes; | |
215 | offset += ctx->val_bytes; | |
216 | } | |
217 | ||
6b8e090e | 218 | if (!IS_ERR(ctx->clk)) |
878ec67b PZ |
219 | clk_disable(ctx->clk); |
220 | ||
45f5ff81 SW |
221 | return 0; |
222 | } | |
223 | ||
224 | static void regmap_mmio_free_context(void *context) | |
225 | { | |
878ec67b PZ |
226 | struct regmap_mmio_context *ctx = context; |
227 | ||
6b8e090e | 228 | if (!IS_ERR(ctx->clk)) { |
878ec67b PZ |
229 | clk_unprepare(ctx->clk); |
230 | clk_put(ctx->clk); | |
231 | } | |
45f5ff81 SW |
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, | |
6a55244e SW |
241 | .reg_format_endian_default = REGMAP_ENDIAN_NATIVE, |
242 | .val_format_endian_default = REGMAP_ENDIAN_NATIVE, | |
45f5ff81 SW |
243 | }; |
244 | ||
878ec67b PZ |
245 | static struct regmap_mmio_context *regmap_mmio_gen_context(struct device *dev, |
246 | const char *clk_id, | |
247 | void __iomem *regs, | |
45f5ff81 SW |
248 | const struct regmap_config *config) |
249 | { | |
250 | struct regmap_mmio_context *ctx; | |
f01ee60f | 251 | int min_stride; |
878ec67b | 252 | int ret; |
45f5ff81 | 253 | |
451485ba XL |
254 | ret = regmap_mmio_regbits_check(config->reg_bits); |
255 | if (ret) | |
256 | return ERR_PTR(ret); | |
45f5ff81 SW |
257 | |
258 | if (config->pad_bits) | |
259 | return ERR_PTR(-EINVAL); | |
260 | ||
75fb0aae XL |
261 | min_stride = regmap_mmio_get_min_stride(config->val_bits); |
262 | if (min_stride < 0) | |
263 | return ERR_PTR(min_stride); | |
45f5ff81 | 264 | |
f01ee60f SW |
265 | if (config->reg_stride < min_stride) |
266 | return ERR_PTR(-EINVAL); | |
267 | ||
6a55244e SW |
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 | ||
46335119 | 276 | ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); |
45f5ff81 SW |
277 | if (!ctx) |
278 | return ERR_PTR(-ENOMEM); | |
279 | ||
280 | ctx->regs = regs; | |
281 | ctx->val_bytes = config->val_bits / 8; | |
93258040 XL |
282 | ctx->reg_bytes = config->reg_bits / 8; |
283 | ctx->pad_bytes = config->pad_bits / 8; | |
6b8e090e | 284 | ctx->clk = ERR_PTR(-ENODEV); |
45f5ff81 | 285 | |
878ec67b PZ |
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 | ||
45f5ff81 | 301 | return ctx; |
878ec67b PZ |
302 | |
303 | err_free: | |
304 | kfree(ctx); | |
305 | ||
306 | return ERR_PTR(ret); | |
45f5ff81 SW |
307 | } |
308 | ||
3cfe7a74 NB |
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) | |
45f5ff81 SW |
314 | { |
315 | struct regmap_mmio_context *ctx; | |
316 | ||
878ec67b | 317 | ctx = regmap_mmio_gen_context(dev, clk_id, regs, config); |
45f5ff81 SW |
318 | if (IS_ERR(ctx)) |
319 | return ERR_CAST(ctx); | |
320 | ||
3cfe7a74 NB |
321 | return __regmap_init(dev, ®map_mmio, ctx, config, |
322 | lock_key, lock_name); | |
45f5ff81 | 323 | } |
3cfe7a74 | 324 | EXPORT_SYMBOL_GPL(__regmap_init_mmio_clk); |
45f5ff81 | 325 | |
3cfe7a74 NB |
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) | |
45f5ff81 SW |
332 | { |
333 | struct regmap_mmio_context *ctx; | |
334 | ||
878ec67b | 335 | ctx = regmap_mmio_gen_context(dev, clk_id, regs, config); |
45f5ff81 SW |
336 | if (IS_ERR(ctx)) |
337 | return ERR_CAST(ctx); | |
338 | ||
3cfe7a74 NB |
339 | return __devm_regmap_init(dev, ®map_mmio, ctx, config, |
340 | lock_key, lock_name); | |
45f5ff81 | 341 | } |
3cfe7a74 | 342 | EXPORT_SYMBOL_GPL(__devm_regmap_init_mmio_clk); |
45f5ff81 SW |
343 | |
344 | MODULE_LICENSE("GPL v2"); |