2 * Register map access API - MMIO support
4 * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved.
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.
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
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/>.
19 #include <linux/clk.h>
20 #include <linux/err.h>
22 #include <linux/module.h>
23 #include <linux/regmap.h>
24 #include <linux/slab.h>
26 struct regmap_mmio_context
{
34 static inline void regmap_mmio_regsize_check(size_t reg_size
)
49 static int regmap_mmio_regbits_check(size_t reg_bits
)
64 static inline void regmap_mmio_count_check(size_t count
, u32 offset
)
66 BUG_ON(count
<= offset
);
69 static inline unsigned int
70 regmap_mmio_get_offset(const void *reg
, size_t reg_size
)
88 static int regmap_mmio_gather_write(void *context
,
89 const void *reg
, size_t reg_size
,
90 const void *val
, size_t val_size
)
92 struct regmap_mmio_context
*ctx
= context
;
96 regmap_mmio_regsize_check(reg_size
);
98 if (!IS_ERR(ctx
->clk
)) {
99 ret
= clk_enable(ctx
->clk
);
104 offset
= regmap_mmio_get_offset(reg
, reg_size
);
107 switch (ctx
->val_bytes
) {
109 writeb(*(u8
*)val
, ctx
->regs
+ offset
);
112 writew(*(u16
*)val
, ctx
->regs
+ offset
);
115 writel(*(u32
*)val
, ctx
->regs
+ offset
);
119 writeq(*(u64
*)val
, ctx
->regs
+ offset
);
123 /* Should be caught by regmap_mmio_check_config */
126 val_size
-= ctx
->val_bytes
;
127 val
+= ctx
->val_bytes
;
128 offset
+= ctx
->val_bytes
;
131 if (!IS_ERR(ctx
->clk
))
132 clk_disable(ctx
->clk
);
137 static int regmap_mmio_write(void *context
, const void *data
, size_t count
)
139 struct regmap_mmio_context
*ctx
= context
;
140 unsigned int offset
= ctx
->reg_bytes
+ ctx
->pad_bytes
;
142 regmap_mmio_count_check(count
, offset
);
144 return regmap_mmio_gather_write(context
, data
, ctx
->reg_bytes
,
145 data
+ offset
, count
- offset
);
148 static int regmap_mmio_read(void *context
,
149 const void *reg
, size_t reg_size
,
150 void *val
, size_t val_size
)
152 struct regmap_mmio_context
*ctx
= context
;
156 regmap_mmio_regsize_check(reg_size
);
158 if (!IS_ERR(ctx
->clk
)) {
159 ret
= clk_enable(ctx
->clk
);
164 offset
= regmap_mmio_get_offset(reg
, reg_size
);
167 switch (ctx
->val_bytes
) {
169 *(u8
*)val
= readb(ctx
->regs
+ offset
);
172 *(u16
*)val
= readw(ctx
->regs
+ offset
);
175 *(u32
*)val
= readl(ctx
->regs
+ offset
);
179 *(u64
*)val
= readq(ctx
->regs
+ offset
);
183 /* Should be caught by regmap_mmio_check_config */
186 val_size
-= ctx
->val_bytes
;
187 val
+= ctx
->val_bytes
;
188 offset
+= ctx
->val_bytes
;
191 if (!IS_ERR(ctx
->clk
))
192 clk_disable(ctx
->clk
);
197 static void regmap_mmio_free_context(void *context
)
199 struct regmap_mmio_context
*ctx
= context
;
201 if (!IS_ERR(ctx
->clk
)) {
202 clk_unprepare(ctx
->clk
);
208 static struct regmap_bus regmap_mmio
= {
210 .write
= regmap_mmio_write
,
211 .gather_write
= regmap_mmio_gather_write
,
212 .read
= regmap_mmio_read
,
213 .free_context
= regmap_mmio_free_context
,
214 .reg_format_endian_default
= REGMAP_ENDIAN_NATIVE
,
215 .val_format_endian_default
= REGMAP_ENDIAN_NATIVE
,
218 static struct regmap_mmio_context
*regmap_mmio_gen_context(struct device
*dev
,
221 const struct regmap_config
*config
)
223 struct regmap_mmio_context
*ctx
;
227 ret
= regmap_mmio_regbits_check(config
->reg_bits
);
231 if (config
->pad_bits
)
232 return ERR_PTR(-EINVAL
);
234 switch (config
->val_bits
) {
236 /* The core treats 0 as 1 */
252 return ERR_PTR(-EINVAL
);
255 if (config
->reg_stride
< min_stride
)
256 return ERR_PTR(-EINVAL
);
258 switch (config
->reg_format_endian
) {
259 case REGMAP_ENDIAN_DEFAULT
:
260 case REGMAP_ENDIAN_NATIVE
:
263 return ERR_PTR(-EINVAL
);
266 ctx
= kzalloc(sizeof(*ctx
), GFP_KERNEL
);
268 return ERR_PTR(-ENOMEM
);
271 ctx
->val_bytes
= config
->val_bits
/ 8;
272 ctx
->reg_bytes
= config
->reg_bits
/ 8;
273 ctx
->pad_bytes
= config
->pad_bits
/ 8;
274 ctx
->clk
= ERR_PTR(-ENODEV
);
279 ctx
->clk
= clk_get(dev
, clk_id
);
280 if (IS_ERR(ctx
->clk
)) {
281 ret
= PTR_ERR(ctx
->clk
);
285 ret
= clk_prepare(ctx
->clk
);
299 struct regmap
*__regmap_init_mmio_clk(struct device
*dev
, const char *clk_id
,
301 const struct regmap_config
*config
,
302 struct lock_class_key
*lock_key
,
303 const char *lock_name
)
305 struct regmap_mmio_context
*ctx
;
307 ctx
= regmap_mmio_gen_context(dev
, clk_id
, regs
, config
);
309 return ERR_CAST(ctx
);
311 return __regmap_init(dev
, ®map_mmio
, ctx
, config
,
312 lock_key
, lock_name
);
314 EXPORT_SYMBOL_GPL(__regmap_init_mmio_clk
);
316 struct regmap
*__devm_regmap_init_mmio_clk(struct device
*dev
,
319 const struct regmap_config
*config
,
320 struct lock_class_key
*lock_key
,
321 const char *lock_name
)
323 struct regmap_mmio_context
*ctx
;
325 ctx
= regmap_mmio_gen_context(dev
, clk_id
, regs
, config
);
327 return ERR_CAST(ctx
);
329 return __devm_regmap_init(dev
, ®map_mmio
, ctx
, config
,
330 lock_key
, lock_name
);
332 EXPORT_SYMBOL_GPL(__devm_regmap_init_mmio_clk
);
334 MODULE_LICENSE("GPL v2");