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