xref: /openbmc/linux/drivers/base/regmap/regmap-mmio.c (revision 781095f903f398148cd0b646d3984234a715f29e)
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 			__raw_writeb(*(u8 *)val, ctx->regs + offset);
137 			break;
138 		case 2:
139 			__raw_writew(*(u16 *)val, ctx->regs + offset);
140 			break;
141 		case 4:
142 			__raw_writel(*(u32 *)val, ctx->regs + offset);
143 			break;
144 #ifdef CONFIG_64BIT
145 		case 8:
146 			__raw_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 = __raw_readb(ctx->regs + offset);
197 			break;
198 		case 2:
199 			*(u16 *)val = __raw_readw(ctx->regs + offset);
200 			break;
201 		case 4:
202 			*(u32 *)val = __raw_readl(ctx->regs + offset);
203 			break;
204 #ifdef CONFIG_64BIT
205 		case 8:
206 			*(u64 *)val = __raw_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");
345