blob: 4f1efce94034e93c5430690f25623566b0380652 [file] [log] [blame]
Stephen Warren45f5ff82012-04-04 15:48:31 -06001/*
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
Philipp Zabel878ec672013-02-14 17:39:08 +010019#include <linux/clk.h>
Stephen Warren45f5ff82012-04-04 15:48:31 -060020#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
27struct regmap_mmio_context {
28 void __iomem *regs;
29 unsigned val_bytes;
Philipp Zabel878ec672013-02-14 17:39:08 +010030 struct clk *clk;
Stephen Warren45f5ff82012-04-04 15:48:31 -060031};
32
Xiubo Li41b0c2c2014-03-27 12:42:42 +080033static inline void regmap_mmio_regsize_check(size_t reg_size)
34{
35 BUG_ON(reg_size != 4);
36}
37
38static inline void regmap_mmio_count_check(size_t count)
39{
40 BUG_ON(count < 4);
41}
42
Stephen Warren45f5ff82012-04-04 15:48:31 -060043static int regmap_mmio_gather_write(void *context,
44 const void *reg, size_t reg_size,
45 const void *val, size_t val_size)
46{
47 struct regmap_mmio_context *ctx = context;
48 u32 offset;
Philipp Zabel878ec672013-02-14 17:39:08 +010049 int ret;
Stephen Warren45f5ff82012-04-04 15:48:31 -060050
Xiubo Li41b0c2c2014-03-27 12:42:42 +080051 regmap_mmio_regsize_check(reg_size);
Stephen Warren40606db2012-04-06 15:17:32 -060052
Stephen Warren6b8e0902013-11-25 15:12:47 -070053 if (!IS_ERR(ctx->clk)) {
Philipp Zabel878ec672013-02-14 17:39:08 +010054 ret = clk_enable(ctx->clk);
55 if (ret < 0)
56 return ret;
57 }
58
Stephen Warren6a552442012-05-24 10:47:27 -060059 offset = *(u32 *)reg;
Stephen Warren45f5ff82012-04-04 15:48:31 -060060
61 while (val_size) {
62 switch (ctx->val_bytes) {
63 case 1:
64 writeb(*(u8 *)val, ctx->regs + offset);
65 break;
66 case 2:
Stephen Warren6a552442012-05-24 10:47:27 -060067 writew(*(u16 *)val, ctx->regs + offset);
Stephen Warren45f5ff82012-04-04 15:48:31 -060068 break;
69 case 4:
Stephen Warren6a552442012-05-24 10:47:27 -060070 writel(*(u32 *)val, ctx->regs + offset);
Stephen Warren45f5ff82012-04-04 15:48:31 -060071 break;
72#ifdef CONFIG_64BIT
73 case 8:
Stephen Warren6a552442012-05-24 10:47:27 -060074 writeq(*(u64 *)val, ctx->regs + offset);
Stephen Warren45f5ff82012-04-04 15:48:31 -060075 break;
76#endif
77 default:
78 /* Should be caught by regmap_mmio_check_config */
Stephen Warren40606db2012-04-06 15:17:32 -060079 BUG();
Stephen Warren45f5ff82012-04-04 15:48:31 -060080 }
81 val_size -= ctx->val_bytes;
82 val += ctx->val_bytes;
83 offset += ctx->val_bytes;
84 }
85
Stephen Warren6b8e0902013-11-25 15:12:47 -070086 if (!IS_ERR(ctx->clk))
Philipp Zabel878ec672013-02-14 17:39:08 +010087 clk_disable(ctx->clk);
88
Stephen Warren45f5ff82012-04-04 15:48:31 -060089 return 0;
90}
91
92static int regmap_mmio_write(void *context, const void *data, size_t count)
93{
Xiubo Li41b0c2c2014-03-27 12:42:42 +080094 regmap_mmio_count_check(count);
Stephen Warren40606db2012-04-06 15:17:32 -060095
Stephen Warren45f5ff82012-04-04 15:48:31 -060096 return regmap_mmio_gather_write(context, data, 4, data + 4, count - 4);
97}
98
99static int regmap_mmio_read(void *context,
100 const void *reg, size_t reg_size,
101 void *val, size_t val_size)
102{
103 struct regmap_mmio_context *ctx = context;
104 u32 offset;
Philipp Zabel878ec672013-02-14 17:39:08 +0100105 int ret;
Stephen Warren45f5ff82012-04-04 15:48:31 -0600106
Xiubo Li41b0c2c2014-03-27 12:42:42 +0800107 regmap_mmio_regsize_check(reg_size);
Stephen Warren40606db2012-04-06 15:17:32 -0600108
Stephen Warren6b8e0902013-11-25 15:12:47 -0700109 if (!IS_ERR(ctx->clk)) {
Philipp Zabel878ec672013-02-14 17:39:08 +0100110 ret = clk_enable(ctx->clk);
111 if (ret < 0)
112 return ret;
113 }
114
Stephen Warren6a552442012-05-24 10:47:27 -0600115 offset = *(u32 *)reg;
Stephen Warren45f5ff82012-04-04 15:48:31 -0600116
117 while (val_size) {
118 switch (ctx->val_bytes) {
119 case 1:
120 *(u8 *)val = readb(ctx->regs + offset);
121 break;
122 case 2:
Stephen Warren6a552442012-05-24 10:47:27 -0600123 *(u16 *)val = readw(ctx->regs + offset);
Stephen Warren45f5ff82012-04-04 15:48:31 -0600124 break;
125 case 4:
Stephen Warren6a552442012-05-24 10:47:27 -0600126 *(u32 *)val = readl(ctx->regs + offset);
Stephen Warren45f5ff82012-04-04 15:48:31 -0600127 break;
128#ifdef CONFIG_64BIT
129 case 8:
Stephen Warren6a552442012-05-24 10:47:27 -0600130 *(u64 *)val = readq(ctx->regs + offset);
Stephen Warren45f5ff82012-04-04 15:48:31 -0600131 break;
132#endif
133 default:
134 /* Should be caught by regmap_mmio_check_config */
Stephen Warren40606db2012-04-06 15:17:32 -0600135 BUG();
Stephen Warren45f5ff82012-04-04 15:48:31 -0600136 }
137 val_size -= ctx->val_bytes;
138 val += ctx->val_bytes;
139 offset += ctx->val_bytes;
140 }
141
Stephen Warren6b8e0902013-11-25 15:12:47 -0700142 if (!IS_ERR(ctx->clk))
Philipp Zabel878ec672013-02-14 17:39:08 +0100143 clk_disable(ctx->clk);
144
Stephen Warren45f5ff82012-04-04 15:48:31 -0600145 return 0;
146}
147
148static void regmap_mmio_free_context(void *context)
149{
Philipp Zabel878ec672013-02-14 17:39:08 +0100150 struct regmap_mmio_context *ctx = context;
151
Stephen Warren6b8e0902013-11-25 15:12:47 -0700152 if (!IS_ERR(ctx->clk)) {
Philipp Zabel878ec672013-02-14 17:39:08 +0100153 clk_unprepare(ctx->clk);
154 clk_put(ctx->clk);
155 }
Stephen Warren45f5ff82012-04-04 15:48:31 -0600156 kfree(context);
157}
158
159static struct regmap_bus regmap_mmio = {
160 .fast_io = true,
161 .write = regmap_mmio_write,
162 .gather_write = regmap_mmio_gather_write,
163 .read = regmap_mmio_read,
164 .free_context = regmap_mmio_free_context,
Stephen Warren6a552442012-05-24 10:47:27 -0600165 .reg_format_endian_default = REGMAP_ENDIAN_NATIVE,
166 .val_format_endian_default = REGMAP_ENDIAN_NATIVE,
Stephen Warren45f5ff82012-04-04 15:48:31 -0600167};
168
Philipp Zabel878ec672013-02-14 17:39:08 +0100169static struct regmap_mmio_context *regmap_mmio_gen_context(struct device *dev,
170 const char *clk_id,
171 void __iomem *regs,
Stephen Warren45f5ff82012-04-04 15:48:31 -0600172 const struct regmap_config *config)
173{
174 struct regmap_mmio_context *ctx;
Stephen Warrenf01ee602012-04-09 13:40:24 -0600175 int min_stride;
Philipp Zabel878ec672013-02-14 17:39:08 +0100176 int ret;
Stephen Warren45f5ff82012-04-04 15:48:31 -0600177
178 if (config->reg_bits != 32)
179 return ERR_PTR(-EINVAL);
180
181 if (config->pad_bits)
182 return ERR_PTR(-EINVAL);
183
184 switch (config->val_bits) {
185 case 8:
Stephen Warrenf01ee602012-04-09 13:40:24 -0600186 /* The core treats 0 as 1 */
187 min_stride = 0;
188 break;
Stephen Warren45f5ff82012-04-04 15:48:31 -0600189 case 16:
Stephen Warrenf01ee602012-04-09 13:40:24 -0600190 min_stride = 2;
191 break;
Stephen Warren45f5ff82012-04-04 15:48:31 -0600192 case 32:
Stephen Warrenf01ee602012-04-09 13:40:24 -0600193 min_stride = 4;
194 break;
Stephen Warren45f5ff82012-04-04 15:48:31 -0600195#ifdef CONFIG_64BIT
196 case 64:
Stephen Warrenf01ee602012-04-09 13:40:24 -0600197 min_stride = 8;
198 break;
Stephen Warren45f5ff82012-04-04 15:48:31 -0600199#endif
200 break;
201 default:
202 return ERR_PTR(-EINVAL);
203 }
204
Stephen Warrenf01ee602012-04-09 13:40:24 -0600205 if (config->reg_stride < min_stride)
206 return ERR_PTR(-EINVAL);
207
Stephen Warren6a552442012-05-24 10:47:27 -0600208 switch (config->reg_format_endian) {
209 case REGMAP_ENDIAN_DEFAULT:
210 case REGMAP_ENDIAN_NATIVE:
211 break;
212 default:
213 return ERR_PTR(-EINVAL);
214 }
215
Dimitris Papastamos46335112012-07-18 14:17:23 +0100216 ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
Stephen Warren45f5ff82012-04-04 15:48:31 -0600217 if (!ctx)
218 return ERR_PTR(-ENOMEM);
219
220 ctx->regs = regs;
221 ctx->val_bytes = config->val_bits / 8;
Stephen Warren6b8e0902013-11-25 15:12:47 -0700222 ctx->clk = ERR_PTR(-ENODEV);
Stephen Warren45f5ff82012-04-04 15:48:31 -0600223
Philipp Zabel878ec672013-02-14 17:39:08 +0100224 if (clk_id == NULL)
225 return ctx;
226
227 ctx->clk = clk_get(dev, clk_id);
228 if (IS_ERR(ctx->clk)) {
229 ret = PTR_ERR(ctx->clk);
230 goto err_free;
231 }
232
233 ret = clk_prepare(ctx->clk);
234 if (ret < 0) {
235 clk_put(ctx->clk);
236 goto err_free;
237 }
238
Stephen Warren45f5ff82012-04-04 15:48:31 -0600239 return ctx;
Philipp Zabel878ec672013-02-14 17:39:08 +0100240
241err_free:
242 kfree(ctx);
243
244 return ERR_PTR(ret);
Stephen Warren45f5ff82012-04-04 15:48:31 -0600245}
246
247/**
Philipp Zabel878ec672013-02-14 17:39:08 +0100248 * regmap_init_mmio_clk(): Initialise register map with register clock
Stephen Warren45f5ff82012-04-04 15:48:31 -0600249 *
250 * @dev: Device that will be interacted with
Philipp Zabel878ec672013-02-14 17:39:08 +0100251 * @clk_id: register clock consumer ID
Stephen Warren45f5ff82012-04-04 15:48:31 -0600252 * @regs: Pointer to memory-mapped IO region
253 * @config: Configuration for register map
254 *
255 * The return value will be an ERR_PTR() on error or a valid pointer to
256 * a struct regmap.
257 */
Philipp Zabel878ec672013-02-14 17:39:08 +0100258struct regmap *regmap_init_mmio_clk(struct device *dev, const char *clk_id,
259 void __iomem *regs,
260 const struct regmap_config *config)
Stephen Warren45f5ff82012-04-04 15:48:31 -0600261{
262 struct regmap_mmio_context *ctx;
263
Philipp Zabel878ec672013-02-14 17:39:08 +0100264 ctx = regmap_mmio_gen_context(dev, clk_id, regs, config);
Stephen Warren45f5ff82012-04-04 15:48:31 -0600265 if (IS_ERR(ctx))
266 return ERR_CAST(ctx);
267
268 return regmap_init(dev, &regmap_mmio, ctx, config);
269}
Philipp Zabel878ec672013-02-14 17:39:08 +0100270EXPORT_SYMBOL_GPL(regmap_init_mmio_clk);
Stephen Warren45f5ff82012-04-04 15:48:31 -0600271
272/**
Philipp Zabel878ec672013-02-14 17:39:08 +0100273 * devm_regmap_init_mmio_clk(): Initialise managed register map with clock
Stephen Warren45f5ff82012-04-04 15:48:31 -0600274 *
275 * @dev: Device that will be interacted with
Philipp Zabel878ec672013-02-14 17:39:08 +0100276 * @clk_id: register clock consumer ID
Stephen Warren45f5ff82012-04-04 15:48:31 -0600277 * @regs: Pointer to memory-mapped IO region
278 * @config: Configuration for register map
279 *
280 * The return value will be an ERR_PTR() on error or a valid pointer
281 * to a struct regmap. The regmap will be automatically freed by the
282 * device management code.
283 */
Philipp Zabel878ec672013-02-14 17:39:08 +0100284struct regmap *devm_regmap_init_mmio_clk(struct device *dev, const char *clk_id,
285 void __iomem *regs,
286 const struct regmap_config *config)
Stephen Warren45f5ff82012-04-04 15:48:31 -0600287{
288 struct regmap_mmio_context *ctx;
289
Philipp Zabel878ec672013-02-14 17:39:08 +0100290 ctx = regmap_mmio_gen_context(dev, clk_id, regs, config);
Stephen Warren45f5ff82012-04-04 15:48:31 -0600291 if (IS_ERR(ctx))
292 return ERR_CAST(ctx);
293
294 return devm_regmap_init(dev, &regmap_mmio, ctx, config);
295}
Philipp Zabel878ec672013-02-14 17:39:08 +0100296EXPORT_SYMBOL_GPL(devm_regmap_init_mmio_clk);
Stephen Warren45f5ff82012-04-04 15:48:31 -0600297
298MODULE_LICENSE("GPL v2");