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 inline void regmap_mmio_count_check(size_t count, u32 offset) 65 { 66 BUG_ON(count <= offset); 67 } 68 69 static inline unsigned int 70 regmap_mmio_get_offset(const void *reg, size_t reg_size) 71 { 72 switch (reg_size) { 73 case 1: 74 return *(u8 *)reg; 75 case 2: 76 return *(u16 *)reg; 77 case 4: 78 return *(u32 *)reg; 79 #ifdef CONFIG_64BIT 80 case 8: 81 return *(u64 *)reg; 82 #endif 83 default: 84 BUG(); 85 } 86 } 87 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) 91 { 92 struct regmap_mmio_context *ctx = context; 93 unsigned int offset; 94 int ret; 95 96 regmap_mmio_regsize_check(reg_size); 97 98 if (!IS_ERR(ctx->clk)) { 99 ret = clk_enable(ctx->clk); 100 if (ret < 0) 101 return ret; 102 } 103 104 offset = regmap_mmio_get_offset(reg, reg_size); 105 106 while (val_size) { 107 switch (ctx->val_bytes) { 108 case 1: 109 writeb(*(u8 *)val, ctx->regs + offset); 110 break; 111 case 2: 112 writew(*(u16 *)val, ctx->regs + offset); 113 break; 114 case 4: 115 writel(*(u32 *)val, ctx->regs + offset); 116 break; 117 #ifdef CONFIG_64BIT 118 case 8: 119 writeq(*(u64 *)val, ctx->regs + offset); 120 break; 121 #endif 122 default: 123 /* Should be caught by regmap_mmio_check_config */ 124 BUG(); 125 } 126 val_size -= ctx->val_bytes; 127 val += ctx->val_bytes; 128 offset += ctx->val_bytes; 129 } 130 131 if (!IS_ERR(ctx->clk)) 132 clk_disable(ctx->clk); 133 134 return 0; 135 } 136 137 static int regmap_mmio_write(void *context, const void *data, size_t count) 138 { 139 struct regmap_mmio_context *ctx = context; 140 unsigned int offset = ctx->reg_bytes + ctx->pad_bytes; 141 142 regmap_mmio_count_check(count, offset); 143 144 return regmap_mmio_gather_write(context, data, ctx->reg_bytes, 145 data + offset, count - offset); 146 } 147 148 static int regmap_mmio_read(void *context, 149 const void *reg, size_t reg_size, 150 void *val, size_t val_size) 151 { 152 struct regmap_mmio_context *ctx = context; 153 unsigned int offset; 154 int ret; 155 156 regmap_mmio_regsize_check(reg_size); 157 158 if (!IS_ERR(ctx->clk)) { 159 ret = clk_enable(ctx->clk); 160 if (ret < 0) 161 return ret; 162 } 163 164 offset = regmap_mmio_get_offset(reg, reg_size); 165 166 while (val_size) { 167 switch (ctx->val_bytes) { 168 case 1: 169 *(u8 *)val = readb(ctx->regs + offset); 170 break; 171 case 2: 172 *(u16 *)val = readw(ctx->regs + offset); 173 break; 174 case 4: 175 *(u32 *)val = readl(ctx->regs + offset); 176 break; 177 #ifdef CONFIG_64BIT 178 case 8: 179 *(u64 *)val = readq(ctx->regs + offset); 180 break; 181 #endif 182 default: 183 /* Should be caught by regmap_mmio_check_config */ 184 BUG(); 185 } 186 val_size -= ctx->val_bytes; 187 val += ctx->val_bytes; 188 offset += ctx->val_bytes; 189 } 190 191 if (!IS_ERR(ctx->clk)) 192 clk_disable(ctx->clk); 193 194 return 0; 195 } 196 197 static void regmap_mmio_free_context(void *context) 198 { 199 struct regmap_mmio_context *ctx = context; 200 201 if (!IS_ERR(ctx->clk)) { 202 clk_unprepare(ctx->clk); 203 clk_put(ctx->clk); 204 } 205 kfree(context); 206 } 207 208 static struct regmap_bus regmap_mmio = { 209 .fast_io = true, 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, 216 }; 217 218 static struct regmap_mmio_context *regmap_mmio_gen_context(struct device *dev, 219 const char *clk_id, 220 void __iomem *regs, 221 const struct regmap_config *config) 222 { 223 struct regmap_mmio_context *ctx; 224 int min_stride; 225 int ret; 226 227 ret = regmap_mmio_regbits_check(config->reg_bits); 228 if (ret) 229 return ERR_PTR(ret); 230 231 if (config->pad_bits) 232 return ERR_PTR(-EINVAL); 233 234 switch (config->val_bits) { 235 case 8: 236 /* The core treats 0 as 1 */ 237 min_stride = 0; 238 break; 239 case 16: 240 min_stride = 2; 241 break; 242 case 32: 243 min_stride = 4; 244 break; 245 #ifdef CONFIG_64BIT 246 case 64: 247 min_stride = 8; 248 break; 249 #endif 250 break; 251 default: 252 return ERR_PTR(-EINVAL); 253 } 254 255 if (config->reg_stride < min_stride) 256 return ERR_PTR(-EINVAL); 257 258 switch (config->reg_format_endian) { 259 case REGMAP_ENDIAN_DEFAULT: 260 case REGMAP_ENDIAN_NATIVE: 261 break; 262 default: 263 return ERR_PTR(-EINVAL); 264 } 265 266 ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); 267 if (!ctx) 268 return ERR_PTR(-ENOMEM); 269 270 ctx->regs = regs; 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); 275 276 if (clk_id == NULL) 277 return ctx; 278 279 ctx->clk = clk_get(dev, clk_id); 280 if (IS_ERR(ctx->clk)) { 281 ret = PTR_ERR(ctx->clk); 282 goto err_free; 283 } 284 285 ret = clk_prepare(ctx->clk); 286 if (ret < 0) { 287 clk_put(ctx->clk); 288 goto err_free; 289 } 290 291 return ctx; 292 293 err_free: 294 kfree(ctx); 295 296 return ERR_PTR(ret); 297 } 298 299 struct regmap *__regmap_init_mmio_clk(struct device *dev, const char *clk_id, 300 void __iomem *regs, 301 const struct regmap_config *config, 302 struct lock_class_key *lock_key, 303 const char *lock_name) 304 { 305 struct regmap_mmio_context *ctx; 306 307 ctx = regmap_mmio_gen_context(dev, clk_id, regs, config); 308 if (IS_ERR(ctx)) 309 return ERR_CAST(ctx); 310 311 return __regmap_init(dev, ®map_mmio, ctx, config, 312 lock_key, lock_name); 313 } 314 EXPORT_SYMBOL_GPL(__regmap_init_mmio_clk); 315 316 struct regmap *__devm_regmap_init_mmio_clk(struct device *dev, 317 const char *clk_id, 318 void __iomem *regs, 319 const struct regmap_config *config, 320 struct lock_class_key *lock_key, 321 const char *lock_name) 322 { 323 struct regmap_mmio_context *ctx; 324 325 ctx = regmap_mmio_gen_context(dev, clk_id, regs, config); 326 if (IS_ERR(ctx)) 327 return ERR_CAST(ctx); 328 329 return __devm_regmap_init(dev, ®map_mmio, ctx, config, 330 lock_key, lock_name); 331 } 332 EXPORT_SYMBOL_GPL(__devm_regmap_init_mmio_clk); 333 334 MODULE_LICENSE("GPL v2"); 335